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

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

私に教えられることなら

Common Lispで関数合成

lisp CommonLisp

年末に注文した「初めての人のためのLISP」が元旦に届いたので読み進めている。 PharoやHaskellが一段落するまで我慢しようと思っていたのだけど、チラッと目を通したらそのまま読みハマってしまった。

いつもどおり、書いてあることを打ち込んでいるうちに「いいこと思いついた」ので、関数合成をやってみることにした。

; Lispではreduceらしい。
(defun fold (x acc f)
  (cond 
    ((null x) acc)
    (t (fold (cdr x) (funcall f (car x) acc) f))))

; 省略可能引数のテスト。fromからtoまでの数列を作る。
; デフォルトでは公差1の等差数列。
(defun from-to-list (from to &optional (fn #'1+))
  (cond
    ((> from to) nil)
    (t (cons from 
             (from-to-list (funcall fn from) to fn)))))

; 関数合成
(defun compose (fn &rest fns)
  (fold fns (lambda (x) (funcall fn x))
        (lambda (f g)
          (lambda (x) (funcall f (funcall g x))))))

(defun -> (x &rest fns)
  (funcall (apply #'compose fns) x))

(defun doit ()
  (-> 10
      (lambda (x) (from-to-list 1 x))
      (lambda (x) (mapcar (lambda (i) (* i 2)) x))
      (lambda (x) (mapcar (lambda (i) (cons i (* i i))) x))))

実行結果

* (doit)
((2 . 4) (4 . 16) (6 . 36) (8 . 64) (10 . 100) (12 . 144) (14 . 196) (16 . 256) (18 . 324) (20 . 400))

本当は (-: x f g h)とすごいHaskell本みたいにやりたかったんだけど、名前の最初にハイフンはダメみたいだ。

(※追記 コメントで指摘して頂いた通り、「:」が原因だった)

できればさらに書きやすく

(-> (x 10)
    (from-to-list 1 x)
    (mapcar (lambda (i) (* i 2)) x)
    (mapcar (lambda (i) (cons (* i i))) x))

みたいに、lambdaを減らして、xに結果を束縛していきたいんだけど、リーダーマクロとかを使えばできるんだろうか?funcallの連続もマクロで置き換えたら速くなりそうだ。やってみたい。

それと->は型ってイメージついちゃってるから、別の名前が欲しいところだ。

広告を非表示にする