[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
6.16.1 手続き | ||
6.16.2 適用可能なオブジェクト | ||
6.16.3 継続 | ||
6.16.4 多値 | ||
6.16.5 遅延評価 |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[R5RS]
objが手続きなら#t
を、そうでなければ#f
を返します。
[R5RS]
(arg1 … . args)
を引数として手続きprocを呼びます。
最後の引数argsは正規のリストでなければなりません。 procが返す
値をそのまま返します。
(apply list 'a 'b '(c d e)) ⇒ (a b c d e) (apply + 1 2 '(3 4 5)) ⇒ 15 |
6.16.1.1 マッピング | ||
6.16.1.2 コンビネータ | ||
6.16.1.3 省略可能引数のパージング | ||
6.16.1.4 手続きのアリティ |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[R5RS+] 与えられたリストの各要素に対してprocを適用し、その結果をリストにして 返します。R5RSでは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
がリスト以外のコレクション型に対しても動作するようになります。
[R5RS]
手続きprocをリストの各エレメントに対して順に適用します。
procの結果は捨てられます。for-each
の戻り値は定義されていません。
複数のリストが与えられた場合、一番短いリストが終了した時点でfor-each
は終了します。
gauche.collection
モジュール(gauche.collection
- コレクションフレームワーク参照)
を使うと、for-each
がリスト以外のコレクション型に対しても動作するようになります。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Gaucheには、combinatory programmingに使えるいくつかの基本手続きがあります。
部分適用。手続きを返します。その手続きが引数m …を伴って
呼ばれた場合、それは(proc arg … m …)
と等価になります。
(define add3 (pa$ + 3)) (add3 4) ⇒ 7 (map (pa$ * 2) '(1 2 3)) ⇒ (2 4 6) |
SRFI-26で定義されているマクロcut
とcute
も似たような抽象化の
方法を提供しますが、pa$
より多少柔軟性が高く、その分やや冗長です。
手続きを作るを参照して下さい。
apply
, map
とfor-each
の部分適用版です。
(define map2* (map$ (pa$ * 2))) (map2* '(1 2 3)) ⇒ (2 4 6) |
SRFI-1(srfi-1
- List library参照)の手続に対応する部分適用版手続。
複数の手続きを結合します。引数は全て手続きでなければなりません。
2つの引数が渡された時、(compose f g)
は次の式と等価です。
(lambda args (call-with-values (lambda () (apply g args)) f)) |
2つ以上の引数が渡された場合は、次のように結合されます。
(compose f g h ...) ≡ (compose (compose f g) h ...) |
いくつか例を示します。
(define not-zero? (compose not zero?)) (not-zero? 3) ⇒ #t (not-zero? 0) ⇒ #f (define dot-product (compose (apply$ +) (map$ *))) (dot-product '(1 2 3) '(4 5 6)) ⇒ 32 |
境界のケース:ひとつだけ引数が渡された場合は、その引数がそのまま返されます。 引数が全く渡されなかった場合は手続きvaluesが返されます。
述語predの意味を逆にした手続きを返します。すなわち、predが真を 返すような引数にたいして偽を返す、またその逆も同様であるような手続きです。
(map (complement even?) '(1 2 3)) ⇒ '(#t #f #t) (map (complement =) '(1 2 3) '(1 1 3)) ⇒ '(#f #t #f) ((complement (lambda () #f))) ⇒ #t |
与えられた引数をそれぞれ述語predに適用する手続きを返します。
いずれかのpredが#f
でない値を返す場合、その値を返します。
全てのpredが#f
を返す場合、#f
を返します。
(define string-or-symbol? (any-pred string? symbol?)) (string-or-symbol? "abc") ⇒ #t (string-or-symbol? 'abc) ⇒ #t (string-or-symbol? 3) ⇒ #f (define <> (any-pred < >)) (<> 3 4) ⇒ #t (<> 3 3) ⇒ #f ((any-pred (cut memq <> '(a b c)) (cut memq <> '(1 2 3))) 'b) ⇒ '(b c) |
与えられた引数をそれぞれ述語predに適用する手続きを返します。
全てのpredが#f
でない値を返す場合、戻り値は最後の
predの戻り値になります。いずれかのpredが#f
を
返す場合、every-pred
はそれ以降のpredを呼び出さずに
#f
を返します。
((every-pred odd? positive?) 3) ⇒ #t ((every-pred odd? positive?) 4) ⇒ #f ((every-pred odd? positive?) -3) ⇒ #f (define safe-length (every-pred list? length)) (safe-length '(a b c)) ⇒ 3 (safe-length "aaa") ⇒ #f |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Schemeでオプショナル引数やキーワード引数を使うためには、 可変引数をリストとして取り自分で分解しなければなりません。 以下のマクロがヘルパーとして使えます。
与えられた値のリストrestargsを、var-specにしたがって 変数に束縛し、bodyを評価します。
var-specはシンボルか、そのcarがシンボルである2要素のリストの いずれかです。シンボルは束縛された変数名です。 restargsにある値は、順番にシンボルに束縛されます。 restargsにvar-specに示される数の値がない場合は、 残りのsymbolは以下に従ってデフォルト値が束縛されます。 var-specが単なるシンボルなら、デフォルト値は未定義です。 var-specがリストなら、デフォルト値はリストの2番目の要素を 評価した結果です。後者の場合、2番目の要素は十分な引数がない場合にのみ 評価されます。 束縛はvar-specの順番にしたがって行われるので、2番目の要素は 以前のvar-specのバインディングを参照するかも知れません。
2番目のフォームでは、restvarはシンボルでなければならず、 var-specに束縛された後、restargsに残っている値のリストに 束縛されます。
restargがvar-specよりも多い値を持っていてもエラーでは ありません。最初のフォームでは、余分な値は単に無視されます。
(define (proc x . args) (let-optionals* args ((a 'a) (b 'b) (c 'c)) (list x a b c))) (proc 0) ⇒ (0 a b c) (proc 0 1) ⇒ (0 1 b c) (proc 0 1 2) ⇒ (0 1 2 c) (proc 0 1 2 3) ⇒ (0 1 2 3) (define (proc2 . args) (let-optionals* args ((a 'a) . b) (list a b))) (proc2) ⇒ (a ()) (proc2 0) ⇒ (0 ()) (proc2 0 1) ⇒ (0 (1)) (proc2 0 1 2) ⇒ (0 (1 2)) (define (proc3 . args) (let-optionals* args ((a 0) (b (+ a 1)) (c (+ b 1))) (list a b c))) (proc3) ⇒ (0 1 2) (proc3 8) ⇒ (8 9 10) (proc3 8 2) ⇒ (8 2 3) (proc3 8 2 -1) ⇒ (8 2 -1) |
これはlet-optionals*の短いバージョンで、オプショナル引数が 1つしかないときに使います。オプショナル引数のリストとしてrestargsが 与えらると、このマクロはオプショナル引数が与えられていればその値を返し、 そうでなければdefaultの結果を返します。defaultはrestargsが 空リストでなければ評価されません。
(define (proc x . maybe-opt) (let ((option (get-optional maybe-opt #f))) (list x option))) (proc 0) ⇒ (0 #f) (proc 0 1) ⇒ (0 1) |
このマクロはキーワード引数のためのものです。var-specは 以下のフォームのうちのいずれかです。
(symbol expr)
restargがsymbolと同じ名前を持つキーワードを含んでいる場合、 symbolを対応する値に束縛します。そのようなキーワードがrestargに ない場合は、symbolをexprの結果に束縛します。
(symbol keyword expr)
restargがキーワードkeywordを含む場合、 symbolを対応する値に束縛します。そのようなキーワードがrestargに ない場合、symbolをexprの結果に束縛します。
デフォルト値exprは、restargにキーワードが与えられてなかった 場合にのみ評価されます。
1番目のフォームでは、var-specにないキーワード引数がrestargに 現れるのはエラーです。以前のバージョンとの互換性のために、現在は警告を 表示するだけですが、将来はエラーを通知するよう動作が変わるでしょう。 警告を表示したくない場合は次の2番目のフォームを使ってください。
2番目のフォームでは、restvarはシンボルか#f
でなければなりません。
シンボルのときは、var-specに束縛されなかったrestargsのキーワード
リストがrestvarに束縛されます。#f
のときは、それらのrestargs
のキーワードは単に無視されます。
(define (proc x . options) (let-keywords options ((a 'a) (b :beta 'b) (c 'c) . rest) (list x a b c rest))) (proc 0) ⇒ (0 a b c ()) (proc 0 :a 1) ⇒ (0 1 b c ()) (proc 0 :beta 1) ⇒ (0 a 1 c ()) (proc 0 :beta 1 :c 3 :unknown 4) ⇒ (0 a 1 3 (:unknown 4)) |
このマクロはlet-keywords
とほぼ同じですが、束縛がvar-specでの
順番に行われるところが異なります。exprは以前のvar-specにより
束縛された変数を参照できます。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
手続きのアリティを問い合わせるインターフェースです。 APIは、MzScheme (PLT Scheme)を参考にしました。
手続きprocを与え、整数、arity-at-leastオブジェクト、 整数とarity-at-leastオブジェクトからなるリストのいずれかを 返します。
整数の戻り値は、procが正確にその数の引数を取ることを表します。
arity-at-leastは、procが最低でも
引数(arity-at-least-value arity-at-least)
を取ることを
表します。リストは、異なるアリティを持つ複数の手続きがあることを
表します。
Gaucheではいつでも、既存の手続きやジェネリック関数にメソッドを追加
できるので、arity
が返す値はその手続きの現在の状態を示すに
過ぎません。その手続きやジェネリック関数に新しいメソッドが追加
されると、それも変更されます。
(arity cons) ⇒ 2 (arity list) ⇒ #<arity-at-least 0> (arity make) ⇒ (#<arity-at-least 1>) |
objがarity-at-leastオブジェクトなら、真を返します。
arity-at-leastオブジェクトが表す必須引数の数を返します。
手続きprocが引数kを取れる場合、#t
を返します。
そうでなければ#f
を返します。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Gaucheでは、特別な組み込みの機構によって任意のオブジェクトを 「適用可能」にすることができます。
手続きでもジェネリックファンクションでもないオブジェクトが何らかの引数に
適用されたとき、そのオブジェクトと引数がジェネリックファンクションobject-apply
に渡されます。
この機能は、具体的な例を挙げた方が説明し易いでしょう。
例えば、次のような式を評価しようとしたとします。
("abcde" 2) |
オペレータは文字列に評価されますから、手続きでもジェネリックファンクションでも ありません。そこで、Gaucheはこの式を、あたかも次のような式が与えられた かのように解釈します。
(object-apply "abcde" 2) |
デフォルトでは、<string>
と<integer>
を引数とする
object-apply
のメソッドは定義されていないので、
この式はエラーになります。しかし、次のようなメソッドを定義すると:
(define-method object-apply ((s <string>) (i <integer>)) (string-ref s i)) |
最初の式はまるで文字列が整数に適用されたかのように動作します。
("abcde" 2) ⇒ #\c |
このメカニズムは手続きが許されるほとんどの箇所で使うことができます。
(apply "abcde" '(1)) ⇒ (#\b) (map "abcde" '(3 2 1)) ⇒ (#\d #\c #\b) |
Gauche組み込みオブジェクトのうち、<regexp>
オブジェクトと
<regmatch>
オブジェクトに対してはobject-apply
メソッドが定義されて
います。正規表現を参照して下さい。
適用可能オブジェクトを適用するフォームがset!
フォームの第一ポジションに
現れた場合、そのフォームは下に示すように展開され、このメソッドが呼ばれます。
(set! (object arg …) value) ⇒ ((setter object-apply) object arg … value) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[R5RS]
現在の継続を手続き (継続手続き) にパッケージ化して、それを引数として
procを呼び出します。procが戻ったら、その返り値がcall/cc
の
値となります。作成された継続手続きがどこかで0個または複数個の引数を伴って呼ばれたら、
あたかもcall/cc
から戻ったかのように実行が継続されます。その場合、
call/cc
は、継続手続きに与えられた引数を複数の値として返します。
ファーストクラスの継続はSchemeの最も特徴的な機能のひとつですが、それを 十分に説明するにはこの本の余白は狭すぎます。適切なドキュメントを参照してください。
Gaucheはわずかの例外を除いて、完全な継続をサポートしています。つまり継続は通常 無制限のエクステントを持ちます。しかし、継続がCコードからの「コールバック」 —SchemeがCで書かれたコードを呼び出し、それが再びSchemeコードを呼び出した場合— で作られたら、その継続のエクステントはコールバックが呼び出したCコードに戻るまでと なります。エクステントの切れた継続を呼ぼうとするとGaucheはエラーを報告します。 これは根本的な制限であり、おそらく解決されないでしょう。
なお、コールバックコードから有効な継続を呼ぶことは常に可能です。また、
高階関数を使うmap
やfor-each
、apply
といった手続きは
Cからのコールバックを使っておらず、この制限の影響を受けません。
おそらく、そのようなコールバック内で無制限のエクステントを持つ継続を作る必要というのは あまり無いでしょう。Gauche組み込みの機能では、以下のようなコードがCからのコールバックで 呼び出されます。さらに、外部のCライブラリを使った場合、例えばGUIツールキットからの コールバックなどはこの制限を受けるでしょう。
write
、display
、format
から呼び出される
write-object
メソッド (出力参照)。
gauche.vport
- 仮想ポート参照)。
このマクロは次のように展開されます :
(call/cc (lambda (var) body …))
.
APIはPLT Schemeから取りました。
[R5RS]
before、thunkおよびafter は引数を取らない手続きです。
dynamic-wind
はまずbeforeを呼び出し、続いてthunkを呼び出し、
続いてafterを呼び出します。そしてthunkが返した値を返します。
もしdynamic-wind
のダイナミックスコープの外で捕捉された継続が
thunkの中で呼ばれることにより制御がthunkから飛び出した場合、
(thunkの中でエラーが起こった場合などが考えられます)、
afterが呼ばれます。
もし、thunkの中で捕捉された継続がdynamic-windのダイナミックスコープの 外で呼ばれることにより制御がthunkの中へ飛び込んだ場合、 beforeが呼ばれます。
(letrec ((paths '()) (c #f) (add (lambda (s) (push! paths s)))) (dynamic-wind (lambda () (add 'connect)) (lambda () (add (call/cc (lambda (c0) (set! c c0) 'talk1)))) (lambda () (add 'disconnect))) (if (< (length paths) 4) (c 'talk2) (reverse paths))) ⇒ (connect talk1 disconnect connect talk2 disconnect) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[R5RS]
obj … を多値として返します。
呼び出し側は、組み込み構文の receive
(変数束縛参照)か、
下に説明するR5RSの手続きcall-with-values
を使って多値を受け取ることが
できます。
srfi-11
- Let-valuesも参照してください。
(values 1 2) ⇒ 1 and 2
|
[R5RS] 手続きproducerを引数無しで呼びます。そして、それが返した値 を引数としてconsumerを呼びます。consumerが返す値を 返します。
(call-with-values (lambda () (values 1 2)) cons) ⇒ (1 . 2) |
mv-exprが返す多値のk-番目の値を返します。概念としては、 以下のコードと同じです。
(call-with-values (lambda () mv-expr) (lambda r (list-ref r k))) |
このマクロは k がゼロであるような典型的な場合にはより単純な形へと 展開されます。
Common Lisp の nth-value
に似ていますが、引数の順が逆になっています。
Scheme の他の*-ref
手続きと合わせるためです。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
GaucheではSRFI-45による拡張された遅延評価機構を用意しています。R5RSの
ふたつのプリミティブではなく、lazy
、delay
、force
の3つのプリミティブがあります。
伝統的なdelay
およびforce
だけを使ったのでは末尾再帰的なア
ルゴリズムとの相性がよくないことがわかっています。末尾再帰的なアルゴリ
ズムの本体が反復的に表現できるにもかかわらず、メモリを際限なく要求して
しまいます。詳しい説明はSRFI-45のドキュメントを見てください。ここでは3
つのプリミティブの使い方を説明します。
[SRFI-45][R5RS]
これらの形式はexpressionの評価を遅延するプロミスを生成し
ます。Expression はこのプロミスがforce
にわたったときに評
価されます。
expression自身がプロミスを評価するようになっている場合には、
lazy
を使うべきです。そうでなければ、delay
を使うべきです。
型で考えればその違いは明白でしょう。
lazy : Promise a -> Promise a delay : a -> Promise a |
Schemeでは静的な型付けをしないので、この使い分けを強制することができま
せん。文脈にしたがってプログラマが適切に選択する必要があります。一般的
にはlazy
は遅延アルゴリズムを表現している関数本体全体を囲む場合
にのみ出現します。
lazy
の実用的な使用例についてはutil.stream
(util.stream
- ストリームライブラリ)の実装をチェックするといいでしょう。
[R5RS] もし、promiseがプロミスでなければ、それをそのまま返します。
そうではない場合で、もしpromiseの値がまだ計算されていない場合には、
force
はpromiseが内包している式を評価し、その結果を返します。
いったん、promiseの値が計算されると、その値はメモ化され、あとで
再びforce
されても、再計算がおこなわれることはありません。
objがプロミスオブジェクトである場合に
#t
を返します。
以下の例は遅延リストによるFibonacci数です。リストfib
は
fib
自身に1要素シフトしてfib
を足し合せることで計算されま
す。直前の要素をキャッシュとして使っているので、n番目のFibonacci数の計
算はO(n)ですみます。
(define (lcar lis) ;; lazy car (car (force lis))) (define (lcdr lis) ;; lazy cdr (cdr (force lis))) (define (ltake lis n) ;; lazy take (if (<= n 0) '() (cons (lcar lis) (ltake (lcdr lis) (- n 1))))) (define (lmap proc l1 l2) ;; lazy map (if (null? l1) '() (cons (proc (lcar l1) (lcar l2)) (delay (lmap proc (lcdr l1) (lcdr l2)))))) ;; lazy list of fibonacci numbers (define fibs (list* 1 1 (delay (lmap + fibs (cdr fibs))))) ;; take a look (ltake fibs 20) ⇒ (1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765) |
これはエレガントな例ですが、n番目のFibonacci数を求めるのにO(n)の空間が
必要になるということに注意してください。これは、fibs
の末尾にあ
るdelay
式がfibs
リストの先頭を掴んだまま離さないからです。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] |
This document was generated by Shiro Kawai on November, 22 2009 using texi2html 1.78.