函数式编程是否允许本地范围对象的可变性?

时间:2015-02-05 15:45:16

标签: python language-agnostic functional-programming

我了解到对象的可变性是根据其状态而不是其身份来定义的。

以下程序更改了函数count中名称为hailstone()的名称引用的本地作用域对象的状态。

def hailstone(n):
    count = 1
    """Print the terms of the 'hailstone sequence' from n to 1."""
    assert n > 0
    print(n)
    if n > 1:
        if n % 2 == 0:
            count += hailstone(n / 2)
        else:
            count += hailstone((n * 3) + 1)
    return count

if (__name__ == '__main__'):
    result = hailstone(10)
    print(result)

在阅读this article的以下段落后,我看到上面的代码在更改名称count引用的本地作用域对象的状态方面看起来很好(即count += hailstone(n / 2)) :

  

忽略这一切。功能代码的特点是一件事:没有副作用。它不依赖于当前函数之外的数据,也不会更改当前函数之外的数据。每个其他“功能”的东西都可以从这个属性派生。在你学习的时候用它作为引导绳。

那么,我如何从this answer中理解这句话的含义:

  

在函数式编程中,不可能改变变量的值。

函数式编程是否允许更改上述程序中名称count引用的本地作用域对象的状态?

4 个答案:

答案 0 :(得分:2)

功能编程没有明确的定义。然而,它通常意味着暗示副作用的缺席或至少是最小化 - 有时人们会谈论函数式编程以强调它们完全缺席。但是副作用是什么?

一个常见的定义是副作用是破坏参照透明度的任何内容。参考透明度可以通过各种方式定义,但也许最有启发性的定义是评估顺序应该是无关紧要的。也就是说,如果您可以按任何顺序简化任何子表达式,仅仅通过替换定义,而不改变程序的结果,则程序是引用透明的(或)。特别是,您始终可以使用其(唯一!)定义替换变量。

显然,对可变变量的赋值会打破这种情况,因此必须将其视为副作用 - 即使它只是局部的。顺便说一下,打印声明也是如此。

有多种方法可以在不破坏参照透明度的情况下拥有可变状态或IO。这是 monad 的神秘概念。但我不会在这里进入。

答案 1 :(得分:0)

不知道Python,我认为count是一个“本地”变量,从外部看不到,hailstone的每个调用都有自己的count实例。如果是这样,那么,正如@jonrsharpe所解释的那样,你对count做了什么并不重要。

然而,请注意,您的代码不必要地冗长。为什么不简单:

if n > 1:
    if n % 2 == 0:
        return 1 + hailstone(n/2)
    else:
        return 1 + hailstone(n*3+1)
else:
    return 1

事实证明,根本不需要变量,更不用说更新变量了。

答案 2 :(得分:0)

您的功能在功能级别没有副作用(除print之外)。这是一个必要但不充分的条件,被称为功能性。功能程序包含一个重要的表达方式。您的示例是函数级别的表达式,其中散布着函数体中的命令式语句。没有if的{​​{1}}是一个声明,可变重新分配也是如此。

这并不是说没有副作用的命令功能并不比具有副作用的命令功能更好。你的例子肯定有优点,即使它不被认为是纯粹的功能。

答案 3 :(得分:0)

该程序改变了x

x = 2
x = x * 3
x = x + 4
return x

在函数式编程中,你不能使用变异。您的代码应该只定义一次变量:

x1 = 2
x2 = x1 * 3
x3 = x2 + 4
return x3

编译器可能会为x1,x2和x3分配相同的内存位置,并像原始程序一样进行mutate。但是,这是一个实施细节,并不重要。

与以前的顺序程序不同,您可以将新的程序视为一个方程组,并以任意顺序读取它。在这种情况下,由于数据依赖性,计算仍然必须从上到下进行,但这通常不需要。

你可以摆脱" ="通过使用函数完全符号。要摆脱" x1 = 2",用x1参数化剩余的行并将2传递给函数:

(function(x1) {
    x2 = x1 * 3
    x3 = x2 + 4
    return x3
})(2)

并继续此过程以删除" x2 = x1 * 3"和" x3 = x2 * 4"。

有时你会在循环中分配一个变量来积累某些东西(例如在数组for x in A: sum += x中添加项目)。在这种情况下,您可以重写程序,使用" sum"," reduce"," map"," filter& #34;,它是循环的高级抽象。


我说谎了。只要可变性没有“泄漏”,就可以在纯函数程序中具有局部可变状态。超出范围。在Haskell中,有一个名为" ST"以此目的。它嵌入了一个小命令式语言,带有命令'创建变量'读变量',写变量',如果你的程序没有泄漏可变变量,你可以在纯函数中检索其结果。但是,使用起来比较笨拙(参见this answer),并且在命令式语言中使用赋值时没有使用它。