簡單的Functional Programming in Javascript練習
Thu, 01 May 2008 20:04:08 +0800之前看過蔡學鏞關於F#的介紹,覺得很有意思,於是在網上找了一些資料看看。在wikipedia關於Lambda Calculus的介紹中,發現他把Javascript也歸類於Functional Languages:
....Modern functional languages, building on the lambda calculus, include Lisp, Scheme, ML, ECMAScript (the most common implementation being JavaScript) and Haskell.
(Lambda Calculus)
為了嘗試跟練習,就試著把Wikipedia解釋Continuation-passing style中用Scheme寫的例子來改寫成Javascript試試看;
第一個例子是計算兩個數字平方合的平方,例子中的Scheme程式如下...
這個是direct style的寫法:
(define (pyth x y) (sqrt (+ (* x x) (* y y))))
同樣的計算,改成continuation passing style的寫法:
(define (pyth x y k) (* x x (lambda (x2) (* y y (lambda (y2) (+ x2 y2 (lambda (x2py2) (sqrt x2py2 k))))))))
改成Javascript版本的direct style:
function pyth(x,y) { return sqrt(x*x+y*y); } function sqrt(x) { return x*x; } alert(pyth(2,3));
改成javascript版本的continuation passing style:
function pyth1(x,y,k) { (function(x2){ (function(y2){ (function(x2py2){ k(sqrt(x2py2)); })(x2+y2); })(y*y); })(x*x); } function sqrt(x) { return x*x; } pyth1(2,3,function(x){alert(x);});
第二個例子是階乘...
Scheme的direct style:
(define (factorial n) (if (= n 0) 1 (* n (factorial (- n 1)))))
Scheme的continuation passing style:
(define (factorial n k) (= n 0 (lambda (b) (if b (k 1) (- n 1 (lambda (nm1) (factorial nm1 (lambda (fnm1) (* n fnm1 k))))))))
Javascript的direct style:
function factorial(n) { if(n==0) { return 1; } else { return n*factorial(n-1); } } alert(factorial(5));
Javascript的continuation passing style:
function factorial1(n, k) { (function(b){ if(b) { k(1); } else { (function(nm1){ factorial1(nm1,function(fnm1){k(n*fnm1);}); })(n-1); } })(n==0); } factorial1(5,function(x){alert(x);});
第三個例子還是階乘,但是改成呼叫一個外部的函數來遞迴...
Scheme的direct style:
(define (factorial n) (f-aux n 1)) (define (f-aux n a) (if (= n 0) a (f-aux (- n 1) (* n a))))
Scheme的continuation passing style:
(define (factorial n k) (f-aux n 1 k)) (define (f-aux n a k) (= n 0 (lambda (b) (if b (k a) (- n 1 (lambda (nm1) (* n a (lambda (nta) (f-aux nm1 nta k)))))))))
Javascript的direct style:
function factorial(n) { return f_aux(n,1); } function f_aux(n, a) { if(n==0) { return a; } else { return f_aux(n-1, n*a); } } alert(factorial(5));
Javascript的continuation passing style:
function factorial1(n, k) { f_aux1(n, 1, k); } function f_aux1(n, a, k) { (function(b){ if(b) { k(a); } else { (function(nm1) { (function(nta){ f_aux1(nm1, nta, k); })(n*a); })(n-1); } })(n==0); } factorial1(5, function(x){alert(x);});
我沒學過Lisp或Scheme,不過這幾個例子算是淺顯易懂,所以把他依照Scheme的程式的邏輯結構直接一對一地改成Javascript並沒有太大的困難,更有趣的是,直接「翻譯」成javascript以後,完全可以正常執行,一點都不用改。
這裡看得到Javascript的functional programming language的特色:用函數做為參數、用匿名函數實現lambda calculus等,不過沒有用到返回函數這個特色,另外似乎也沒有明顯展現lexical scope的特色。恩恩,果然javascript在設計上是有functional programming的能力的。
(當然,這些例子也可以看出,javascript也可以用continuation passing style來改寫,讓它具備continuation的能力,計算的結果是靠傳進去的函數「捕捉」然後呈現出來的...以後再來試作一些比較實用的例子)