For Development HEAD DRAFTSearch (procedure/syntax/module):

6.6 ペアとリスト

ペアとリストはSchemeにおける最も基本的なデータ構造のひとつです。 Gaucheのコアは全ての標準のリスト手続きと、多くのScheme実装に見られる 便利な手続きのいくつかを提供します。それらで足りない場合は さらに多くの手続きがscheme.list - R7RSリストutil.combinations - 組み合わせ といったモジュールで提供されています。 また、リストに限らないジェネリックなシーケンス/コレクションの操作が gauche.collection - コレクションフレームワークgauche.sequence - シーケンスフレームワークで提供されています。


6.6.1 pairクラスとnullクラス

Builtin Class: <list>

リストを表す抽象クラスで、クラス<null>とクラス<pair>の親クラスです。 クラス<sequence>を継承します。

循環リストやドットリストも<list>クラスのインスタンスですが、 list?は偽の値を返すことに注意して下さい。

(use srfi.1)
(list? (circular-list 1 2)) ⇒ #f
(is-a? (circular-list 1 2) <list>) ⇒ #t
Builtin Class: <null>

空リストのクラスです。()がこのクラスの唯一のインスタンスです。

Builtin Class: <pair>

ペアのクラスです。


6.6.2 変更可能なペアと変更不可なペア

ペアには変更可能なものと変更不可なものがあります。 変更可能なペアは、set-car!set-cdr!等の破壊的変更手続き (通常、名前の最後に!がついています)で変更することが可能です。 変更不可なペアを変更しようとするとエラーが報告されます。

Gaucheでは、変更しようとしない限り、どちらのペアも全く同じように振る舞います。 pair?にはどちらも真を返します。ペアが変更不可であることを調べたければ ipair?が使えます(リストに関する述語参照)。 伝統的なコンストラクタconsは変更可能なペアを作ります。 変更不可なペアを作るにはipairを使ってください (リストの作成参照)。 変更不可なペアを特に扱う手続きは、R7RS-largeのscheme.ilistモジュールに 定義されています (scheme.ilist - R7RS変更不可リスト参照)。

クオートされたリテラルは変更不可です。 ただ、古いバージョンのGaucheは変更不可なペアを持っておらず、クオートされたリテラルリストも 変更可能でした。その頃に書かれたコードでうっかりリテラルを変更していたら、 現在のGaucheではエラーになってしまうでしょう。 問題を修正せずにどうしても古いコードのまま走らせたい場合は、環境変数 GAUCHE_MUTABLE_LITERALSを定義してください。


6.6.3 リストに関する述語

Function: pair? obj

[R7RS base] objがペアなら#tを、そうでなければ#fを返します。

Function: ipair? obj

[R7RS ilist] objが変更不可なペアなら#tを、そうでなければ#fを返します。

変更不可なペアは、この述語を使うか、あるいはそのペアを変更しようとした場合を除いては、 変更可能なペアと全く同じように動作します。 詳しくは変更可能なペアと変更不可なペア参照。

Function: null? obj

[R7RS base] objが空リストなら#tを、そうでなければ#fを返します。

Function: null-list? obj

[R7RS list] obj が空リストなら#tを、ペアなら#fを返し、 それ以外のときはエラーを報告します。

リスト終了条件を検査する時に、正しいリスト以外を除外したい場合に null?の代わりに使えます。

Function: list? obj

[R7RS base] objが正しいリストなら#tを、そうでなければ#fを返します。 この手続きはobjがドットリストや循環リストなら#fを返します。

下に説明する、 proper-list?circular-list?dotted-list? といった手続きも参照してください。

Function: proper-list? x

[R7RS list] x が真性リスト、つまり()で終端された有限のリストであれば #t を、そうでなければ#fを返します。

Function: circular-list? x

[R7RS list] x が循環リストであれば #t を返します。リストをcdr方向に 辿って行って、既に訪れたペアに出会ったら、それは循環リストです。 リストの頭 (x) が循環に含まれている必要はありません。 また、car方向への循環があってもそれだけでは循環リストとはみなされません。

Function: dotted-list? x

[R7RS list] x が有限の大きさで、空リストで終端していないリストなら #t を返します。これには、ペアではなく、空リストもない値(たとえば シンボルや数値)のような長さ0のドットリストと考えられるものを含みます。


6.6.4 リストの作成

Function: cons obj1 obj2

[R7RS base] obj1obj2の変更可能なペアを作成して返します。

(cons 'a 'b) ⇒ (a . b)
Function: ipair obj1 obj2

[R7RS ilist] obj1obj2の変更不可なペアを作成して返します。

(ipair 'a 'b) ⇒ (a . b)
Function: make-list len :optional fill

[R7RS base] 長さlenの正規のリストを返します。引数fillが与えられていれば、各要素は fillになります。そうでなければ各要素の値は不定です。

