很多程序語言所帶給你的“完美”的感覺都來自於數學抽象之美。
在Lua中,function被描述成“具有真正的詞法範圍的一類值”(first-class values with proper lexical scoping)。
所謂的“一類值”,應該滿足以下條件:
- 可以被保存到變量和數據結構中
- 可以被當作子程序的參數和返回值
- 可以在運行期創建
- 具有內在標識,不依賴於任何給定的名稱
大多數語言的基本數據類型,比如int,就屬於“一類值”。很多語言中的函數實現,只能滿足其中一些條件。比如在C中可以將函數指針保存到變量中,可以將函數指針當作參數和返回值。這樣的函數實現一般不會被算作“一類值"。
在Lua中,所有的值都是“一類”值,包括function本身。函數可以被保存到任何的變量或者table中,可以被當作參數和返回值使用,所有的函數(更準確的說應該是closure)都是運行期被創建的,函數本身並沒有名字,名字只是對函數的引用而已。作為“一類值”的function更為抽象,可以用來表示很多的"Functional Programming"的概念。比如“高階函數”(Higher-order functions),“匿名函數”(Anonymous functions")。這些功能在很多語言中都是通過特殊的語法來支持的,而在lua中則不需要。
而所謂的“真正詞法範圍”,則是說Lua function可以訪問外圍函數中的局部變量。這是通過lua的closure來實現的。
“具有真正的詞法範圍的一類值”使得Lua function可以用來表示" Lambda calculus "。Lambda calculus是Functional Programming的數學基礎。他使用抽象的function和一套簡單的規則來構建一個完整的等價於"Turing Machine"的計算模型。使用Lua function來表示Lambda calculus可以讓你從另一個更真實的角度去理解Lambda calculus的語義,同時也可以更深入的體會Lua function功能的強大。並且對於程序員來說,這本身也是一個非常有趣的嘗試。
Lambda calculus是一個操作lambda expression的系統。Lambda expression由variable,function abstraction和function application組成。我們可以使用一個Lua function的定義來代表一個function abstraction。這個function接受一個參數,也就是variable,並返回一個function。而對這個function的調用,就是function application。這樣,我們就有了lambda calculus基本規則對應的lua實現。Lambda calculus可以通過如此簡單的基本規則構建出各種高層語義,比如數據類型,算數和邏輯運算,循環和遞歸等等。這也就是說,理論上我們可以僅僅使用lua function,而不需要任何其他的語言功能,來進行任何的計算。我想這也就是"functional programming"的極限了吧。
我們首先來看一些最簡單的function。
Identity
λx.x
單位函數直接返回應用的參數。對應的lua function為:
- function identity(x)
- return x;
- end
將一個identity應用到identity,結果還應該是identity。
λx.x λx.x=>λx.x
- print(identity(identity) == identity)
Self Application Function
λs.(ss)
自應用函數將參數應用到參數本身。對應的lua function為:
- function self_apply(s)
- return s(s);
- end
將自應用函數應用到identity,最終會得到identity:
λs.(ss) λx.x => λx.x λx.x => λx.x
- print(self_apply(identity) == identity);
將自應用函數應用到自身,會造成估值不能結束:
λs.(ss) λs.(ss) => λs.(ss) λs.(ss) =>...
同樣,對於lua調用
- self_apply(self_apply)
Function Application Function
λf.λa.(fa)
函數應用函數將參數f應用到參數a上。對應的lua function為:
- function apply(f)
- return (function(a)
- return f(a);
- end);
- end
如果將此函數應用到identity:
λf.λa.(fa) λx.x => λa.(λx.x a)
會得到一個參數為a的函數。而對於lua function也是如此:
- apply(identity)
如果將此函數連續應用到identity:
λf.λa.(fa) λx.x λx.x =>λa.(λx.xa) λx.x => λx.x λx.x => λx.x
效果就和將identity應用到自身是一樣的。
對應的lua調用:
- print(apply(identity)(identity) == identity)
接下來,我們開始構建一些基礎的function,並在這些基礎上構建更高層的語義。
Boolean Values
在lambda calculas中,我們可以通過函數來表示boolean values。
- TRUE可以表示成λf.λs.f,代表選擇兩個參數中的第一個。
- FALSE可以表示成λf.λs.s,代表選擇兩個參數中的第二個。
其對應的lua function為:
- function TRUE(f)
- return (function(s)
- return f;
- end);
- end
- function FALSE(f)
- return (function(s)
- return s;
- end);
- end
我們可以測試一下這個lambda expression:
TRUE identity apply == λf.λs.f identity apply => λs.identity apply => identity
FALSE identity apply == λf.λs.s identity apply => λs.s apply => apply
同樣,對應的lua調用也成立:
- print(TRUE(identity)(apply) == identity) -- true
- print(FALSE(identity)(apply) == apply) -- true
Condition
根據Boolean value的定義,我們可以構造出條件判斷的lambda function:
λt.λf.λb.(btf)
這個表達式的語義是根據boolean值b,來選擇t或者f。如果b為TRUE,就選擇t,否則選擇f。
其對應的lua function為:
- function COND(t)
- return (function(f)
- return (function(b)
- return b(t)(f);
- end);
- end);
- end
我們可以通過將條件表達式應用到identity,apply和TRUE,來看一下結果:
λt.λf.λb.(btf) identity apply TRUE =>
λf.λb(b identity f) apply TRUE =>
λb(b identity apply) TRUE =>
TRUE identity apply =>
identity
λf.λb(b identity f) apply TRUE =>
λb(b identity apply) TRUE =>
TRUE identity apply =>
identity
同樣,對應的Lua調用也成立:
- print(COND(identity)(apply)(TRUE) == identity) -- true
這等同於如下邏輯:
- if(true)
- return identity
- else
- return apply
至此,我們已經看到,僅僅使用lua function,就可以構造出基於if...else...的邏輯判斷語義。
NOT
λb.(COND FALSE TRUE b) == λb.(λt.λf.λb(btf) FALSE TRUE b) => λb(b FALSE TRUE)
這個表達式的語義是:當b為TRUE時,選擇FALSE,否則選擇TRUE。
對應的lua function:
- function NOT(b)
- return b(FALSE)(TRUE);
- end
- print(NOT(TRUE) == FALSE); -- true
- print(NOT(NOT(TRUE)) == TRUE); --true
AND
λx.λy.(COND y FALSE x) == λx.λy.(λt.λf.λb(btf) y FALSE x) => λx.λy.(xy FALSE)
這個表達式的語義是:當x為TRUE時,選擇y,否則選擇FALSE。
對應的lua function:
- function AND(x)
- return (function(y)
- return x(y)(FALSE);
- end);
- end
- print(AND(TRUE)(TRUE) == TRUE); -- true
- print(AND(TRUE)(FALSE) == FALSE); -- true
- print(AND(FALSE)(TRUE) == FALSE); -- true
- print(AND(FALSE)(FALSE) == FALSE); -- true
OR
λx.λy.(COND TRUE yx) == λx.λy.(λt.λf.λb(btf) TRUE yx) => λx.λy.(x TRUE y)
這個表達式的語義是:當x為TRUE時,選擇TRUE,否則選擇y。
對應的lua function:
- function OR(x)
- return (function(y)
- return x(TRUE)(y);
- end);
- end
- print(OR(TRUE)(TRUE) == TRUE); -- true
- print(OR(TRUE)(FALSE) == TRUE); -- true
- print(OR(FALSE)(TRUE) == TRUE); -- true
- print(OR(FALSE)(FALSE) == FALSE); -- true
至此,我們已經有了基本的邏輯運算符NOT,AND和OR。可以通過他們來組合出更複雜的boolean邏輯表達式。
Natural Numbers
使用lambda expression表示自然數,我們首先要定義0。我們將0定義為identity。
- zero = identity
然後,定義一個succ函數,代表自然數n的下一個自然數:
λn.λb.(b FALSE n)
- function succ(n)
- return (function(b)
- return b(FALSE)(n);
- end);
- end
- one = succ(zero);
- two = succ(one);
- three = succ(two);
- four = succ(three);
- five = succ(four);
- six = succ(five);
- seven = succ(six);
- eight = succ(seven);
- nine = succ(eight);
接著我們需要定義一個函數iszero來判斷一個自然數是否為0:
λn.(n TRUE)
然後我們定義一個函數iszero,用來判斷是否是0:
λn.(n TRUE)
- function iszero(n)
- return n(TRUE);
- end
- print(iszero(zero) == TRUE) -- true
- print(iszero(one) == FALSE) -- true
最後,我們還可以定義一個pred函數,用來獲得一個自然數的前一個自然數:
λn.(COND zero (n FALSE) (iszero n)) => λn.((iszero n) zero (n FALSE))
- function pred(n)
- return iszero(n)(zero)(n(FALSE));
- end
這裡麵包含了當n為0時的特殊處理。當n為0時,返回0。
- print(pred(one) == zero) -- true
至此,我們有了基本的自然數的表示方法。接下來,我們將利用自然數來計數,進行一個簡單的循環。
Loop
在Functional Programming中,循環使用遞歸調用來進行。Lambda calculus的遞歸調用是通過將一個Y Conbinator函數引用到一個stepper函數來實現的。stepper函數代表了循環的每一次需要做的事情,而YConbinator函數則多次調用這個stepper函數,來表示循環。
Y Conbinator:
λf.(λx.(f (xx)) λx.(f (xx)))
- function recursive(f)
- return (function(x)
- return f(x(x))
- )(function(x)
- return f(x(x))
- end)
- end
λs.λn.(COND zero (s (pred n) (iszero n))
- function stepper(next_step)
- return (function(n)
- return COND(zero)(next_step(pred(n))(iszero(n))
- end)
- end
- recursive(stepper)(five)
- function recursive(f)
- return (function(x)
- return f(function(dummy) return x(x) end)
- end)(function(x)
- return f(function(dummy) return x(x) end)
- end)
- end
- function stepper(next_step)
- return (function(n)
- return COND(function(dummy)
- return zero
- end)(function(dummy)
- print("one step");
- return next_step(identity)(pred(n))
- end)(iszero(n))(identity)
- end)
- end
- recursive(stepper)(five)
綜上所述,我們已經使用lua function作為lambda calculas的表示形式,從新構建了一個包含了高層語義的計算模型,從而也體會到了lua function高度抽象的能力。希望對大家學習lambda calculus和lua function有所幫助。
No comments:
Post a Comment