Python 是什么让函数组合如此强大?

Python 是什么让函数组合如此强大?,python,python-3.x,functional-programming,Python,Python 3.x,Functional Programming,我读过函数式编程,它的核心概念似乎是高度使用不可变的数据结构,这反过来又导致了纯函数。这些纯函数应该易于组合,如本例所示: def add(x, y): return 3 + 3 def minus1(z): return z - 1 print(minus1(add(3,3))) 我知道,我可以用这种方法组合函数和构建新函数。在以下示例中,我合成了两个不纯函数: def add_list_last(arr): arr.append(1) return ar

我读过函数式编程,它的核心概念似乎是高度使用不可变的数据结构,这反过来又导致了纯函数。这些纯函数应该易于组合,如本例所示:

def add(x, y):
    return 3 + 3

def minus1(z):
    return z - 1

print(minus1(add(3,3)))
我知道,我可以用这种方法组合函数和构建新函数。在以下示例中,我合成了两个不纯函数:

def add_list_last(arr):
    arr.append(1)
    return arr

def insert_entry_at_start(arr):
    arr.insert(0,1)
    return arr

print(add_list_last(insert_entry_at_start([1,2,3])))
为什么我不能像在第二个例子中那样编写不纯函数,为什么它如此强大?第二个示例的缺点是什么?为什么数组的变异会阻止可组合性

T = TypeVar('T')

def split(v: T) -> Tuple[T, T]:
    return v, v

def apply_to_first(f: Callable[[T], T], *xs: T) -> Sequence[T]:
    return (f(xs[0]), *xs[1:])

apply_to_first(insert_entry_at_start, *split([1, 2]))
您可以在这里看到这个结果:

([1, 2, 1], [1, 2])
但事实上,你得到:

([1, 2, 1], [1, 2, 1])

因为
insert\u entry\u在\u start
是不纯的。

这里有一个简化的例子:

def prepend_impure(lst, element):
    lst.insert(0, element)
    return lst

def prepend_pure(lst, element):
    cpy = lst[:]
    cpy.insert(0, element)
    return cpy

def combine(list1, list2):
    return [i + j for i, j in zip(list1, list2)]

list1 = [2, 3, 4]
list2 = [2, 3, 4]

print(combine(prepend_impure(list1, 1), prepend_impure(list1, 1)))
print(combine(prepend_pure(list2, 1), prepend_pure(list2, 1)))
输出:

[2, 2, 4, 6, 8]
[2, 4, 6, 8]
prepend
函数在给定的
列表的开头插入一个元素
,并返回
列表

combine
功能将两个列表添加到一起

就我个人而言,我希望代码既返回
列表
[1,2,3,4]
中的
项,又将这2项相加


你不仅不能从不纯的版本中得到你想要的。。在一个大型的代码库中,试图记住函数可以改变或修改对象的所有方式可能会产生大量认知过载。

由于列表在python中是可变的,因此您将更改原始数组。假设您有一个名为
original
的数组,您最后运行
modified=add_list_(在开始时插入条目(original))
您希望original和modified是不同的,但它们是完全相同的数组(不是副本,而是字面上相同的数组)如果函数是纯函数,那么可以使用很多聪明的技巧。然而,如果它们是不纯的,那么当
f(x)
不再是每次相同的操作时,这就限制了你很多。最简单的例子是记忆化-如果
f
不再可记忆化,那么您的代码就失去了一条基本上免费的优化路径。@VLAZ您所说的可记忆化是什么意思?是一种缓存方式。您基本上建立了一个函数的输入到输出的映射。因此,如果您调用
f(1)
并获得
“a”
,则当
1
被传递到
f中时,您无需第二次重新计算
f
,您只需从缓存映射中获取它。但是,如果
f(1)
可以产生
“a”
“b”
,那么你就不能记忆函数-相同的输入不一定产生相同的输出。因此纯函数的合成功能非常强大,因为你可以用纯函数合成新的纯函数,对吗?是的…!?换言之,如果函数程序中的任何函数不纯净且具有副作用,则很难对程序的行为进行推理。