我们可以说函数闭包是将状态保存给函数,每次我们给函数分配一个新状态时,它都会产生一个新函数吗?

Can we say function closure is saving a state to a function, and every time we assign a new state to the function it will result in a new function?

提问人:Gaurav Singh 提问时间:12/2/2021 最后编辑:Gaurav Singh 更新时间:12/3/2021 访问量:92

问:

在以下函数中,在 Scala 中调用 inc,它执行增量操作。

def inc(more:Int) = {
  def helper(x:Int) = x+more
  helper _
}

每当调用 inc 函数时,它都会返回另一个函数,该函数绑定传递给它的参数。例如,inc(1) 将返回另一个 Int => Int 类型的函数,其中变量 more 与 1 绑定。

inc(1) // This is of type Int => Int

那么我们可以说 more 是返回函数的状态变量,当我们调用 inc(1) 时,1 被分配给 more?

这里有一些进一步的阐述,

由于我来自OO编程范式,因此当我说状态时,我将其与类的实例相关联,该类在给定时间具有特定状态。让我们首先考虑一个 Java 中的类,调用 IncHelper,如下所示:

class IncHelper{
    private int more;
    public IncHelper(int more){
        this.more = more;
    }

    public int inc(int x){
        return x+this.more;
    }
}

如果我创建上述类的不同实例,如下所示:

IncHelper inc1 = new IncHelper(1);  
// This instance will always increase a value by 1
inc1.inc(10);     // output will be 11

如果我创建上述类的不同实例,如下所示:

IncHelper inc2 = new IncHelper(2);  
// This instance will always increase a value by 2
inc2.inc(10);     // output will be 12

因此,在上述两种情况下,两个实例 inc1 和 inc2 包含两个不同的状态变量值。我给出的 Scala 函数式编程示例也是如此:

val inc1 = inc(1)
inc1(10)                   // Will return 11

如果我创建另一个值,如下所示:

val inc2 = inc(2)
inc2(10)                    // Will return 12

因此,在这两种情况下,即当我创建 IncHelper 的 2 个实例时,OO 编程会记住在构造它时传递的变量。同样,对于我们创建的两个函数文本也是如此,其中两个变量在创建函数文本 inc1 和 inc2 时传递了值。

Scala 函数式编程 闭包

评论


答:

2赞 Mateusz Kubuszok 12/2/2021 #1

函数的闭包存储有关上下文的信息 - 此上下文是否由变和/或不可变数据组成是另一回事。

我们只将函数称为有状态函数,因为它中有一些可观察到的可变状态(使函数在引用上不透明;打印等副作用算作可变状态)。因此,除非你有一些可能被变异的数据(拥有从未变异的 s 使它们有效不可变),否则根据通常的术语,你的函数没有状态。var

在您的情况下:

def inc(more:Int) = {
  def helper(x:Int) = x+more
  helper _
}

inc不存储可变数据。 返回一个新值,但不改变 NOR 值。如果这样做:x+morexmore

inc(1)
inc(1)
inc(1)
inc(1)

您每次都会得到相同的结果(具有相同行为的功能),并且没有副作用。因此,根据通常接受的术语,函数是状态的,你不能说它有状态。事实上,里面有一些数据存储在里面......是无关紧要的,因为你曾经写过的每一段代码都是这样做的,所以一切都会被称为有状态的,有状态和无状态之间的区别几乎是无用的。Int => Intinc

现在,如果您将函数定义为:

var state = 0
def inc(more:Int) = {
  state = state + more
  val y = state // binds current value of state
  def helper(x:Int) = x + y
  helper _
}

每次调用具有相同值的函数都会产生不同的值(此处为:具有不同行为的函数),因此会有一些状态可以讨论。( 函数将有一个状态 - 存储在 中,而返回的函数将是无状态的,因为它们只捕获不可变的值)。incInt => Intintvar state

该状态问题将独立于闭包的概念 - 闭包只是“说”函数的定义使用创建时提供的一些数据,并且该函数仍然可以在您不再可以从它外部访问这些数据之后。

总而言之,如果创建该函数的环境中存在一些可变状态,并且该函数被该函数捕获(使用),我只会说(特定)“函数的闭包将状态保存到函数”。在其他情况下,我不会这么说,因为没有“状态”可以捕获。然而,由于这是非常不精确的陈述(函数是否捕获了可变状态的快照,而该快照本身是不可变的?还是函数捕获了对一些可变数据的引用,这些数据可以在其外部发生突变?我会完全避免它。

评论

0赞 Gaurav Singh 12/3/2021
感谢 Mateusz Kubuszok,由于我更多地关注 OOP Paradigm,因此我将其与 OOP 相关联。我已经详细阐述了我的问题,我试图将两者联系起来。
0赞 Mateusz Kubuszok 12/6/2021
“实例”、“对象”、“状态”等是您可能会在 JVM(设计上是 OO)上观察到的实现细节。但是相同的概念(“函数”,“闭包”)可能会以不同的方式实现,例如,在Haskell中,你会用一些低级C概念(程序员不可见)实现闭包,而在C++中,闭包捕获非常明确(这个奇怪的东西),而lambda本身可能会内联到没有内存分配的原始ASM指令中。将“闭包”视为“状态”可能会成为充分利用该概念的方式。[]