綁定
細心的讀者可能記得我在 第 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
新聞熱點
疑難解答