mapcanってなによ

なんとなくOn Lispを読み始めた。読む本には不自由していない。向こう数年、することも無いだろう。
さて、3章の終盤で

  1. 彼らは副作用を使う部分をいくつかの関数に隔離し、プログラムの大部分は純粋に関数型スタイルで書けるようにする。
  2. 関数が副作用を使うのをさけられないなら、彼らは少なくともそこに関数型のインターフェースを盛り込もうとする。
  3. 彼らは関数に明確な目的を一つだけ与える。

と出てくるまで、副作用の使用には注意を払うべきであることが書いてあった。だからなのか、4章のall-nicknamesの箇所で出てきたmapcanという関数の使用について、ちょっと悩んだ。前段の刷り込みのおかげで、副作用のあるコードが書いてあると、「何か副作用を使わざるを得ない理由があるのだな...」と先入観を持ってみてしまうようになっていたからかな。
調べてみると、mapcanはschemeでいうとSRFI-1のappend-map!に相当するらしい。これは言い換えると、

(apply append! (map fn list1 list2 list3 ...)

と等価な手続き。append!ってのはSRFI-1の手続きで、appendの副作用バージョン。次のような動作をする。

gosh> (define a (list 1 2 3))
a
gosh> (define b (list 4 5 6))
b
gosh> (define c (list 'foo 'bar 'baz))
c
gosh> (append a b c)
(1 2 3 4 5 6 foo bar baz)
gosh> a
(1 2 3)
gosh> b
(4 5 6)
gosh> c
(foo bar baz)
gosh> (append! a b c)
(1 2 3 4 5 6 foo bar baz)
gosh> a
(1 2 3 4 5 6 foo bar baz)
gosh> b
(4 5 6 foo bar baz)
gosh> c
(foo bar baz)

引数として渡した値に対して破壊的な処理がなされている。実は、append-map!に対して、副作用なしバージョンのappend-mapもあるらしい。こっちは

(apply append (map fn list1 list2 list3 ...))

と等価な手続きらしい。

で、append-map!が有用な局面が想像できないのは置いといて、なぜ、副作用を持つappend-map!に相当するmapcanを使う必要があったのか、ということに関してしばらく悩んだ。実際、ここの例all-nicknamesに限っては、append-mapでもappend-map!でも同じ結果が得られる。

もしかして、理由は単純で、append-mapに相当する関数は無いからなのかな。

http://www.komaba.utmc.or.jp/~flatline/onlispjhtml/
Gauche Users’ Reference: Top