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

レガシーコード生産ガイド

私に教えられることなら

Schemeでクロージャのみでリストを作る

昨日のようなコードをSchemeで書いてみた。

コード

JavaScriptだと関数が返す関数に値を適用して、その結果返ってくる関数に……というコードは次のように書けるんだけど

f(g)(1)(2)(3)(h);

Schemeだと次のようにちょっと書きづらくなる。

(((((f g) 1) 2) 3) h)

なので、引数に次の引数を渡すと必ず1引数関数を返す、と仮定して畳み込んでいくchain関数を用意して、直線的に書けるようにした。

(define chain
  (lambda args
    (define (loop f fs)
      (if (null? fs)
          f
          (loop (f (car fs))
                (cdr fs))))
    (loop (car args) (cdr args))))

(畳み込みなのでreduce使えば良かったのか……)

で、こんなカンジで関数を適用していくと、9が印字される。

(chain flist  1 2 3  (mapper inc)  (reducer + 0)  print)

さらにせっかくlambdaで囲んでいるので、mapreducertakeを全て遅延評価にした。

(define (mapper f)
  (lambda (x xs)
    (lambda (next)
      ((fcons (f x)
              (if (null? xs)
                  '()
                  (lambda (g)
                    (chain xs (mapper f) g)))) next))))

(define (reducer f init)
  (define (loop acc x xs)
    (if (null? xs)
        (f acc x)
        (loop (f acc x) (chain xs fcar id) (xs fcdr))))
  (lambda (x xs)
    (lambda (next)
      (next
       (loop init x xs)))))

(define (iterator n step)
  (define (loop n)
    (lambda (f)
      ((fcons n (loop (+ n step))) f)))
  (loop n))

(define (taker n)
  (define (loop n)
    (lambda (x xs)
      (lambda (f)
        ((fcons x
                (if (> n 0)
                    (chain xs (loop (- n 1)))
                    '())) f))))
  (loop (- n 1)))

混乱の渦ってカンジのコードだ。。

一応上手くいったみたいで、次のように書くと1から10までの合計を印字する

(chain (iterator 1 1)  (taker 100)  (reducer + 0)  print)

(iterator 1 1)で無限の正の整数のリストを作り、mapしてからtakeすることもできる。

#?= (chain (iterator 1 1)
           (mapper (lambda (x) (* x x)))
           (taker 16)
           (reducer (lambda (acc x) (cons x acc)) '())
           reverse)

結果

#?="./main.scm":73:(chain (iterator 1 1) (mapper (lambda (x) (* x x))) (taker 16 ...
#?-    (1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256)

感想

  • λをコネコネするとマクロ使わなくても見た目の評価順序がいろいろ変えられて楽しい
  • 明日になったらもう読め無さそうなコードだと思ったけど、既にこれ書いてる時点で読めなくなってきた
  • 普通にメンテナンスするコード書くならマクロ書いた方がいい
  • パズルみたいで本当に楽しい
広告を非表示にする