绑定
细心的读者可能记得我在 第 1 部分的函数技术中指出的限制。特别在 Python 中不能避免表示函数表达式的名称的重新绑定。在 FP 中,名称通常被理解为较长表达式的缩写,但这一说法暗示着“同一表达式总是求出相同的值”。如果标记的名称重新被绑定,这一暗示便不成立。例如,让我们定义一些在函数编程中要用到的快捷表达式,比如:
清单 1. 以下 Python FP 部分的重新绑定要造成故障
>>> car = lambda lst: lst[0]>>> cdr = lambda lst: lst[1:]>>> sum2 = lambda lst: car(lst)+car(cdr(lst))>>> sum2(range(10))1>>> car = lambda lst: lst[2]>>> sum2(range(10))5
不幸的是,完全相同的表达式 sum2(range(10)) 在程序中的两处求得两个不同的值,即使该表达式自身并没有在其参数中使用任何可变变量。
幸运的是, functional 模块提供了称为 Bindings 的类(向 Keller 提议)来防止这样的重新绑定(至少在偶然情况下,Python 不会阻止一心想要解除绑定的程序员)。然而使用 Bindings 需要一些额外的语法,这样意外就不太容易发生。在 functional 模块的示例中,Keller 将 Bindings 实例命名为 let (我假定在 ML 家族语言的 let 关键词的后面)。 例如,我们会这样做:
清单 2. 具有安全重新绑定的 Python FP 部分
>>> from functional import *>>> let = Bindings()>>> let.car = lambda lst: lst[0]>>> let.car = lambda lst: lst[2]Traceback (innermost last): File "<stdin>", line 1, in ? File "d:/tools/functional.py", line 976, in __setattr__ raise BindingError, "Binding '%s' cannot be modified." % namefunctional.BindingError: Binding 'car' cannot be modified.>>> car(range(10))0
很明显,真正的程序必须做一些设置来捕获“绑定错误”,而且他们被抛出也避免了一类问题的出现。
与 Bindings 一起, functional 提供 namespace 函数从 Bindings 实例中获取命名空间(实际上是个字典)。如果希望在 Bindings 中定义的(不可变)命名空间中运算一个表达式,这非常容易实现。Python 的 eval() 函数允许在命名空间中进行运算。 让我们通过一个示例来弄清楚:
清单 3. 使用不可变命名空间的 Python FP 部分
>>> let = Bindings() # "Real world" function names>>> let.r10 = range(10)>>> let.car = lambda lst: lst[0]>>> let.cdr = lambda lst: lst[1:]>>> eval('car(r10)+car(cdr(r10))', namespace(let))>>> inv = Bindings() # "Inverted list" function names>>> inv.r10 = let.r10>>> inv.car = lambda lst: lst[-1]>>> inv.cdr = lambda lst: lst[:-1]>>> eval('car(r10)+car(cdr(r10))', namespace(inv))17
新闻热点
疑难解答