代码之家  ›  专栏  ›  技术社区  ›  J Cooper

对调用方来说是纯函数,但在内部使用变异的函数

  •  8
  • J Cooper  · 技术社区  · 14 年前

    我刚拿到一份 专家F#2.0

    例如,必要时,您可以 对私有数据使用副作用 项目开始时分配的结构 一个算法,然后丢弃这些 返回 实际上没有副作用 功能。分离的一个例子 从F#图书馆是图书馆的 实施列表.map,使用 内部突变;写入发生 进入。

    换言之,如果把绩效放在一边,实施绩效管理是否更可取 List.map 以一种纯粹的方式?

    7 回复  |  直到 13 年前
        1
  •  14
  •   Brian    14 年前

    (我能想到的唯一的不利之处是,当人类看到这样一个局部可变的东西时,他们必须思考它是否能逃脱。在F#中,局部可变项永远无法逃逸(闭包不能捕获可变项),因此唯一潜在的“精神税”来自于对可变引用类型的推理。)

    小结:使用特效是可以的,只要能简单地说服自己,特效只发生在无法逃逸的当地人身上。(在其他情况下也可以使用效果,但我忽略了其他情况,因为在这个问题线程中,我们是开明的函数程序员,试图在合理的情况下回避效果。:) )

    (如果你想深入研究局部效应,比如F#的实现列表.map,不仅不妨碍并行化,而且实际上是一种好处,因为更高效的实现分配的资源更少,因此对GC的共享资源的压力更小。)

        2
  •  6
  •   Travis Brown    14 年前

    你可能对西蒙·佩顿·琼斯的有兴趣 "Lazy Functional State Threads" . 我只看了前几页,非常清楚(我相信其余的也很清楚)。

    重要的一点是当你使用 Control.Monad.ST 为了在Haskell中完成这类工作,类型系统本身强制进行封装。在Scala(可能还有F#)中,这种方法更像是“相信我们,我们在这里没有做任何鬼鬼祟祟的事情 ListBuffer map ".

        3
  •  4
  •   Gabe Timothy Khouri    13 年前

    map 函数在内部创建一个列表大小的数组,并对数组中填充的元素进行迭代,您仍然可以运行 地图 在同一个列表上并发100次,不用担心,因为 地图

    另一方面,如果函数使用全局可变的数据结构,并行化可能会受到影响。例如,假设您有 Memoize 功能。显然,它的全部目的是维护一些全局状态(虽然“全局”的意义是它不是函数调用的本地状态,但它仍然是“私有”的意义是它不能在函数外部访问),这样它就不必使用相同的参数多次运行函数,但是它仍然是纯的,因为相同的输入将始终产生相同的输出。如果缓存数据结构是线程安全的(如 ConcurrentDictionary )然后你仍然可以并行运行你的函数。如果不是,那么您可能会认为该函数不是纯粹的,因为它有副作用,在并发运行时可以观察到。

    我应该补充一点,在F中,一种常见的技术是从一个纯函数例程开始,然后利用可变状态(例如缓存、显式循环)对其进行优化,当分析显示它太慢时。

        4
  •  3
  •   Abhinav Sarkar    14 年前

    同样的方法也可以在Clojure中找到。Clojure中不可变的数据结构-列表、映射和向量-有它们的“瞬时”对应物,它们是可变的。这个 Clojure reference about transient 敦促只在“任何其他代码”看不到的代码中使用它们。

    • 通常作用于不可变数据结构的函数不适用于瞬态。调用它们将引发异常。

    这个clojure.core公司代码本身在幕后使用了很多瞬态。

    使用瞬变的主要好处是它们提供了巨大的加速。

    因此,在函数语言中严格控制可变状态的使用似乎是可以的。

        5
  •  2
  •   Stephen Hosking    14 年前

    它不会影响该函数是否可以与其他函数并行运行。它会影响到函数的内部是否可以被并行化,但是对于大多数小型函数(比如map)来说,这不太可能是一个问题,目标是PC。

    我注意到,一些优秀的F#程序员(在网上和书上)似乎对使用命令式循环技术非常放松。与复杂的递归函数相比,他们似乎更喜欢一个简单的循环和可变的循环变量。

        6
  •  2
  •   fuz    14 年前

    我能想到的另一个问题是懒惰——可变数据结构通常不是懒惰的,因此可变函数可能会强制对参数进行不必要的计算。

        7
  •  0
  •   Snark    14 年前

    我会用一个问题来回答这个问题:“你是在写这个函数,还是在使用这个函数?”

    作为一个用户,一点也不关心函数的内部结构。它可以用字节码编码,并从现在起到审判日在内部使用硬副作用,只要它与预期的数据输入和输出的契约相匹配。函数是一个黑匣子或甲骨文,它的内部结构是不相关的(假设它不做任何愚蠢和外部的事情)。

    作为函数的开发人员,内部结构非常重要。不变性、常量正确性和避免副作用都有助于开发和维护函数,并将函数扩展到并行领域。

    许多人开发一个函数,然后使用它,所以这两个方面都适用。

    不变结构和可变结构的优点是什么是另一个问题。