Guile基礎/拡張版ラムダ式(lambda*)

変更履歴

概 要

目 次

参考資料

lambda*式の構文

lmabda* 式は次の文法に沿って記述します.
(lambda* formals* body) library syntax
   formals*    ::=  ([required] [optional] [keyword] [rest]) 
   required  ::=  identifier+
   optional    ::=  #:optional param+
   keyword     ::=  #:key param* [#:allow-other-keys]
   param       ::=  identifier
                   |  (identifier expression)
   rest        ::=  #:rest identifier
                   |  . identifier
   identifier  ::=  識別子
   body        ::=  definition* expression+
   definition  ::=  定義(define形式など)
   expression  ::=  式
補足: Gule 3.0 以降,lambda* 式の本体は定義と式を交互に記述してもよいことになっています.つまり,以下に示すように,lambda* 式の本体は上記の body を複数指定してよいことになります.

(lambda* formals* body+)

参照: Guile[6.10.3 Internal definitions] の最後に次の一文があります.
Relatedly, it used to be that internal definitions had to precede all expressions in the body; this restriction was relaxed in Guile 3.0.

lambda* 式は,構文キーワードの lambda* に続けて, 仮引数(formals*)と本体(body)から構成されます. 本体(body)は lambda 式と全く同じです.

仮引数(formals*)は次のものから構成されます. それぞれの仮引数は(不要ならば)省略できます. 構文規則の角括弧は省略可能であることを示しています(仮引数の構文要素ではありません).

補足: lambda 式で記述できることは lambda* 式で記述できます. 端的にいえば,lambda 式の「lambda」を「lambda*」に置き換えることができます. 例えば,可変的な仮引数だけを指定した
(lambda* $x$ body)
という構文も使用できます. しかし,これは lambda 式を利用すればよいので,上の構文規則から省いています.

注意: 以下では,次の事項の説明を省略します.

オプション仮引数

オプション仮引数の構文を再掲します.
   optional    ::=  #:optional param+
   param       ::=  identifier
                   |  (identifier expression)
   identifier  ::=  識別子
   expression  ::=  式

#:optional はオプション仮引数を指定するための構文キーワードです.identifier は仮引数として使用する変数を表し,expression はその仮引数の既定値(省略時の値)を求めるための式を表しています.expression には,あらゆる式が指定できます.

仮引数の束縛

オプション仮引数の実引数は,手続き呼び出しの際に省略できます. 実引数を省略した場合,オプション仮引数は次のように束縛されます.

手続き呼び出し

手続き呼び出しの際のオプション仮引数と実引数との対応付けは, 必須仮引数と同様に,仮引数の位置に基づいて行います. そのため,2番目以降のオプション仮引数の実引数を指定するときには, それより前のオプション仮引数の実引数も指定しなければなりません.

具体例

