參數 currying 與函數管道
在寫列表操作時,常會串接 map filter 最後 reduce, 簡化每個操作的內容。 在 javascript 中會用 dot notation 串接每個返回值的 map method, 但 scheme 中就沒辦法,要寫成巢狀不太方便, haskell 中就能搭配 pipe 和函數自動 currying 達成。
(filter (lambda (x) (= 1 (mod x 2)))
(map (lambda (x) (+ x 3))
(map (lambda (x) (* x 7))
'(sequence 10))))
在 scheme 中這樣寫很難閱讀, 反觀 javascript, 因為這些函數都是 Array 的方法, 就能用 dot notation 串接調用。
sequence(10)
.map((x) => x * 7)
.map((x) => x + 3)
.filter((x) => x % 2 == 1)
我有想過建構一個 pipe 函數, 能把值依序傳給每個傳入的函數。
(define (pipe value . function-list)
(fold-left (lambda (value function)
(function value))
value
function-list))
(pipe 12 (lambda (x) (+ x 12))
(lambda (x) (* x 2))
(lambda (x) (mod x 7)))
;; 6
但用在陣列上就還要多寫一次 lambda。
(pipe '(1 2 3 4)
(lambda (list)
(map (lambda (x) (+ x 12))
list))
(lambda (list)
(map (lambda (x) (* x 2))
list))
(lambda (list)
(map (lambda (x) (mod x 7))
list)))
而聽說 haskell 的函數,如果沒有給足參數數量, 就會變成半成品狀態,還是個函數, 再繼續傳入參數到足夠數量就能得到結果。 這是源自 curry 的用單參函數構造多參函數的方法。
// 假設 js 也有 currying
function add(a, b) {
return a + b
}
const add3 = add(3)
add3(7) == 10
add3(6) == 9
add(2, 4) == 6
所以如果 scheme 也有參數 currying,
那上述的對列表的 pipe 直接傳入
(map (lambda (x) (* x 2)))
就同等於
(lambda (list) (map (lambda (x) (* x 2)) list))
。
// currying pipe list
map((x) => x+3, [1,2,3]) // [4,5,6]
const map4 = map((x) => x+4)
map4([1,2,3]) // [5,6,7]
pipe([1,2,3],
map((x) => x * 3)
map((x) => x + 6))
// [9, 12, 15]
更別提 haskell 還有 pipe 算符, 不用辛苦自己實現 pipe 函數。 傳入只差一個參數的函數,就能用作 pipe 單元。
雖然 scheme 可以用自幹出類似輔助函數的方式解決。
(define (apply-list list-operation atom-operation)
(lambda ( . list)
(apply list-operation
(cons atom-operation list))))
(pipe '(1 2 3 4)
(apply-list map (lambda (x) (+ x 12)))
(apply-list map (lambda (x) (* x 2)))
(apply-list filter (lambda (x) (= 2 (mod x 7))))
(apply-list fold-left (lambda (sum x) (+ sum x)) 0))
題外話,還是覺得 S 表達式好美,
haskell 和一般的語言,包括其它偏指令式的,
用到一堆關鍵字 if else,=
賦值好醜。