Language agnostic 函数式编程:不变性等

Language agnostic 函数式编程:不变性等,language-agnostic,concurrency,functional-programming,immutability,Language Agnostic,Concurrency,Functional Programming,Immutability,我最近学习了函数式编程,并得到了(很好的!)答案,这些答案引发了更多的问题(有时学习也是如此)。以下是几个例子: 一个答案提到了不可变数据结构的优点:每个线程都可以有自己的副本。现在,对我来说,这听起来很像一个版本控制系统(打个比方),在这里,每个人都可以签出自己的副本,而不是锁定某人签出的代码,这样其他人就不能修改它。听起来不错。然而,在风投中,你有“合并”变化的概念,如果两个人改变了相同的东西。似乎在多线程场景中肯定会出现此问题。。。那么,当线程必须看到最新的数据时,“合并”是如何完成的呢

我最近学习了函数式编程,并得到了(很好的!)答案,这些答案引发了更多的问题(有时学习也是如此)。以下是几个例子:

  • 一个答案提到了不可变数据结构的优点:每个线程都可以有自己的副本。现在,对我来说,这听起来很像一个版本控制系统(打个比方),在这里,每个人都可以签出自己的副本,而不是锁定某人签出的代码,这样其他人就不能修改它。听起来不错。然而,在风投中,你有“合并”变化的概念,如果两个人改变了相同的东西。似乎在多线程场景中肯定会出现此问题。。。那么,当线程必须看到最新的数据时,“合并”是如何完成的呢

  • 讨论了在对象上的循环中执行操作的情况,以及如何每次使用新对象而不是更新旧对象。但是,假设在非循环场景中更新了
    bankAccount
    ,例如GUI银行系统。操作员单击“更改利率”按钮,触发一个事件(例如,在C语言中)执行类似于
    bankAccount.InterestRate=newRateFromUser
    的操作。我觉得我在这里很密集,但希望我的例子有意义:必须有某种方式更新对象,对吗?其他一些事情可能取决于新数据

  • 无论如何,如果你能帮助我了解范式转换,我将不胜感激。我记得在学习OOP时,我的大脑也经历了类似的“愚蠢阶段”,在学习了简单的程序命令式编码方法之后

  • 不变的数据结构与VCS不同。将不可变数据结构视为只读文件。如果它是只读的,那么无论是谁在任何给定的时间读取文件的哪一部分,每个人都将读取正确的信息

  • 这个答案是关于


  • 考虑一下.Net中的String类(它是一个不可变的对象)。如果对字符串调用方法,将获得一个新副本:

    String s1 = "there";
    String s2 = s1.Insert(0, "hello ");
    
    Console.Writeline("string 1: " + s1);
    Console.Writeline("string 2: " + s2);
    
    这将输出:

    字符串1:那里

    字符串2:你好

    将此行为与StringBuilder进行比较,后者具有基本相同的方法签名:

    StringBuilder sb  = new StringBuilder("there");
    StringBuilder sb2 = sb.Insert(0, "hi ");
    
    Console.WriteLine("sb 1: " + sb.ToString());
    Console.WriteLine("sb 2: " + sb2.ToString());
    
    因为StringBuilder是可变的,所以两个变量都指向同一个对象。输出将是:

    sb 1:你好

    sb 2:你好

    因此,一旦创建了字符串,就绝对不能更改它。s1将始终“存在”,直到时间结束(或直到其垃圾被收集)。这在线程处理中很重要,因为您始终可以单步遍历每个字符并打印其值,因为您知道它将始终打印“there”。如果您在创建StringBuilder后开始打印它,您可能会打印其中的前两个字符并得到“th”。现在,想象另一个线程出现在广告插入“嗨”。值现在不同了!打印第三个字符时,它是“hi”的空格。所以你可以打印:“th there”。

    “Immutable”的意思就是:它不会改变


    功能程序进行更新的方式是传递新事物。现有值永远不会更改:您只需构建一个新值并传递它即可。通常新的价值份额与旧的价值份额相同;该技术的好例子是由cons单元格组成的列表,以及对第1部分的回答。

    不可变对象本身不支持任何类似“合并”的操作,以允许将两个线程的更新结果合并。有两种主要的策略:悲观主义者和乐观主义者。如果您对此持悲观态度,那么您会假设两个线程很可能希望同时更新同一段数据。因此,您使用了锁定,这样第二个线程将冻结,直到第一个线程说它已经完成。如果您对这种情况很少发生持乐观态度,那么您可以让两个线程处理各自的数据逻辑副本。首先完成的一个提供新版本,而另一个必须从头开始重新开始——只是现在它从第一个线程更改的结果开始。但是,这种昂贵的重新启动只是偶尔发生的,因此总的来说,由于缺少锁定,它的性能会更好(尽管只有当您对冲突发生的频率非常乐观时,这才是正确的)

    第2部分:纯函数式无状态语言并不能真正消除这个问题。即使是一个纯Haskell程序也可以有与之关联的状态。区别在于有状态代码的返回类型不同。操纵状态的函数表示为对表示该状态的对象进行操作的操作序列。在一个荒谬的例子中,考虑计算机的文件系统。每当一个程序修改一个文件的内容(即使是一个字节)时,它就创建了整个文件系统的新“版本”。从广义上讲,是整个宇宙的新版本。但是现在让我们把重点放在文件系统上。程序中检查文件系统的任何其他部分现在都可能受到该修改字节的影响。因此Haskell说,在文件系统上运行的函数必须有效地传递表示文件系统版本的对象。然后,由于手动处理这一问题会很繁琐,它会将需求从内到外转换,并表示如果函数希望能够执行IO,它必须返回某种容器对象。容器内是函数要返回的值。但是这个容器可以作为证据,证明这个函数也有副作用或者可以看到副作用。这意味着Haskell的类型系统能够区分有副作用的功能和“纯”功能。因此,它有助于包含和管理代码的状态性,而不会真正消除它。

    关于#2

    其他一些事情可能取决于 th