以下の手続き(disp-args)は, 必須仮引数 x の値,オプション仮引数の y と z の値を表示するだけのものです.y は既定値を指定していませんが,z は既定値としてシンボル 'Z を指定しています.
;; disp-args.scm
(define disp-args
  (lambda* (x #:optional y (z 'Z))
    (display "  x= ") (display x)
    (display "  y= ") (display y)
    (display "  z= ") (display z)
    (newline)))
プログラムファイルをロードしたあと, 適当な実引数に対して上記の手続きを実行してみます.
guile> (load "disp-args.scm")
      ...... コンパイルメッセージ ......
まず,オプション仮引数に対する実引数を省略して上記の手続きを呼び出してみます. オプション仮引数の y は #f に束縛され,z は 'Z に束縛されます.
guile> (disp-args 'A)
  x= A  y= #f  z= Z
次に,y の実引数を指定して呼び出してみます.z は 'Z に束縛されます.
guile> (disp-args 'A 'B)
  x= A  y= B  z= Z
最後に z の実引数を指定して呼び出してみます. オプション仮引数と実引数は位置によって対応付けられるので,z の実引数を指定するときには y の実引数も指定しなければなりません.
guile> (disp-args 'A 'B 'C)
  x= A  y= B  z= C

必須仮引数を省略して,オプション仮引数だけからなる手続きを作ることもできます. 以下の disp-opt-args 手続きは,上記の disp-args 手続きから必須仮引数の x を省いたもののです.
;; disp-opt-args.scm
(define disp-opt-args
  (lambda* (#:optional y (z 'Z))
    (display "  y= ") (display y)
    (display "  z= ") (display z)
    (newline)))
以下の実行例の内容は,必須仮引数の実引数を指定していない点を除いて, 上の実行例と同じことをしています.
guile> (load "disp-opt-args.scm")
      ...... コンパイルメッセージ ......
guile> (disp-opt-args)
  y= #f  z= Z
guile> (disp-opt-args 'B)
  y= B  z= Z
guile> (disp-opt-args 'B 'C)
  y= B  z= C

具体例

以下の disp-values 手続きは,1引数の数値関数 f,数値の個数 num, 初期値 init,増分 diff に対して
f(init), f(init+diff), f(init+2*diff), ..., f(init+(num-1)*diff)
の値を表示します.init と diff はオプション仮引数としていて, それぞれの既定値を 0.0 と 1.0 にしています.
;; disp-values.scm
(define disp-values
  (lambda* (f num #:optional (init 0.0) (diff 1.0))
    (let loop ((k 0) (x init))
      (when (< k num)
        (format #t "f(~A) = ~A\n" x (f x))
        (loop (1+ k) (+ x diff))))))
プログラムファイルをロードしたあと, 指数関数(exp)の値を適当に表示してみます.
guile> (load "disp-values.scm")
      ...... コンパイルメッセージ ......
まず,数値の個数(num)は5個を指定し, オプション仮引数を省略して上記の手続きを呼び出してみます. この場合,初期値(init)は 0.0 に束縛され,増分(diff)は 1.0 に束縛されます.
guile> (disp-values exp 5)
f(0.0) = 1.0
f(1.0) = 2.718281828459045
f(2.0) = 7.38905609893065
f(3.0) = 20.085536923187668
f(4.0) = 54.598150033144236
次に,初期値(init)を -2.0 に指定して呼び出してみます. 増分(diff)の実引数は省略します.そのため,増分(diff)は 1.0 に束縛されます.
guile> (disp-values exp 5 -2.0)
f(-2.0) = 0.1353352832366127
f(-1.0) = 0.36787944117144233
f(0.0) = 1.0
f(1.0) = 2.718281828459045
f(2.0) = 7.38905609893065
最後に,増分(diff)を 0.5 に指定して呼び出してみます. オプション仮引数と実引数は位置によって対応付けれられるので, 増分(diff)の実引数を指定するときには初期値(init)の実引数も指定しばければいけません.そこで,今回は初期値(init)を -1.0 に指定してみます.
guile> (disp-values exp 5 -1.0 0.5)
f(-1.0) = 0.36787944117144233
f(-0.5) = 0.6065306597126334
f(0.0) = 1.0
f(0.5) = 1.6487212707001282
f(1.0) = 2.718281828459045

キーワード仮引数

キーワード仮引数の構文を再掲します.
   keyword     ::=  #:key param* [#:allow-other-keys]
   param       ::=  identifier
                   |  (identifier expression)
   identifier  ::=  識別子
   expression  ::=  式

#:key はキーワード仮引数を指定するための構文キーワードです.identifier は仮引数として使用する変数を表し,expression はその仮引数の既定値(省略時の値)を求めるための式を表しています.expression には,あらゆる式が指定できます.

仮引数の束縛

キーワード仮引数の実引数は,手続き呼び出しの際に省略できます.実引数を省略した場合,キーワード仮引数は次のように束縛されます.

手続き呼び出し

キーワード仮引数に対する実引数を指定して手続き呼び出しを行うには, キーワード仮引数の変数名をキーとして実引数を指定します. 一般的には,次のように指定します.
(proc ... #:identifier obj ... )
ここで,proc は lambda* 式によって作られた手続きを表し,identifier はキーワード仮引数の変数名を表しています.#:identifier はキーワード仮引数を特定するために,その変数名をキーにしたもので,obj はそのキーワード仮引数に対する実引数です. 要約すると,上の指定形式は, ということを表していると言えます.

順不同: 上で述べたように,キーワード仮引数と実引数は, キー(仮引数の変数名)によって対応付けられます. そのため,キーワード仮引数の実引数は順不同で指定できます. つまり,lambda* 式の中で宣言した順番を守る必要はありません.

重複指定: さらに,1つのキーワード仮引数に対して, 実引数を2つ以上指定することもできます. その場合,キーワード仮引数は最後(もっとも右)の実引数に束縛されます. 一見すると,どうでもよいように思えるのですが, 状況によっては役立つこともあるようです.

記録(2022.3.11に追加)

必須仮引数があるにも関わらず,その実引数をうっかり指定せずに, キーワード仮引数のキー/実引数を指定した場合, キーや実引数が必須仮引数を束縛してしまうことがあります. 以下はそのような実行例です.
guile> (lambda* (x y #:key z) (format #t "x=~A y=~A z=~A\n" x y z))
$1 = #<procedure 556fb7f41c00 at <unknown port>:1:0 (x y #:key z)>
guile> ($1 #:z 'A)
;;; <stdin>:2:0: warning: possibly wrong number of arguments to `#<procedure 556fb7f41c00 at <unknown port>:1:0 (x y #:key z)>'
x=#:z y=A z=#f
上記の手続き呼び出しでは,キーの #:z は x を束縛し,'A は y を束縛します.z は実引数によって束縛されずに #f に設定されます. ワーニングレベルのメッセージは表示されていますが, 処理が中断されることはありません.

そこで,lambda* 式の必須仮引数,オプション仮引数,キーワード仮引数を持つ手続きの仮引数に対する束縛をより良く理解するために,その一般論を示します. ただし,以下の説明は筆者の推測なので,たぶん大丈夫だと思っていますが, 間違っている可能性もあります.lambda* 式の仮引数の束縛について注意すべき点は, キー(#:identifier という形式のシンボル)の取り扱い方です. キーワード仮引数の有無に応じてキーの扱い方はまったく異なります.

以下では,lambda* 式によって作られた手続き(以下,proc)が必須仮引数を$n$個,オプション仮引数を0個以上,キーワード仮引数を0個以上持っていると仮定します.さらに,proc を$n+m$個のオブジェクトを指定して呼び出した場合を考えます.ここで,「実引数」と呼ばずに「オブジェクト」と呼んでいるのは, キーも仮引数を束縛する値の1つとして説明したいためです. 従って,キーもオブジェクトの1つとします.

まず,先頭から$n$個のオブジェクトは必須仮引数を束縛します. 注意すべき点は,そこにキーが入っていたとしても, それは必須仮引数を束縛する値として処理されることです. 必須の実引数が不足しているなどのエラーにして欲しい気もするのですが, エラーにはなりません. 言い換えると,先頭から$n$個のオブジェクトは, キーワード仮引数(やオプション仮引数やレスト仮引数)とはまったく無縁であると言えます.

次に,残りの$m$個のオブジェクトについては,以下の2つの場合で扱い方(特に,キーの扱い方)が異なります. 要約すると,キーワード仮引数がない場合にはキーは一般的な値として処理され, キーワード仮引数がある場合にはキーに関して厳密に検査を行う,と言えます.

「残りの$m$個のオブジェクト」に関する上記の説明を実験的に確認してみましょう. 以下の1番目の lambda* 式はキーワード仮引数がない手続き($1)を作っています. その手続き($1)をキー形式をした #:p を含むオブジェクトに対して呼び出しています. この場合,#:p は値(実引数)の1つとして処理され,y を束縛しています. なお,$2 の #t は手続きからの返り値(formatから返り値)です.

次に,2番目の lambda* 式はキーワード仮引数(p)を持つ手続き($3)を作っています.その手続き($3)を $1 の場合と同じオブジェクトに対して呼び出しています. この場合,#:p はキーワード仮引数(p)の実引数を指定するためのキーとして処理され, そのあとに続く 'B は p を束縛しています.一方,オプション仮引数の y と z は, 実引数として与えられたオブジェクトによって束縛されることはなく, いずれも #f に設定されています.

さらに,$3 をキーの形式をした #:q を含むオブジェクトに対して呼び出しています. この場合も,#:q はキーワード仮引数の実引数を指定するためのキーとして処理されます. でも,q という名前のキーワード仮引数はないのでエラーが発生しています. 赤字はエラーメッセージです.
guile> (lambda* (x #:optional y z) (format #t "x=~A y=~A z=~A\n" x y z))
$1 = #<procedure 561be8ea7dd8 at <unknown port>:1:0 (x #:optional y z)>
guile> ($1 'A #:p 'B)
x=A y=#:p z=B
$2 = #t
guile> (lambda* (x #:optional y z #:key p) (format #t "x=~A y=~A z=~A p=~A\n" x y z p))
$3 = #<procedure 561be8eb4540 at <unknown port>:3:0 (x #:optional y z #:key p)>
guile> ($3 'A #:p 'B)
x=A y=#f z=#f p=B
$4 = #t
guile> ($3 'A #:q 'B)
ice-9/boot-9.scm:1669:16: In procedure raise-exception:
Unrecognized keyword: #:q

これまで説明したことをもう一度要約すると, キーワード仮引数がない場合,キー(の形式をしたシンボル)は普通の値として処理されます.一方,キーワード仮引数がある場合,キーに関して厳密な検査を行います.これは, lambda* 式だけを眺めていれば,ごく自然なことのように思います. しかし,case-lambda* 式の動作を理解するためには大切なことのように思います. 実際,ここで説明したことを,筆者は, lambda* 式を勉強している間はまったく気にしなかったのですが,case-lambda* 式を勉強している間に気になって詳しく検討してみたのです.

具体例

以下の手続き(disp-keyword-args)は,必須仮引数の x の値,キーワード仮引数の y と z の値を表示するだけのものです.y は既定値を指定していませんが,z は既定値としてシンボル 'Z を指定しています.
;; disp-key-args.scm
(define disp-keyword-args
  (lambda* (x #:key y (z 'Z))
    (display "  x= ") (display x)
    (display "  y= ") (display y)
    (display "  z= ") (display z)
    (newline)))
プログラムファイルをロードしたあと,まず, キーワード仮引数の実引数を省略して上記の手続きを呼び出してみます. この場合,y は #f に束縛され,z は既定値の 'Z に束縛されます.
guile> (load "disp-key-args.scm")
      ...... コンパイルメッセージ ......
guile> (disp-keyword-args 'A)
  x= A  y= #f  z= Z
次に,y の実引数を指定して呼び出してみます.y の実引数は #:y というキーのあとに指定します.なお,z の実引数は省略します.そのため,z は既定値の 'Z に束縛されます.
guile> (disp-keyword-args 'A #:y 'B)
  x= A  y= B  z= Z
上とは逆に,z の実引数を指定して, y の実引数は省略して呼び出してみます.z の実引数は #:z というキーのあとに指定します. キーワード仮引数と実引数はキーによって対応付けられるので, キーワード仮引数のキー/実引数は,lambda* 式の中で宣言した位置を気にすることなく省略できます.以下の実行例では y の実引数は省略するので,y は #f に束縛されます.
guile> (disp-keyword-args 'A #:z 'C)
  x= A  y= #f  z= C
次に,y と z の両方の実引数を指定して呼び出してみます.
guile> (disp-keyword-args 'A #:y 'B #:z 'C)
  x= A  y= B  z= C
次に,キーワード仮引数の実引数の順番を逆にして呼び出してみます. キーワード仮引数と実引数はキーによって対応付けられるので, lambda* 式の中で宣言した位置(順番)を守る必要はありません.
guile> (disp-keyword-args 'A #:z 'C #:y 'B)
  x= A  y= B  z= C
最後に,同一のキーワード仮引数に対して複数の実引数を指定することもできます. そういった場合,キーワード仮引数は最後(もっとも右)に指定した実引数に束縛されます.以下の実行例は,キーワード仮引数 y に対して3つの実引数('B,'BB,'BBB)を指定しています. この場合,y は最後(もっとも右)の 'BBB に束縛されます.
guile> (disp-keyword-args 'A #:y 'B #:y 'BB #:y 'BBB)
  x= A  y= BBB  z= Z

具体例

以下の手続き(disp-values)は,オプション仮引数のところで示した同名の手続きを, オプション仮引数をキーワード仮引数に変更したものです.
;; disp-values.scm
(define disp-values
  (lambda* (f num #:key (init 0.0) (diff 1.0))
    (let loop ((k 0) (x init))
      (when (< k num)
        (format #t "f(~A) = ~A\n" x (f x))
        (loop (1+ k) (+ x diff))))))
以下の実行例は,オプション仮引数のところで示したものとまったく同じことをしています.
guile> (load "disp-values.scm")
      ...... コンパイルメッセージ ......
guile> (disp-values exp 5)
f(0.0) = 1.0
f(1.0) = 2.718281828459045
f(2.0) = 7.38905609893065
f(3.0) = 20.085536923187668
f(4.0) = 54.598150033144236
guile> (disp-values exp 5 #:init -2.0)
f(-2.0) = 0.1353352832366127
f(-1.0) = 0.36787944117144233
f(0.0) = 1.0
f(1.0) = 2.718281828459045
f(2.0) = 7.38905609893065
guile> (disp-values exp 5 #:init -1.0 #:diff 0.5)
f(-1.0) = 0.36787944117144233
f(-0.5) = 0.6065306597126334
f(0.0) = 1.0
f(0.5) = 1.6487212707001282
f(1.0) = 2.718281828459045
オプション仮引数の場合と異なり,初期値(init)の実引数は省略して, 増分(diff)の実引数だけを指定して呼び出すこともできます. 以下はそのような実行例です.初期値(init)の実引数を省略しているので, 初期値(init)は既定値の 0.0 に束縛されます.
guile> (disp-values exp 5 #:diff 0.5)
f(0.0) = 1.0
f(0.5) = 1.6487212707001282
f(1.0) = 2.718281828459045
f(1.5) = 4.4816890703380645
f(2.0) = 7.38905609893065
さらに,キーワード仮引数に対する実引数の順番を入れ替えて呼び出すこともできます.
guile> (disp-values exp 5 #:diff 0.5 #:init -1.0)
f(-1.0) = 0.36787944117144233
f(-0.5) = 0.6065306597126334
f(0.0) = 1.0
f(0.5) = 1.6487212707001282
f(1.0) = 2.718281828459045
さらに,キーワード仮引数の実引数を重複して指定することもできます. 実行例は省略します.

#:allow-other-keys

手続き呼び出しの際にキーワード仮引数の以外のキーを指定するとエラーが発生します. 以下はそのことを確認するための実行例です. この実行例は,上で示した disp-values 手続きをキーワード仮引数以外のキー(#:name)を指定して呼び出したために,「Unrecognized keyword(認識不可能なキーワード)」というエラーが発生しています.
guile> (disp-values exp 5 #:name "exp")
;;; <stdin>:9:0: warning: possibly wrong number of arguments to `disp-values'
ice-9/boot-9.scm:1669:16: In procedure raise-exception:
Unrecognized keyword: #:name
一方,手続きを定義する際に,キーワード仮引数宣言のうしろに #:allow-other-keys という構文キーワードを指定すると,キーワード仮引数以外のキーが指定できるようになります.例えば,disp-values 手続きを次のように定義し直します.
;; disp-values.scm
(define disp-values
  (lambda* (f num #:key (init 0.0) (diff 1.0) #:allow-other-keys)
    (let loop ((k 0) (x init))
      (when (< k num)
        (format #t "f(~A) = ~A\n" x (f x))
        (loop (1+ k) (+ x diff))))))
このように定義し直した手続きを再ロードして, 上と同じ手続き呼び出しを実行してみると,今度はエラーが発生しません.
guile> (load "disp-values.scm")
      ...... コンパイルメッセージ .....
guile> (disp-values exp 5 #:name "exp")
f(0.0) = 1.0
f(1.0) = 2.718281828459045
f(2.0) = 7.38905609893065
f(3.0) = 20.085536923187668
f(4.0) = 54.598150033144236

補足: 手続き呼び出しの際に指定したキーと実引数の組み合わせは, 次節で説明するレスト仮引数を束縛する実引数リストに含まれます. その実引数リストを手続き本体の中で解析することによって, それぞれのキーに対応する実引数を取り出すことができます. 従って,レスト仮引数と #:allow-other-keys を組み合わせることによって, キーワード仮引数以外のキーに対する実引数を処理することができます. ただ, Guileのモジュールライブラリを見ても(おそらく)まったく利用していないので, これがどの程度有効な機能なのかが筆者にはよく分かりません. Common Lisp の具体例を調べてもほとんど見当たらないのですが, 高階関数の関数引数にキー/実引数をそのまま渡すときに利用したり, パターンマッチングの一種として利用しているようです. Guileにも,キー/実引数をパターンマッチングの一種として処理するための let-keywords や let-keywords* といった構文があります.

レスト仮引数

レスト仮引数の構文を再掲します.
   rest        ::=  #:rest identifier
                   |  . identifier
   identifier  ::=  識別子

#:rest はレスト仮引数を指定するための構文キーワードです. identifier はレスト仮引数の変数名を表しています. 上記の構文は,レスト仮引数を指定するときには,#:rest を使って
(lambda* ( …… #:rest ideintifier) body)
と記述してもよいし,lambda 式の場合と同様に,ピリオドを使って
(lambda* ( …… . ideintifier) body)
と記述してもよいことを示しています.

仮引数の束縛

レスト仮引数は,必須仮引数とオプション仮引数の実引数を除いて, 手続き呼び出しの際に実引数として指定したすべてのオブジェクトからなるリストに束縛されます.言い換えると,必須仮引数とオプション仮引数に対応しない「残りもの」からなるリストが作られて,レスト仮引数はそのリストに束縛されます. 特に,そのリストには,キーワード仮引数に対するキー/実引数も含まれます. 「残りもの」が何もなければ空リストに束縛されます. 以下,レスト仮引数を束縛するリストのことを レスト実引数リスト と呼ぶことにします.

具体例

まず,必須仮引数とレスト仮引数だけを指定した具体例を示します.
;; rest-args.scm
(define rest-args
  (lambda* (x #:rest r)
    (display "  x = ") (display x)
    (display "  r = ") (display r)
    (newline)))
まず,プログラムファイルをロードしたあと, 必須仮引数(x)の実引数だけを指定して上の手続きを呼び出してみます. この場合(つまり「残りもの」がない場合),レスト仮引数は空リストに束縛されます.
guile> (load "rest-args.scm")
      ...... コンパイルメッセージ ......
guile> (rest-args 'A)
  x = A  r = ()
次に,「残りもの」を指定して呼び出してみます. 以下の1番目の実引数 'A は必須仮引数 x を束縛し, 「残りもの」の 'B と 'C はレスト実引数リスト '(B C) となり, レスト仮引数の r を束縛します.
guile> (load "rest-args.scm")
      ...... コンパイルメッセージ ......
guile> (rest-args 'A 'B 'C)
  x = A  r = (B C)

次に,オプション仮引数も指定した具体例を示します.
;; rest-args.scm
(define rest-args
  (lambda* (x #:optional y #:rest r)
    (display "  x = ") (display x)
    (display "  y = ") (display y)
    (display "  r = ") (display r)
    (newline)))
まず,プログラムファイルをロードしたあと, 必須仮引数(x)をとオプション仮引数(y)の実引数だけを指定して上の手続きを呼び出してみます.以下の1番目の実引数 'A は必須仮引数 x を束縛し,2番目の実引数 'B はオプション仮引数 y を束縛します.さらに,レスト仮引数(r)は空リストに束縛されます.
guile> (load "rest-args.scm")
      ...... コンパイルメッセージ ......
guile> (rest-args 'A 'B)
  x = A  y = B  r = ()
次に,「残りもの」を指定して呼び出してみます. 以下の 'C と 'D はレスト実引数リスト '(C D) となり,レスト仮引数 r を束縛します.
guile> (rest-args 'A 'B 'C 'D)
  x = A  y = B  r = (C D)

次に,キーワード仮引数も指定した具体例を示します.
;; rest-args.scm
(define rest-args-with-keys
  (lambda* (x #:optional y #:key z #:rest r)
    (display "  x = ") (display x)
    (display "  y = ") (display y)
    (display "  z = ") (display z)
    (display "  r = ") (display r)
    (newline)))
以下の1番目の実引数 'A は x を束縛し,2番目の実引数 'B は y を束縛し, キー #:z のうしろの実引数 'C は z を束縛します. さらに,必須仮引数とオプション仮引数に対応しないすべてのオブジェクト(注:キーも含む)はレスト実引数リスト '(#:z C D E) となり,レスト仮引数 r を束縛します.
guile> (load "rest-args.scm")
      ...... コンパイルメッセージ ......
guile> (rest-args-with-keys 'A 'B #:z 'C 'D 'E)
  x = A  y = B  z = C  r = (#:z C D E)
さらに,余分な実引数(注:仮引数宣言に明示的に対応しない実引数)とキーワード仮引数に対するキー/実引数は任意の順序で指定できます.以下の実行例では,キーワード仮引数 z の実引数(#:z 'C)の前に余分な実引数('P)を指定しています.
guile> (rest-args-with-keys 'A 'B 'P #:z 'C 'D 'E)
  x = A  y = B  z = C  r = (P #:z C D E)

既定値と仮引数のスコープ

既定値の評価

オプション仮引数とキーワード仮引数の既定値を求める式は, 必要がない限り評価されません.この点を簡単な実験で確かめてみましょう.
;; default-exp.scm
(define default-exp 
  (lambda* (#:optional (x (begin (display "SET x TO THE DEFAULT\n") 'X))
            #:key      (y (begin (display "SET y TO THE DEFAULT\n") 'Y)))
    (display "  x=") (display x)
    (display "  y=") (display y)
    (newline)))
上の手続きは,仮引数の x や y に既定値('X や 'Y)を設定する際にメッセージを表示します.従って,既定値を必要としないときにはメッセージも表示されないはずです. 実際,その通りになります.
guile> (load "default-exp.scm")
      ...... コンパイルメッセージ ......
guile> (default-exp)
SET x TO THE DEFAULT
SET y TO THE DEFAULT
  x=X  y=Y
guile> (default-exp 'A)
SET y TO THE DEFAULT
  x=A  y=Y
guile> (default-exp 'A #:y 'B)
  x=A  y=B
1番目の手続き呼び出しは,x と y の実引数を省略しているので既定値が必要になります. そのため両方ともメッセージが表示されます.2番目は,y の既定値だけが必要になるので,y に関するメッセージだけが表示されます.3番目は,x も y も既定値が必要ないので,両方ともメッセージは表示されません.

仮引数のスコープ

lambda* 式の仮引数の束縛は左から右に向かって行われます. ただし,例外事項として,レスト仮引数の(レスト実引数リストへの)束縛は, オプション仮引数の束縛の直後,キーワード仮引数の束縛の直前に行われます. 要約すると,仮引数の束縛は, の順に行われます. さらに,仮引数のスコープ(有効範囲)は,本体だけでなく, その仮引数の「右側」の仮引数宣言も含みます. ここで,「右側」とは上記の順序(束縛の順序)における右側のことで, 同種の仮引数の中では仮引数宣言における右側のことです. そのため,例えば,レスト仮引数のスコープは, 構文的にはキーワード仮引数の右側に宣言するものの, キーワード仮引数の宣言を含みます.

以上に述べたことから, 既定値を求める式の中で「左側」に位置する仮引数の束縛を利用することができます. ここで,「左側」とは束縛の順序における左側のことで,同種の仮引数の中では仮引数宣言における左側のことです. このことを簡単な実験で確かめてみましょう.
;; param-scope.scm
(define param-scope-test 
  (lambda* (px
            #:optional (ox 10) (oy (+ px ox))
            #:key (kx 100) (ky (+ px ox kx)) (kz rst) 
            #:rest rst
            )
    (display "  px=") (display px)
    (display "  ox=") (display ox)
    (display "  oy=") (display oy)
    (display "  kx=") (display kx)
    (display "  ky=") (display ky)
    (display "  kz=") (display kz)
    (display "  rst=") (display rst)
    (newline)))
プログラムファイルをロードしたあと, 必須仮引数(px)の実引数(1)だけを指定して上の手続きを呼び出してみます. この場合,px は1に束縛され,ox は既定値の10に束縛され,oy は既定値の11(=px+ox)に束縛されます. さらに,kx は既定値の 100 に束縛され,ky は既定値の111(=px+ox+kx)に束縛され, kz はレスト仮引数 rst の値に束縛されます.この呼び出しの場合,rst は空リストに束縛されるので,kz も空リストに束縛されます.
guile> (load "param-scope.scm")
      ...... コンパイルメッセージ ......
guile> (param-scope-test 1)
  px=1  ox=10  oy=11  kx=100  ky=111  kz=()  rst=()
次に,適当に実引数を指定して呼び出してみます.以下の実行例では, レスト仮引数 rst が '(#:kx 4 5) に束縛されるので,kz も同じリストに束縛されます.
guile> (param-scope-test 1 2 3 #:kx 4 5)
  px=1  ox=2  oy=3  kx=4  ky=7  kz=(#:kx 4 5)  rst=(#:kx 4 5)

define*式

lambda*式に呼応して,Guileは,define 形式を拡張した define* 形式を用意しています. Guileのマニュアル [6.7.4.1 lambda* and define*] は,define* 形式に関して下記の一文のみを示していて,詳しいことは何も述べていません.
Likewise, define* is syntactic sugar for defining procedures using lambda*.
色々と試してみると,define 形式で出来ることは define* 形式でもできるようです. 例えば,以下の実行例が示すように,単純な変数定義も出来ます.
guile> (define* x 10)
guile> x
$1 = 10
define 形式とdefine* 形式の違いは,手続きを定義したときに(lambda式の代わりに)lambda* 式が使われることだけだろうと思います.

具体例

オプション仮引数のところで示した disp-values 手続きは,define* 形式を使って次のように定義できます.
;; disp-values.scm
(define* (disp-values f num #:optional (init 0.0) (diff 1.0))
  (let loop ((k 0) (x init))
    (when (< k num)
      (format #t "f(~A) = ~A\n" x (f x))
      (loop (1+ k) (+ x diff)))))
guile> (load "disp-values.scm")
      ...... コンパイルメッセージ ......
guile> (disp-values exp 5 -1.0 0.5)
f(-1.0) = 0.36787944117144233
f(-0.5) = 0.6065306597126334
f(0.0) = 1.0
f(0.5) = 1.6487212707001282
f(1.0) = 2.718281828459045
(おしまい)