Gaucheの無名関数について - lambda, ^, ^c, cut
背景
とあるネット記事のGaucheのコードを読んでいて^ _ ( 何か処理 )
というのがあってこれは何だと思って調べたメモです。
^
自体は知っていたのですが、^ _
という別の記法もあるのかなと不思議に思ったので。
無名関数
Gaucheの無名関数はlambda
で書きます。
; 無名関数の定義 gosh> (lambda (a b) (+ a b)) #<closure (#f a b)> ; 無名関数を即実行 gosh> ((lambda (a b) (+ a b)) 1 2) 3
lambda
は^
という略記もあります。
Special Form: ^ formals body …
^はlambdaの短い別名です。これはGauche独自の拡張です。
gosh> (^(a b) (+ a b) 1 2) #<closure (#f a b)> gosh> ((^(a b) (+ a b)) 1 2) 3 gosh>
lambda
もしくは^
は引数部分を(a)
ではなくa
にすると可変長引数を受け取ります。
variable : 手続きは不定個の引数を取ります。 実引数は新しいリストに集められて、そのリストがvaribleに束縛されます。
((lambda a a) 1 2 3) ⇒ (1 2 3)
; 引数をそのまま返すだけの無名関数 gosh> (^ (x) x) #<closure (#f x)> gosh> ((^ (x) x) 10) 10 ; 引数定義をカッコで指定しないと、(#f . x)という定義になっている gosh> (^ x x) #<closure (#f . x)> ; 実行してみると、リストで渡っているのが分かる gosh> ((^ x x) 10) (10) gosh> ((^ x x) 10 20) (10 20) ; ちなみにもちろんカッコ付きの方だと引数の個数が合わないとエラーになる gosh> ((^ (x) x) 10 20) *** ERROR: wrong number of arguments: #f requires 1, but got 2 While compiling "(standard input)" at line 8: ((^ (x) x) 10 20) gosh>
冒頭の^ _
はこれを意図した書き方だったのかもしれません。
他の言語でもイディオムとして良くあるように、引数を使わないことを意味するため_
にしていたのだと思います。実際使っていなかったので。
またこれらとは別に、^c
というのもあります。
Macro: ^c body …
(lambda (c) body …)の短縮表記です。 cには#[_a-z]に含まれる任意の一文字が使えます。
(map (^x (* x x)) ‘(1 2 3 4 5)) ⇒ (1 4 9 16 25)
; 以下の2つは同じなはず gosh> ((^x (+ 1 2))) 3 gosh> ((^_ (+ 1 2))) 3
スペースがあると前述の(#f . x)
になるので注意が必要です。最初この違いに気付けず、スペース付きのまま試して期待通りにいかず悩みました。
gosh> (^x (+ 1 2)) #<closure (#f x)> gosh> (^ x (+ 1 2)) #<closure (#f . x)>
ちなみに部分適用に向いたcut
というのもあります。
Macro: cut expr-or-slot expr-or-slot2 …
[SRFI-26] 手続きを簡潔に書ける便利なマクロです。 いわゆる部分適用を実現するために使えます。
; 引数なしの定義 gosh> (cut + 1 2) #<closure (#f)> ; 定義して即実行 gosh> ((cut + 1 2)) 3 ; 2引数の定義 gosh> (cut + <> <>) #<closure (#f #<identifier srfi-26##<identifier srfi-26#x.126a9c0>.1293ee0> #<identifier srfi-26##<identifier srfi-26#x.126a9c0>.1293e00>)> gosh> ((cut + <> <>) 1 2) 3
本来はmapとかfor-eachとかそういうやつで使うべきな気がします。
; こう書くより gosh> (map (^a (+ a 1)) '(1 2 3)) (2 3 4) ; こう書いた方が一時変数置かなくて済むのでスマートでしょ的な gosh> (map (cut + <> 1) '(1 2 3)) (2 3 4)
余談: 調べ方
今回のような記号(^
)に関するリファレンスを調べる場合でもapropos
とinfo
で簡単に辿り着けるのがとても良いです。
; ^を含むリファレンスを探す gosh> (apropos '^) ^ (gauche) ^-generator (gauche) ^_ (gauche) ^a (gauche) ^b (gauche) ^c (gauche) ^d (gauche) ^e (gauche) ^f (gauche) ^g (gauche) ^h (gauche) ^i (gauche) ^j (gauche) ^k (gauche) ^l (gauche) ^m (gauche) ^n (gauche) ^o (gauche) ^p (gauche) ^q (gauche) ^r (gauche) ^s (gauche) ^t (gauche) ^u (gauche) ^v (gauche) ^w (gauche) ^x (gauche) ^y (gauche) ^z (gauche) define-^x (gauche) gosh> (info '^) ; リファレンスが開く
余談
公開前にプレビューで推敲したらlambdaに著作権発生してた。