(make-list 5 #t) ⇒ (#t #t #t #t #t)
Function: list obj …

[R7RS base] 要素がobj …であるリストを作成します。

(list 1 2 3) ⇒ (1 2 3)
(list) ⇒ ()
Function: ilist obj …

[R7RS ilist] 要素がobj …であり、変更不可なペアで作られたリストを作成します。

(ilist 1 2 3) ⇒ (1 2 3)
(ilist) ⇒ ()

(list-set! (ilist 1 2 3) 1 'a)
  ⇒ ERROR: Attempt to modify an immutable pair: (2 3)
Function: list* obj1 obj2 …
Function: cons* obj1 obj2 …

[R7RS list] listとほぼ同じですが、最後の引数が作成されるリストの最後のペアのcdrになります。 二つの手続きは全く同じです。Gaucheはもともとlist*を持っていましたが、 SRFI-1 (R7RS (scheme list)) がcons*という名前を定義しました。

(list* 1 2 3) ⇒ (1 2 . 3)
(list* 1) ⇒ 1
Function: list-copy list

[R7RS base] listの浅いコピーを行います。 listが循環リストの場合、エラーが投げられます。 (循環リストを検出するのはGaucheの拡張です。 R7RSではこの手続きは停止しなくても良いことになっています。)

Function: iota count :optional (start 0) (step 1)

[R7RS list] startから始まり、stepずつ増加する、 count 個の要素からなる数値のリストを返します。countは 非負の整数でなければなりません。startstepが ともに正確数であれば、結果は正確数のリストになります。そうでなければ 結果は非正確数のリストです。

(iota 5)        ⇒ (0 1 2 3 4)
(iota 5 1 3/7)  ⇒ (1 10/7 13/7 16/7 19/7)
(iota 5 0 -0.1) ⇒ (0 -0.1 -0.2 -0.3 -0.4)

この手続きはリストを最後まで作って返します。リストが短ければ 十分速いですが、何万という数をリストにしたければ、遅延リストを使った方が 良いかもしれません。liotaを見てください(遅延シーケンス参照)。

Macro: cond-list clause …

条件によりエントリを追加することによりリストを構築します。 それぞれのclauseは条件と式を持ちます。 条件が真であれば、関連する式の結果が結果のリストの構築に使われます。 条件が偽であれば、何も挿入されません。

clauseは、以下のフォームのうちの1つでなければなりません。

(test expr …)

testが評価され、それが真ならばexpr …が評価され、 戻り値が結果の一部となります。exprが与えられなければ、 testの結果が偽でなければその結果が使われます。

(test => proc)

testが評価され、それが真ならばprocがその値とともに 呼ばれ、その戻り値が結果を構築するために使われます。

(test @ expr …)

(test expr …)のように動作しますが、最後のexprの 値はリストでなければならず、それは結果のリストに(unquote-splicingのように) スプライスされます。

(test => @ proc)

(test => proc)のように動作しますが、procの戻り値 はリストでなければならず、それは結果のリストに(unquote-splicingのように) スプライスされます。

(let ((alist '((x 3) (y -1) (z 6))))
 (cond-list ((assoc 'x alist) 'have-x)
            ((assoc 'w alist) 'have-w)
            ((assoc 'z alist) => cadr)))
  ⇒ (have-x 6)

(let ((x 2) (y #f) (z 5))
  (cond-list (x @ `(:x ,x))
             (y @ `(:y ,y))
             (z @ `(:z ,z))))
  ⇒ (:x 2 :z 5)

6.6.5 リストへのアクセスと変更

Function: car pair
Function: cdr pair

[R7RS base] それぞれpairのcarとcdrを返します。

Function: set-car! pair obj
Function: set-cdr! pair obj

[R7RS base] pairのcarもしくはcdrをobjで置き換えます。

注: (setter car)set-car! であり、 (setter cdr)set-cdr! です。

Function: caar pair
Function: cadr pair

Function: cdddar pair
Function: cddddr pair

[R7RS base][R7RS cxr] caar(car (car x)), cadr(car (cdr x)), 等々。

R7RSでは、2レベルよりも深いアクセサは(scheme cxr)ライブラリに含まれます。

対応するsetterも定義されています。

(let ((x (list 1 2 3 4 5)))
  (set! (caddr x) -1)
  x)
  ⇒ (1 2 -1 4 5)
Function: length list

[R7RS base] 正規のリストlistの長さを返します。 listがドットリストならばエラーが起きます。 listが循環リストの場合、この関数は無限ループします。

Function: length+ x

[R7RS list] x が真性リストなら、その長さを返します。 x がそうでなければ(たとえ循環リストであっても) #f を返します。

ドットリストの「長さ」を、構成するペアの数で知りたい場合は、 下のnum-pairsが使えます。

Function: length=? x k
Function: length<? x k
Function: length<=? x k
Function: length>? x k
Function: length>=? x k

それぞれ、リストxの長さが正確な整数kと等しいか、k未満か、k以下か、kより大きいか、 k以上かの場合に#tを、そうでなければ#fを返します。 この関数はk要素までしかリストを辿らないので、特にxが遅延シーケンス (遅延シーケンス参照)である場合に、必要以上にリストを実体化しなくて済みます。

xはドットリストや循環リストでも構いません。 ドットリストの長さを数える際には最後のペアのcdrは無視します。 つまり、ペアでないオブジェクトの長さは0で、(a . b)の長さは1になります。 循環リストは無限の長さを持つとみなします。

(length<=? '(a b) 2)  ⇒ #t
(length<=? '(a b) 1)  ⇒ #f
(length<=? '()    0)  ⇒ #t

;; dotted list cases
(length<=? 'a       0)  ⇒ #t
(length<=? '(a . b) 0)  ⇒ #f
(length<=? '(a . b) 1)  ⇒ #t

註: これらの手続きの名前は混乱を招きやすいかもしれません。something<=?等の 名前は通常、同じ型のオブジェクト同士の比較に使われるからです。 残念ながら、今のところより良い名前を思いつけていません。

Function: num-pairs lis

リストlisを構成するペアの数を正確な整数で返します。lisはドットリストや循環リスト でも構いません。lisが正しいリストなら戻り値はlengthと同じです。

(num-pairs '(a b c d e))    ⇒ 5
(num-pairs '())             ⇒ 0
(num-pairs '(a b c d . e))  ⇒ 4
(num-pairs 'a)              ⇒ 0
(num-pairs '#0=(a b . #0#)) ⇒ 2
(num-pairs '(a . #0=(b c . #0#))) ⇒ 3
Function: take x i
Function: drop x i

[R7RS list] take はリスト x の最初のi個の要素を返します。 drop はリスト x の最初のi個の要素を除いたリストを返します。

(take '(a b c d e) 2) => (a b)
(drop '(a b c d e) 2) => (c d e)

x はあらゆる値をとりえます。

(take '(1 2 3 . d) 2) => (1 2)
(drop '(1 2 3 . d) 2) => (3 . d)
(drop '(1 2 3 . d) 3) => d

dropxi 回 cdr 操作をおこなうのと全く 同じです。返される値は、x と共通の末尾を共有します。一方、 take は、引数のリストが長さ0でないリストなら必ず新しいリストの 領域を確保します。

i がリスト x の終端を超えたらエラーが発生します。 より寛容な手続きについては、下のtake*drop*を見てください。

あらゆる並びからの部分並びを抽出する汎用的な方法に関しては、 シーケンスのスライス にある subseq を参照してください。

Function: take* list k :optional (fill? #f) (padding #f)
Function: drop* list k

takedropのより寛容なバージョンです。 これらの手続きは、リストがkより短くてもエラーを通知しません。

その場合、take*はデフォルトではlistのコピーを 返します。もしfill?が真であれば、足りない要素の部分に paddingを追加してk要素にしたリストを返します。

一方、drop*はリストの長さが不足する場合は単に空リストを返します。

(take* '(a b c d) 3)       ⇒ (a b c)
(take* '(a b c d) 6)       ⇒ (a b c d)
(take* '(a b c d) 6 #t)    ⇒ (a b c d #f #f)
(take* '(a b c d) 6 #t 'z) ⇒ (a b c d z z)
(drop* '(a b c d) 3)       ⇒ (d)
(drop* '(a b c d) 5)       ⇒ ()

注意: 一般的な、いかなるシーケンスからのサブシーケンスの抽出については、 シーケンスのスライスsubseqを見て下さい。

Function: take-right lis k
Function: drop-right lis k

[R7RS list] take-rightlis の最後の k個の要素 からなるリストを返します。 drop-rightlis の最後の k個の要素を 除いたリスト返します。

(take-right '(a b c d e) 2) => (d e)
(drop-right '(a b c d e) 2) => (a b c)

lis は有限リストであればOKです。

(take-right '(1 2 3 . d) 2) => (2 3 . d)
(drop-right '(1 2 3 . d) 2) => (1)
(take-right '(1 2 3 . d) 0) => d
(drop-right '(1 2 3 . d) 0) => (1 2 3)

take-right の返す値はいつでも lis の共通の末尾を共有します。 drop-right は、引数が長さが0でないリストなら、必ず新しいリストの 領域を確保します。

k がリスト lis の長さより大きければエラーが発生します。 より寛容なバージョンについては下のtake-right*, drop-right*を 参照してください。

Function: take-right* list k :optional (fill? #f) (padding #f)
Function: drop-right* list k

take*及びdrop*と同じですが、listの右端からカウントします。 これらはlistk要素より短い場合にもエラーを通知しません。 drop-right*はその場合は単に空リストを返します。 take-right*はその場合はデフォルトでlistそのものを 返しますが、fill?が真であればlistの右側に 足りない分だけpaddingを足したものを返します。その場合でも 結果の尾部はlistと共有されます。

Function: take! lis k
Function: drop-right! lis k

[R7RS list] take および drop-right の その場で更新されるバージョンです。これらの 手続きは lis を破壊的に変更するかもしれません。

lis が循環リストなら、take! は期待されるものより短いリストを返す 可能性があります。

Function: list-tail list k :optional fallback

[R7RS base] listk番目のcdrを返します。listは 正規のリストでもドットリストでも循環リストでも構いません。 (listがドットリストの場合、最後のcdrは無視されます)。

kの値が負であったりlistの長さ以上の場合、 fallback引数が与えられていればそれが返され、 そうでなければエラーが報告されます。

Function: list-ref list k :optional fallback

[R7RS+ base] listk番目の要素を返します。listは 正規のリストでもドットリストでも循環リストでも構いません。

もしkがリストの長さを超えていたり、負数であった場合は通常はエラーが起こります。 しかし、オプショナルな引数fallbackが与えられていた場合は、エラーは起きず fallbackが返されます。これはGaucheの拡張です。

Function: list-set! list k v

[R7RS base] リストlistk番目の要素をvへと変更します。 kが0から(- リストの長さ 1)の範囲の正確な整数でない場合は エラーが報告されます。listが変更不可なリストであった場合、 エラーは報告されませんが、その後の振る舞いは不定となります。

Function: last-pair list

[R7RS list] listの最後のペアを返します。listは 正規のリストかドットリストです。

(last-pair '(1 2 3))   ⇒ (3)
(last-pair '(1 2 . 3)) ⇒ (2 . 3)
(last-pair 1)          ⇒ error
Function: last pair

[R7RS list] 空ではない有限リスト pair の最後の要素を返します。 これは、(car (last-pair pair)) と同等です。

(last '(1 2 3))   ⇒ 3
(last '(1 2 . 3)) ⇒ 2
Function: split-at x i
Function: split-at! x i

[R7RS list] split-at はリスト x をインデックス i の 位置で分割し、最初の i 個の要素からなるリストと、残りの末尾とを 返します。

(split-at '(a b c d e) 2) ⇒ (a b) (c d e)

split-at! はその場で更新されるバージョンです。 これは x を破壊的に更新するかもしれません。

Function: split-at* list k :optional (fill? #f) (padding #f)

SRFI-1 (R7RS (scheme list))のsplit-atの寛容なバージョンです。 take*drop*の結果を返します。

(split-at* '(a b c d) 6 #t 'z)
  ⇒ (a b c d z z) and ()
Function: slices list k :optional fill? padding

listを、それぞれの長さがkであるようなサブリスト(スライス)に 分割します。 listの長さがkの整数倍でない場合は、最後のスライスは take*と同じ方法で扱われます。つまり、デフォルトではkより 短いもの、あるいはfill?が真ならばpaddingが追加されます。

(slices '(a b c d e f g) 3)
  ⇒ ((a b c) (d e f) (g))
(slices '(a b c d e f g) 3 #t 'z)
  ⇒ ((a b c) (d e f) (g z z))
Function: intersperse item list

listの要素の間にitemを挿入します。 (引数の順番は、Haskellのintersperseと同じです。)

(intersperse '+ '(1 2 3))  ⇒ (1 + 2 + 3)
(intersperse '+ '(1))      ⇒ (1)
(intersperse '+ '())       ⇒ ()

6.6.6 リストをたどる手続き

Function: map proc list1 list2 …

[R7RS+ base] 与えられたリストの各要素に対してprocを適用し、その結果をリストにして 返します。R7RSではprocの適用順序が定められていませんが、Gaucheでは 常にprocはリスト内の順番で呼ばれます。 複数のリストが与えられた場合、最も短いリストが終了した時点でprocの適用を 打ち切ります。

(map car '((a b) (c d) (e f))) ⇒ (a c e)

(map cons '(a b c) '(d e f))
  ⇒ ((a . d) (b . e) (c . f))

gauche.collectionモジュール(gauche.collection - コレクションフレームワーク参照) を使うと、mapがリスト以外のコレクション型に対しても動作するようになります。

Function: append-map f clist1 clist2 …
Function: append-map! f clist1 clist2 …

[R7RS list] 機能的には以下と同等ですが、若干効率が良いです:

  (apply append (map f clist1 clist2 ...))
  (apply append! (map f clist1 clist2 ...))

引数のリストのうち少くともひとつは有限でなければなりません。

Function: map* proc tail-proc list1 list2 …

mapとほぼ同じですが、引数の最後のペアのcdrtail-procが 適用され、その返り値が結果のリストの最後のペアのcdrになります。 この手続きは正規でないリストを引数に取ることができます。 引数のリストがひとつだけの場合、tail-procの引数は必ずペアでないオブジェクトです。

(map* - / '(1 2 3 . 4)) ⇒ (-1 -2 -3 . 1/4)

(define (proper lis)
  (map* values
        (lambda (p) (if (null? p) '() (list p)))
        lis))

(proper '(1 2 3))     ⇒ (1 2 3)
(proper '(1 2 3 . 4)) ⇒ (1 2 3 4)

二つ以上のリストが与えられた場合は、最も短いリストがtail-procの呼び出しを 決めます。map*が最も短いリストの最後のペアに達した時点で、 その時のそれぞれのペアのcdrがtail-procへと渡されます。

(map* + vector '(1 2 3 4) '(1 2 . 3))
  ⇒ (2 4 . #((3 4) 3))

註: map*という名前は、正規でないリストを作り得るlist*/cons*からの 連想でつけられました (リストの作成scheme.list - R7RSリスト参照)。

Function: for-each proc list1 list2 …

[R7RS base] 手続きprocをリストの各エレメントに対して順に適用します。 procの結果は捨てられます。for-eachの戻り値は定義されていません。 複数のリストが与えられた場合、一番短いリストが終了した時点でfor-eachは終了します。

gauche.collectionモジュール(gauche.collection - コレクションフレームワーク参照) を使うと、for-eachがリスト以外のコレクション型に対しても動作するようになります。

Function: fold kons knil clist1 clist2 …

[R7RS list] 基本的なリスト反復演算子です。単一のリスト clist1 = (e1 e2en) を与えられたときには、以下を返します。

(kons en ... (kons e2 (kons e1 knil)) ... )

n 本のリストが与えられた場合には、kons 関数は n+1 個の引数 をとる関数でなければなりません。それぞれのリストから要素をひとつずつと、 初期値 knil である「種」あるいは畳み込み状態とよばれるものです。 この畳み込み演算は、もっとも短いリストの要素がなくなったところで終了します。 与えられるリストの少くともひとつは有限でなければなりません。

例:

(fold + 0 '(3 1 4 1 5 9)) ⇒ 23 ;sum up the elements
(fold cons '() '(a b c d e)) ⇒ (e d c b a) ;reverse
(fold cons* '() '(a b c) '(1 2 3 4 5))
    ⇒ (c 3 b 2 a 1) ;n-ary case
Function: fold-right kons knil clist1 clist2 …

[R7RS list] 基本的な再帰演算子です。単一のリスト clist1 = (e1 e2en) を与えられたときには、以下を返します。

(kons e1 (kons e2 ... (kons en knil)))

n 本のリストが与えられた場合には、kons 関数は n+1 個の引数 をとる関数でなければなりません。それぞれのリストから要素をひとつずつと、 初期値 knil である「種」あるいは畳み込み状態とよばれものです。 この畳み込み演算は、もっとも短いリストの要素がなくなったところで終了します。 与えられるリストの少くともひとつは有限でなければなりません。

例:

(fold-right cons '() '(a b c d e))
   ⇒ (a b c d e) ;copy list
(fold-right cons* '() '(a b c) '(1 2 3 4 5))
   ⇒ (a 1 b 2 c 3) ;n-ary case
Function: fold-left snok knil clist1 clist2 …

[R6RS] これは左結合のfoldの別バージョンです。一つのリスト clist1 = (e1 e2en) が与えられた場合、次の値を返します。

(snok (... (snok (snok knil e1) e2) ...) en)

上のfoldと見比べてみてください。結合の順序は同じですが、 snokに渡される引数の順序が、foldにおいてkonsに渡される 引数の順序とは逆になっています。snokが可換な演算であれば、 foldfold-leftの結果は同じになります。

(fold-left + 0 '(1 2 3 4 5) ⇒ 15

(fold-left cons 'z '(a b c d))
  ⇒ ((((z . a) . b) . c) . d)

(fold-left (^[a b] (cons b a)) 'z '(a b c d))
  ⇒ (a b c d z)

二つ以上のリストが渡された場合、snokは現在のknil値および 入力リストclist1 clist2 … の対応する要素を引数として 呼び出されます。

(fold-left list 'z '(a b c) '(A B C))
  ⇒ (((z a A) b B) c C)

註:多くの関数型言語で左結合および右結合の畳み込み操作が定義されていますが、 それはfold-leftfold-rightに相当します。 (例: Haskellのfoldlfoldr。) Schemeでは、まずSRFI-1によってfoldfold-rightが 左/右結合の畳み込み演算として導入され、R6RSでfold-leftが定義されました。 (但し、R6RSのfold-leftではclist1 clist2 …の 長さがすべて同じでない場合の動作は未定義ですが、Gaucheでは 最短のリストが終了した時点でそれまでの結果が返されます。)

Function: reduce f ridentity list
Function: reduce-right f ridentity list

[R7RS list] fold および fold-right の変形バージョンです。 f は二項演算子でなければなりません。 また、ridentityf の入力として許される あらゆる値 x について以下を満していなければなりません。

 (f x ridentity) ≡ x

これらの関数は実質的に foldfold-right と同じことを 行いますが、ridentityには上記の性質があるため、 fridentityには適用されません。 ridentityが使われるのはlistが空の場合だけです。

Function: filter pred list
Function: filter! pred list

[R7RS list] 手続き predlist の各要素に適用され、 pred が真を返す要素のリストが返されます。

(filter odd? '(3 1 4 5 9 2 6)) ⇒ (3 1 5 9)

filter! はその場で更新されるバージョンです。結果を生成するために list を破壊的に変更するかもしれません。

Function: filter-map f clist1 clist2 …

map と似ていますが、真になる場合の値のみが保存されます。 引数として与えられるリストの少くともひとつは有限でなければなりません。

(filter-map (lambda (x) (and (number? x) (* x x)))
            '(a 1 b 3 c 7))
  ⇒ (1 9 49)
Function: remove pred list
Function: remove! pred list

[R7RS list] 手続き predlist の各要素に適用され、 pred が偽を返す要素のリストが返されます。

(remove odd? '(3 1 4 5 9 2 6)) ⇒ (4 2 6)

remove! はその場で更新されるバージョンです。結果を生成するために list を破壊的に更新するかもしれません。

Function: find pred clist

[R7RS list] clist の各要素に対して左から右に pred を適用し、 pred が真を返す最初の要素を返します。predを満たす要素が 無い場合は#fを返します。

Function: find-tail pred clist

[R7RS list] clist の各要素に対して左から右に pred を適用し、pred が 真を返す場合、その car がその要素であるペアを返します。 predを満たす要素が無い場合は#fを返します。

Function: any pred clist1 clist2 …

[R7RS list] clist の各要素に pred を適用し、predが偽でない 値を返したら直ちにその値を返します。 predが偽でない値を返す前にリストの要素を使いきってしまったら #fが返ります。

Function: every pred clist1 clist2 …

[R7RS list] clist の各要素に pred を順に適用し、predが 偽を返した場合、直ちに偽を返します。全てのpredの適用が 偽でない値を返した場合は、最後に返された値が返されます。

Function: count pred clist1 clist2 …

[R7RS list] n をゼロから与えられたリストのうち最も短いリストの 長さまでとして、pred 手続きを与えられたリストの n 番目の要素に それぞれ適用します。 pred が真を返した数が返ります。

(count even? '(3 1 4 1 5 9 2 5 6)) ⇒ 3
(count < '(1 2 4 8) '(2 4 6 8 10 12 14 16)) ⇒ 3

引数で与えられるリストの少くともひとつは有限でなければなりません。

(count < '(3 1 4 1) (circular-list 1 10)) ⇒ 2
Function: delete x list :optional elt=
Function: delete! x list :optional elt=

[R7RS list] 以下と同等です。

  (remove (lambda (y) (elt= x y)) list)
  (remove! (lambda (y) (elt= x y)) list)

比較手続き elt= はデフォルトでは equal? です。

Function: delete-duplicates list :optional elt=
Function: delete-duplicates! list :optional elt=

[R7RS list] list から重複した要素を取り除きます。list 中に等しい要素が 複数ある場合、一番左がわにある最初のものだけが残ります。これらの 生き残った要素間の順番は最初のリストの順番が保存されます。 比較手続き elt= のデフォルト値は、equal? です。


6.6.7 他のリスト手続き

Function: append list …

[R7RS base] 渡されたリストの要素を繋げたリストを返します。最後の引数の部分以外は新しいセルがアロケート されて使われます。最後の引数は正規のリストである必要がありません。その場合、結果は正規でない リストとなります。

Function: append! list …

[R7RS list] 渡されたリストの要素を繋げたリストを返します。最後の引数以外のリストのセルは、結果を 作成するために再利用されるかもしれません。 最後の引数は正規のリストである必要はありません。

Function: concatenate list-of-lists
Function: concatenate! list-of-lists!

[R7RS list] それぞれ、(apply append list-of-lists) および (apply append! list-of-lists) と同等ですが、 applyのオーバヘッドが無い分、わずかに効率的です。

Function: reverse list :optional (tail ’())
Function: reverse! list :optional (tail ’())

[R7RS+ base] listの要素を逆順に並べたリストを返します。 reverseは常に新たにリストをアロケートしますが、 reverse!は結果を作るためにlistのセルを再利用するかもしれません。 但し、listの先頭のセルがreverse!の結果でも先頭になるとは 限らないので、listが変更されることを当てにするのではなく、 reverse!の戻り値を利用する必要があります。

省略可能引数tailが与えられた場合、それは結果のリストの末尾に 付け足されます (tail部分はコピーされません)。 これは、既にある結果のリストに新たに作った結果を付け足すイディオムで 便利です。

(reverse '(1 2 3 4 5)) ⇒ (5 4 3 2 1)
(reverse '(1 2 3) '(a b)) ⇒ (3 2 1 a b)

tail引数はGauche独自の拡張で、 伝統的なSchemeのreverseにはありませんが、 次の対応を考えると、そう不自然なことではありません。

(reverse xs)      ≡ (fold cons xs '())
(reverse xs tail) ≡ (fold cons xs tail)

ポータブルなコードでは2引数のreverse/reverse!のかわりに 以下のappend-reverse/append-reverse!が使えます。

Function: append-reverse rev-head tail
Function: append-reverse! rev-head tail

[R7RS list] 2引数のreverseおよびreverse!と同等です。 SRFI-1 (R7RS (scheme list)) との互換のために用意されています。

Function: memq obj list
Function: memv obj list
Function: member obj list :optional obj=

[R7RS base] listからobjを探します。もしlistn番目の要素が objと同一ならば、(list-tail list n)を返します。 memqは同一性の判定にeq?を、memveqv?を、 memberequal?を使います。 objlist中に見つからなければ#fが返されます。

memberに省略可能引数obj=が与えられた場合は、 その手続きがequal?のかわりにobjと要素を比較するのに使われます。

(memq 'a '(a b c))          ⇒ (a b c)
(memq 'b '(a b c))          ⇒  (b c)
(memq 'a '(b c d))          ⇒ #f
(memq (list 'a) '(b (a) c)) ⇒ #f
(memv 101 '(100 101 102))   ⇒ (101 102)

6.6.8 連想リスト

連想リストはペアのリストです。各ペアのcarがキー、cdrがそのキーに 結びつけられた値です。これはマッピングを表現する最も初期のデータ構造でした。

値のルックアップにO(N)を要するのであまり大きなデータを扱うには向いていませんが、 不変データ構造を容易に実現できる (同じキーを持つエントリを追加すると既存のキーが「シャドウ」 されるため、既存のデータ構造を破壊することなく内容の変更が可能) ので、今でも広く使われています。

基本コンストラクタとアクセサ

Function: acons obj1 obj2 obj3

(cons (cons obj1 obj2) obj3) を返します。 連想リストの先頭に新しいエントリを加えるのに便利です。

(この手続きはSRFI-1 (R7RS (scheme list))ではalist-consという名前で定義されています。 scheme.list - R7RSリスト参照)。

(acons 'a 'b '((c . d))) ⇒ ((a . b) (c . d))
Function: assq obj list
Function: assv obj list
Function: assoc obj list :optional key=

[R7RS base] listの各要素はペアでなければなりません (Gaucheはlist中のペアでない要素を単に無視しますが、 他のR7RS処理系はエラーを投げるかもしれないので、 ポータブルなコードを書いている時は注意してください)。 これらの手続きは、listの要素であるペアのうち、そのcarが objと一致するペアを左から探して行きます。もし見付かればそのペアが、 見付からなければ#fが返されます。 assqは比較関数にeq?を、assveqv?を、 assocequal?をそれぞれ用います。

assocの省略可能引数が与えられた場合は、 その手続きがequal?のかわりに、 objと各キーとの同一性判定に使われます。

Function: rassoc val alist :optional val=
Function: rassq val alist
Function: rassv val alist

与えられるvalalistのそれぞれの要素で、carの代わりに cdrにマッチするような逆になった連想リストです。 両方向の連想リストと理解すると簡単です。 rassocは、そのデフォルトがequal?である、オプションの 比較関数を取ります。rassqeq?rassveqv?を 使います。

進んだ操作

Function: alist-copy alist

[R7RS list] alist の新しい複製を返します。alist の背骨の部分、 およびキーと値を指す各セルは複製されます。

(define a (list (cons 'a 'b) (cons 'c 'd)))
a ⇒ ((a . b) (c . d))

(define b (alist-copy a))
b ⇒ ((a . b) (c . d))

(set-cdr! (car a) 'z)
a ⇒ ((a . z) (c . d))
b ⇒ ((a . b) (c . d))
Function: alist-ref alist key :optional key= default

他の*-ref手続きと対称的な方法で、連想リストalistからkeyを 探します。キーはkey=で比較され、省略時はequal?が使われます。

これは、一般的な連想リストアクセスのパターンを提供します。

(alist-ref alist key default eq-fn)
  ≡
  (cond [(assoc key alist eq-fn) => cdr]
        [else default])))

defaultが省略されると、#fが使われます。

註: この手続きは、旧式のassoc-refassq-refassv-refを 置き換えるものです。これらの手続きは、assoc等と似た名を持つのに 引数順が異なり、また新しい連想リスト操作はalist-をプレフィクスに持つという 原則からも外れていることから、非推奨となりました。 assoc-refを使っているコードをalist-refに書き換える際には、 省略可能引数の順序が異なっていることに注意してください。これは、alist-ref で複数のバリエーションをカバーしやすくするためです。 例えば(assq-ref alist key)(alist-ref alist key eq?)で 置き換えられます。

Function: alist-key alist val :optional val= default

値がvalに一致するようなキーを連想リストalistから探して返します。 alist-keyalist-refの関係は、 assocに対するrassocの関係に相当します。 値の等価性はval=で比較されます。デフォルトはequal?です。 valに一致する値を持つエントリが無かった場合はdefaultが 返されます。そのデフォルトは#fです。

(alist-key alist val val= default)
  ≡
  (cond ((rassoc val alist val=) => car)
        (else default))

註: この手続きは旧式のrassoc-refrassq-refrassv-refを 置き換えるものです。省略可能引数の順序がrassoc-refと異なっていることに 注意してください。

Function: alist-set! alist key val :optional key=

alist(key . val)のペアが追加された連想リストを返します。 alistがすでにkeyをキーとする要素を持っている場合、 その要素のcdrは破壊的にvalに変更されます。 alistkeyをキーとする要素を持っていない場合は、 新しいペアが作成され、alistの一番前に追加されます。 したがって、key-valペアが追加されたことを保証するために その戻り値を使うべきです。

キーはkey=によって比較されます。デフォルトはequal?です。

元のalistを破壊したくない場合は、下のalist-adjoinを使ってください。

Function: alist-adjoin alist key val :optional key=

alist-set!の非破壊的バージョンです。

alistkeyをキーとするエントリを持っていた場合、その値をvalに 変えた新たな連想リストを作って返します。alist中のエントリの順序は保存されます。 もしalist中にkeyをキーとするエントリが無い場合は、 (acons key val alist)が返されます。

元のalistは変更されません。但し、返された連想リストはalistと末尾部分を 共有するかもしれません。

key=引数は2引数を取る手続きで、キーの比較に使われます。 省略された場合はequal?が使われます。

Function: alist-delete key alist :optional key=
Function: alist-delete! key alist :optional key=

[R7RS list] alist から keyと同じキーをもつすべてのセルを削除します。 比較は key= で行います。これのデフォルト値は equal? です。

その場で更新を行うバージョン alist-delete! は元の alist を変更してしまうことがあります。

引数順についての注意: 残念なことに、この手続きはalistを最初の引数に取るという 原則から外れています。この手続きはSRFI-1で定義され、それはassoc の引数順に合わせました。

Function: alist-merge [key=] reducer alist1 alist2 …

alist1 alist2 … に含まれている全てのキーを含むような 新たな連想リストを返します。同じキーを持つエントリが複数の入力連想リストに 含まれていた場合は、その値はreducerを用いて次のとおり計算されます: 入力に含まれる同じキーを持つエントリの値が順にxyzであったとすると、 結果の連想リストの値は (reducer x (reducer y z)

(alist-merge + '((a . 1) (b . 2)) '((c . 3) (a . 4)) '((b . 5)))
 ⇒ ((a . 5) (b . 7) (c . 3))
(alist-merge string=? append
  '(("a" 1) ("b" 2) ("c" 3))
  '(("b" 4) ("d" 5))
  '(("c" 6)))
 ⇒ (("a" 1) ("b" 2 4) ("c" 3 6) ("d" 5))

結果のエントリの順序は、重複キーのエントリが最初のエントリにマージされていること以外は 入力の連想リストの順序を保存します。

註: この手続きは、それぞれのalist内には重複するキーのエントリが無いことを仮定しています。 ひとつのalist内で同じキーを持つエントリを集めるには、 gauche.collectiongroup-collection->alistが使えます (gauche.collection - コレクションフレームワーク参照)。

Function: alist-update-in alist keys proc :optional key= default

この手続きは、ネストした連想リストを更新するのに使えます。 alistはネストした連想リストです。 keysはキーのリストで、procは1引数を取る手続きです。 まず、keysにあるキーを順に使って、ネストした連想リストから 値が取り出され、procに渡されます。 手続きの戻り値は新しいネストした連想リストで、該当するエントリの値が procの戻り値に置き換えられたものとなります。

(assoc-update-in '((a (b . 1) (c . 2))) '(a c) (cut + <> 1))
  ⇒ ((a (b . 1) (c . 3))

エントリの順序は保存されます。元のalistは変更を受けませんが、 戻り値と部分構造を共有するかもしれません。

alistkeysで指定されるエントリを持たない場合は新たなエントリが 追加されます。新たなエントリは、キーが存在しないシーケンスの先頭に付け加えられます。

(assoc-update-in '((a (b . 1) (c . 2))) '(a d e) (^_ 99))
  ⇒ ((a (d (e . 99)) (b . 1) (c . 3)))

default引数は、キーに対応するエントリが存在しなかった場合にprocに 渡されます。省略値は#fです。

key=引数は2引数を取る手続きで、キーの比較に使われます。 省略された場合はequal?が使われます。

註: ネストした集合データ構造の一部を破壊的に更新するには、 ~のセッタが便利です(万能アクセサ参照)。 例えばハッシュテーブルのベクタのリスト、から一番奥のハッシュテーブルのエントリを 更新することが出来ます。しかし連想リストはちょっと特殊で、リストと型的に区別 できないため、~が使えません。連想リストはまた、関数的に使われることも 多いため、専用の更新手続きを用意しました。

非推奨の手続き

以下の手続きは、名前の一貫性のために非推奨となったものです。 assocのような歴史的ないくつかの手続き以外は、現在の連想リスト操作は alist-*と命名され、原則として連想リストを第一引数に取ります。

Function: assoc-ref alist key :optional default key=
Function: assq-ref alist key :optional default
Function: assv-ref alist key :optional default

Deprecated. (assoc-ref alist key default key=)(alist-ref alist key key= default). 省略可能引数の順序に注意。

(assq-ref alist key default)(alist-ref alist key eq? default).

(assv-ref alist key default)(alist-ref alist key eqv? default).

Function: rassoc-ref alist val :optioanl default val=
Function: rassq-ref alist val :optional default
Function: rassv-ref alist val :optional default

Deprecated. (rassoc-ref alist val default val=)(alist-key alist val val= default). 省略可能引数の順序に注意。

(rassq-ref alist val default)(alist-key alist val eq? default).

(rassv-ref alist key default)(alist-key alist val eqv? default).

Function: assoc-set! alist key val :optional key=
Function: assq-set! alist key val
Function: assv-set! alist key val

Deprecated. (assoc-set! alist key key=)(alist-set! alist key key=).

(assq-set! alist key)(alist-set! alist key eq?).

(assv-set! alist key)(alist-set! alist key eqv?).

Function: assoc-adjoin alist key val :optional key=

Deprecated. alist-adjoinと同じです。

Function: assoc-update-in alist keys proc :optional default key=

Deprecated. assoc-update-in alist keys proc default key=alist-update-in alist keys proc key= default

省略可能引数の順序に注意。


6.6.9 拡張ペアとペア属性

Gaucheは、拡張ペア (extended pair) と呼ばれる特別なペアを持っています。 これは通常のペアと同じように振る舞いますが、属性リストを付加することができます。 例えば、Gaucheはソースコードの位置情報を保持するのに拡張ペアを使っています。

拡張ペアのcar/cdrへのアクセスにオーバーヘッドは全くありません。 set-car!set-cdr! には少しだけオーバヘッドがあります (できるだけ副作用を避けましょう!)。 内部的に、拡張ペアは通常のペアの倍のメモリを使います。

ペア属性は外部表現には書き出されませんし、copy-listや、出力リストを 入力リストを元に新たに組み立てる多くのリスト手続きでコピーされません。 つまり、ペア属性は簡単に失われます。「あったら良いけど、必須ではない」という 補助的な情報に使うのが良いでしょう。

また、拡張ペアを使ったコードは、弱参照ハッシュテーブルでエミュレートできるとはいえ、 他のScheme処理系には簡単に移植できないことに留意してください。

Function: extended-pair? obj

objが拡張ペアなら#tを、そうでなければ#fを返します。

Function: extended-cons car cdr :optional attrs

carcdrからなる拡張ペアを作って返します。 attrs引数が与えられた場合、 それはペア属性の初期値を指定する連想リストでなければなりません。 デフォルトでは作られる拡張ペアのペア属性は空です。

Function: extended-list obj obj2 …

obj obj2 …からなるリストを作って返しますが、 最初のペアが拡張ペアになっています。二番目以降のペアは通常のペアです。

Function: pair-attributes pair

pairのペア属性を連想リストとして返します。 通常のペアを引数に渡すこともできます。その場合は常に空リストが返ります。

(pair-attributes (extended-cons 'a 'b '((c . d) (e . f))))
  ⇒ ((c . d) (e . f))

(pair-attributes (cons 'a 'b)
  ⇒ ()
Function: pair-attribute-get pair key :optional default

pairのペア属性のうち、キーkeyに結びつけられた値を返します。 keyは任意のSchemeオブジェクトで、eq?で比較されます。 キーに結びついた値がない場合、defaultが与えられていればそれが返され、 与えられていなければエラーが投げられます。

通常のペアをpairに渡すこともできます。その場合、ペア属性は空として扱われます。

Function: pair-attribute-set! pair key value

拡張ペアpairのペア属性keyに値valueをセットします。 keyvalueは任意のSchemeオブジェクトです。 pairが拡張ペアでなければエラーが投げられます。

この手続きは既存の連想リスト自体は変更せず、必要に応じてコピーします。 ペア属性は頻繁に変更される運用を想定していません。



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT