cadr、caddr、cadddr…

前のエントリでしらっと cadr という関数を使ってるけど、これは (car (cdr ...)) と等しい。同じ要領で caddr、cadddr もある。

gosh> (cadr '(1 2 3 4 5 6 7 8 9 10))
2
gosh> (caddr '(1 2 3 4 5 6 7 8 9 10))
3
gosh> (cadddr '(1 2 3 4 5 6 7 8 9 10))
4
gosh> (caddddr '(1 2 3 4 5 6 7 8 9 10))
*** ERROR: unbound variable: caddddr
Stack Trace:
_______________________________________

d 4つ以上はないみたいだ。

cdar というのもある。これは (cdr (car ...)) に等しい。

gosh> (cdar '((1 2 3 4) (5 6 7 8)))
(2 3 4)
gosh> (cddar '((1 2 3 4) (5 6 7 8)))
(3 4)
gosh> (cdddar '((1 2 3 4) (5 6 7 8)))
(4)
gosh> (cddddar '((1 2 3 4) (5 6 7 8)))
*** ERROR: unbound variable: cddddar
Stack Trace:
_______________________________________

こっちも d 4つ以上はないみたい。

[追記]

caar というのもあった。

gosh> (car '(((((1) 2) 3) 4) 5))
((((1) 2) 3) 4)
gosh> (caar '(((((1) 2) 3) 4) 5))
(((1) 2) 3)
gosh> (caaar '(((((1) 2) 3) 4) 5))
((1) 2)
gosh> (caaaar '(((((1) 2) 3) 4) 5))
(1)
gosh> (caaaaar '(((((1) 2) 3) 4) 5))
*** ERROR: unbound variable: caaaaar
Stack Trace:
_______________________________________

a 4つまである。どうやら a と d (つまり car と cdr)をあわせて4つまであるみたいだ。

Schemeのunfoldはめんどくさい

こないだ fold について書いた。で、今日は unfold について書こうと思って調べたら、なんだか面倒な関数だということがわかった。
Gauceh のユーザマニュアルより:

Function: unfold p f g seed :optional tail-gen

[SRFI-1] 基本リスト再帰構築子です。 以下のように再帰的に定義されています。

(unfold p f g seed tail-gen) ≡
   (if (p seed)
       (tail-gen seed)
       (cons (f seed)
             (unfold p f g (g seed))))

ここでは、p は終了位置の判定、g は現在の「種」から次の「種」 を生成するのに用い、f はそれぞれの「種」をリストの要素に変換する のに用いられます。

Haskell では種からリスト要素を作るのと次のための種を作るのと、さらに終了判定まで1つの関数で済ませている(Maybe 型を使う)。たとえば100未満のフィボナッチ数列を作るにはこうする。

Prelude Data.List> takeWhile (< 100) $ unfoldr (\ (a, b) -> Just (a, (b, a+b))) 
(0, 1)
[0,1,1,2,3,5,8,13,21,34,55,89]

それに対して、Scheme の unfold ではこうなる。

gosh> (use srfi-1)
#<undef>
gosh> (define f
  (lambda (pr)
    (car pr)))
f
gosh> (define g
  (lambda (pr)
    (list (cadr pr) (+ (car pr) (cadr pr)))))
g
gosh> (unfold (lambda (pr) (> (car pr) 100)) f g '(0 1))
(0 1 1 2 3 5 8 13 21 34 55 89)

種からリストの要素への変換と、次の種の生成、終了条件がそれぞれ別の関数なので面倒。tail-gen の使い方はわからない。なくてもいいみたいだけど。おまけに car や cdr も面倒な原因か。こういうところは Haskell のほうが楽だな。

ちなみに unfold-right を使うと逆順のリストが得られる。

gosh> (unfold-right (lambda (pr) (> (car pr) 100)) f g '(0 1))
(89 55 34 21 13 8 5 3 2 1 1 0)