Church 数を書いてみた

Emacs Lisp で書いてみる.
とりあえず,Hello World から.

; -*- mode: emacs-lisp; coding: utf-8-unix; -*-
; hello.el
(princ "Hello, world!\n")
$ emacs -script hello.el
$ emacs -batch -l hello.el # for emacs 21 or before

ちょっと調べたら,lexcal-let*1 を使わないとアレみたい.

d:id:Ehren:20091019:1255976209 さんのを真似して,テストも書いてみる.

;; -*- mode: emacs-lisp; coding: utf-8-unix; -*-
;; church.el

(require 'cl)

(setq zero
      ;; church number
      (lambda (f)
        (lexical-let ((f f))
          (lambda (x)
            (lexical-let ((x x))
              x)))))

(setq add1
      ;; church number - (+ 1 x)
      (lambda (n)
        (lexical-let ((n n))
          (lambda (f)
            (lexical-let ((f f))
              (lambda (x)
                (lexical-let ((x x))
                  (funcall f (funcall (funcall n f) x)))))))))

(setq plus
      ;; church number - (+ x y)
      (lambda (m n)
        (lexical-let ((m m)
                      (n n))
          (lambda (f)
            (lexical-let ((f f))
              (lambda (x)
                (lexical-let ((x x))
                  (funcall (funcall m f) (funcall (funcall n f) x)))))))))

(setq church-to-int
      (lambda (n)
        (lexical-let ((n n)
                      (inc
                       (lambda (n)
                         (lexical-let ((n n))
                           (+ n 1)))))
          (funcall (funcall n inc) 0))))

(setq print-church
      (lambda (n)
        (lexical-let ((n n))
          (princ (concat (int-to-string (funcall church-to-int n)) "\n")))))

;; (let* ((one
;;         (funcall add1 zero))
;;        (two
;;         (funcall add1 one)))
;;   (funcall print-church zero)
;;   (funcall print-church one)
;;   (funcall print-church two)
;;   (funcall print-church (funcall plus one two)) ; (+ 1 2) => 3
;;   (funcall print-church (funcall plus (funcall plus one two) two)) ; (+ (+ 1 2) 2) => 5
;;   )

(provide 'church)
;; -*- mode: emacs-lisp; coding: utf-8-unix; -*-
;; test-church.el

(require 'el-expectations)
(require 'church)

(setq one (funcall add1 zero))
(setq two (funcall add1 one))
(setq three (funcall plus one two)) ; (+ 1 2) => 3
(setq five (funcall plus (funcall plus one two) two)) ; (+ (+ 1 2) 2) => 5

(expectations

 (desc "zero")
 (expect 0
         (funcall church-to-int zero))

 (desc "one")
 (expect 1
         (funcall church-to-int one))

 (desc "two")
 (expect 2
         (funcall church-to-int two))

 (desc "three")
 (expect 3
         (funcall church-to-int three))

 (desc "five")
 (expect 5
         (funcall church-to-int five))

 )
$ emacs-app -q --no-site-file --batch -L . -l el-expectations -f batch-expectations aaa test-church.el; cat aaa
Executing expectations in nil...
5 expectations, 0 failures, 0 errors
Expectations finished at Sun Nov  8 16:35:33 2009
1  :+++++ New expectations +++++
2  :zero
3  :OK
4  :one
5  :OK
6  :two
7  :OK
8  :three
9  :OK
10 :five
11 :OK

5 expectations, 0 failures, 0 errors
Expectations finished at Sun Nov  8 16:35:33 2009

ちょっとfuncall がウザイけど.まぁ.読めばわかるよね.
スコープを作るところに lexcal-let が入っているだけ.
churcl.el のコメントアウトしてある部分を使えば,
emacs -script church.el で確認することもできます.

これで,みんなも Emacs-Lispスクリプトが書けるようになったよね.

追記
後から思ったけど,Emacs-Lisp 的には prefix 付けるべきだよな.
最初 lexical-let で囲んでたからそのまま書いちゃった.

参考

*1:Emacs-Lisp はデフォルトダイナミックスコープ(全部外部変数みたいなもの)なので,lexcal-let はレキシカルスコープを導入するマクロ.(require 'cl) が必要