読者です 読者をやめる 読者になる 読者になる

スロット番号付き cut を syntax-rules で書く

Scheme には cut という部分適用のための構文がある。 srfi-26 で定義されている。

この cut のスロットを番号で指定して、より柔軟に使えるようにしようと以前に書いたのが一昨年のことだ。

スロット番号付き cut - 主題のない日記

このとき書いたものは中途半端に slib を使っていたりして微妙に使い勝手が悪い。 そして「原理的には syntax-rules でも出来そうだ」という自分の言葉を実証すべく、今回は syntax-rules での記述を試みた。

とは言うものの、残念ながら R5RS の範囲から外れている。 Gauche では動かない。 詳細は後述するが、試すには R6RS 処理系を用意して頂きたい。

(define-syntax %%%cutn
  (syntax-rules (<...>)
    ((_ args () as ...)
     (lambda () args))
    ((_ (args ... <...>) (ss ... s) as ...)
     (letrec-syntax ((ex (syntax-rules (as ...)
                           ((_ as)
                            (lambda(ss ... s . <...>) (apply args ... <...>)))
                           ...
                           ((_ _)
                            (%%%cutn (args ... <...>) (ss ...) as ...)))))
       (ex s)))
    ((_ args (ss ... s) as ...)
     (letrec-syntax ((ex (syntax-rules (as ...)
                           ((_ as)
                            (lambda(ss ... s) args))
                           ...
                           ((_ _)
                            (%%%cutn args (ss ...) as ...)))))
       (ex s)))))

(define-syntax %%cutn
  (syntax-rules ()
    ((_ args (r ...) (ss ...))
     (%%%cutn args (ss ...) r ...))
    ((_ args (r ...) (ss ...) a as ...)
     (letrec-syntax ((ex (syntax-rules (ss ...)
                           ((_ ss)
                            (%%cutn args (r ... a) (ss ...) as ...))
                           ...
                           ((_ _)
                            (%%cutn args (r ...) (ss ...) as ...)))))
       (ex a)))))

(define-syntax %cutn
  (syntax-rules ()
    ((_ (as ...) (ss ...))
     (%%cutn (as ...) () (ss ...) as ...))
    ((_ (as ...) (ss ...) arg args ...)
     (letrec-syntax ((ex (syntax-rules (ss ...)
                           ((_ ss)
                            (%cutn (as ... ss) (ss ...) args ...))
                           ...
                           ((_ _) (%cutn (as ... arg) (ss ...) args ...))
                           )))
       (ex arg)))))

(define-syntax cutn
  (syntax-rules ()
    ((_ args ...)
     (%cutn () (<0> <1> <2> <3> <4> <5> <6> <7> <8> <9>) args ...))))

簡単なテストケースを用意した。

((cutn list <0> <2> <1>) 1 3 2)
;; => (1 2 3)

((cutn list <0> <1> <2> <...>) 1 2 3 4 5 6 7)
;; => (1 2 3 4 5 6 7)

(let ((f 'c))
  ((cutn list <0> <2> f) 'a 'dummy 'b))
;; => (a b c)

さて、上の実装例で R5RS から外れている部分はふたつある。 まずひとつめはパターンだ。 ellipsis の後にパターン変数が続く形は R5RS では未定義となる。 以下の例は R5RS では未定義だが R6RS では問題ないはずだ。

(define-syntax last-item
  (syntax-rules ()
    ((_ a ... b)
     b)))

(last-item 'a 'b 'c 'd)

そしてふたつめはパターン部のプレースホルダだ。 R5RS においてはパターン部に書かれたアンダースコアは単なるパターン変数だが、 R6RS ではプレースホルダとして機能するので複数回表れても問題ない。 例示する。

(define-syntax const3
  (syntax-rules ()
    ((_ x _ _) x)))

(const3 'a 'b 'c)

今回書いたスロット番号付き cut についてこれらの制約を回避することはおそらく可能だが、面倒になったので将来の自分、あるいは読者への課題として残しておくことにする。 と言うより、探せばどこかにありそうな気もする。

Document ID: d4f63758687f9272e2d187f1fd4dbfe6