Source of Nothingness - マクロ

* Menu

About | Profile | まとめ | オリジナル | xyzzy | あんてな | | buzz

* 2010-01-20


diff --git a/package.lisp b/package.lisp
new file mode 100644
index 0000000..3667c7a
--- /dev/null
+++ b/package.lisp
@@ -0,0 +1,13 @@
+;; -*- Mode: Lisp; Syntax: Common-Lisp -*-
+
+;;; Package Management
+(in-package :cl-user)
+
+(defpackage :hige
+  (:use :cl)
+  #+ABCL (:shadow :y-or-n-p)
+  (:export #:pin
+           #:pon
+           #:pun
+           #:pan
+           #:pen))
diff --git a/scheme_baton.asd b/scheme_baton.asd
new file mode 100644
index 0000000..b2d9ce6
--- /dev/null
+++ b/scheme_baton.asd
@@ -0,0 +1,4 @@
+(defsystem :scheme_baton
+  :serial t
+  :components ((:file "package") (:file "scheme_baton"))
+  )
diff --git a/scheme_baton.lisp b/scheme_baton.lisp
index 27713dc..fc50ad7 100644
--- a/scheme_baton.lisp
+++ b/scheme_baton.lisp
@@ -53,6 +53,7 @@
 ;;   8. masatoi (http://d.hatena.ne.jp/masatoi/): n択問題(hige:pen)を実装.英単語から意味を問うのと意味から英単語を問うのと選べる.named-let使いまくり.
 ;;   9. cranebird (http://d.hatena.ne.jp/cranebird/): CL と言えばマルチパラダイムなので CLOS を。with-系のマクロを追加。
 ;;  10. smeghead (http://d.hatena.ne.jp/smeghead/): 単語のスコアを導入した。問題の単語表示時にスコアを表示するようにした。単語の一覧表示(hige:pun)にキーワード引数 score-thresholdを追加。
+;;  11. NANRI (http://raido.sakura.ne.jp/southly/rn/): デバッグとちょっとした整理。あと (asdf:oos 'asdf:load-op :scheme_baton) でロードできるようにした。
 ;;
 ;; =================================================================================================================================================
 ;;                            これより下がコードとその説明 - 変更・削除歓迎
@@ -79,18 +80,6 @@
 ;; ■辞書ファイルの例
 ;;   http://gist.github.com/273424
 
-;;; Package Management
-(in-package :cl-user)
-
-(defpackage :hige
-  (:use :cl)
-  #+ABCL (:shadow :y-or-n-p)
-  (:export #:pin
-           #:pon
-           #:pun
-           #:pan
-           #:pen))
-
 (in-package :hige)
 
 ;;quek-san's  http://read-eval-print.blogspot.com/2009/04/abcl-java.html without cl-ppcre
@@ -177,25 +166,25 @@
        (format *debug-io* "done~%"))
      *dict*))
 
-(defmacro with-entries ((entry) &rest body)
+(defmacro do-entries ((entry) &rest body)
   `(dolist (,entry (entries-of *dict*))
      ,@body))
 
 ;;; Top-Level Functions
 (defun pin ()
   "Register new entries to the dictionary."
-  (unless *dict-file*
-    (ensure-directories-exist *dict-file*))
   (with-dict ()
-    (loop (add-entry (prompt-for-entry))
-       (if (not (y-or-n-p "Another words to register? [yn]: ")) (return)))))
+    (loop 
+      (add-entry (prompt-for-entry))
+      (unless (y-or-n-p "Another words to register? [yn]: ")
+        (return)))))
 
 (defun pon ()
   "Start self-study english vocabulary quiz."
   (with-dict ()
-    (with-entries (e)
+    (do-entries (e)
       (p "~&~A (score: ~D): " (read-aloud (entry-word e)) (entry-score e))
-      (ready?)
+      #-ABCL (ready?)
       #-ABCL (p "~&~A [Ynq]: " (entry-meaning e))
       :again
       (case (query #+ABCL (entry-meaning e))
@@ -209,12 +198,12 @@
 (defun pan ()
   "Search the word user has input from the dictionary"
   (with-dict (:read-only t)
-    (let ((word (intern (prompt-read "Word to search") :hige)))
+    (let ((word (intern (prompt-read "Word to search") #.*package*)))
       (format t "~A" (or (search-dict word) "Not found.")))))
 
 (defun pun (&key score-threshold)
-  (if (and score-threshold
-           (not (numberp score-threshold)))
+  (when (and score-threshold
+             (not (numberp score-threshold)))
     (error "pun: score-threshold must be number."))
   (setup-dict)
   (dump-dict :score-threshold score-threshold))
@@ -226,7 +215,7 @@
   (with-dict ()
     (when (> n-choice (length (entries-of *dict*))) ; 辞書の長さチェック
       (error "Dictionary size is too small .~%"))
-    (with-entries (e)
+    (do-entries (e)
       (p "~&~A (score: ~D): "
          (if meaning? (entry-meaning e) (read-aloud (entry-word e)))
          (entry-score e))
@@ -239,13 +228,15 @@
            :do (p "~A.~A  " i (if meaning? (entry-word item) (entry-meaning item))))
         (p " [1-~Aq]: " n-choice)
         (nlet itr ((query (read *query-io* nil nil)))
-          (cond ((and (numberp query) (> query 0) (> (1+ n-choice) query))
+          (cond ((and (numberp query) (< 0 query (1+ n-choice)))
                  (if (= query correct-answer)
                      (incf (entry-ok-count e))
                      (incf (entry-ng-count e))))
-                ((and (symbolp query) (string= (symbol-name query) "Q")) (return))
-                (t (p "~&Please type number of the choice or Q for quit.~%[1-3q]: ")
-                   (itr (read *query-io* nil nil)))))))))
+                ((and (symbolp query) (string= (symbol-name query) "Q")) 
+                 (return))
+                (t
+                 (p "~&Please type number of the choice or Q for quit.~%[1-3q]: ")
+                 (itr (read *query-io* nil nil)))))))))
 
 ;;; Auxiliary Functions
 (defun setup-dict (&key (fn #'sort-dict-standard) (file *dict-file*))
@@ -261,36 +252,51 @@
   (mapcar #'(lambda (e)
               (make-entry :word (entry-word e)
                           :meaning (entry-meaning e)
-                          :ok-count (or (entry-ok-count e) 0)
-                          :ng-count (or (entry-ng-count e) 0)))
+                          :ok-count (or (ignore-errors (entry-ok-count e)) 0)
+                          :ng-count (or (ignore-errors (entry-ng-count e)) 0)))
           entries))
 
+(defmacro with-dictionary-io-syntax (&body body)
+  `(with-standard-io-syntax
+       (let ((*readtable* (copy-readtable nil))
+             (*package* #.*package*) ; 単語Symbolのホームは:higeパッケージです。
+             (*read-eval* nil))
+         (setf (readtable-case *readtable*) :preserve) ; 単語Symbolは大文字小文字を区別して扱います。
+         ,@body)))
+
 (defun read-dict (file)
   "Read dictionary data from a file."
-  (let ((*readtable* (copy-readtable nil))
-        (*package* #.*package*)) ; 単語Symbolのホームは:higeパッケージです。
-    (setf (readtable-case *readtable*) :preserve) ; 単語Symbolは大文字小文字を区別して扱います。
-    (with-open-file (in file)
-      (normalize-dict
-       (loop :for word := (read in nil in) :until (eq word in)
-          :collect word)))))
+  (unless (probe-file file)
+    (return-from read-dict NIL))
+  (with-open-file (in file)
+    (with-dictionary-io-syntax
+        (normalize-dict
+         (loop :for word := (read in nil in) :until (eq word in)
+               :collect word)))))
 
 (defun save-dict (&key (file *dict-file*))
   "Save the dictionary data into a file."
-  (with-open-file (out file :direction :output :if-exists :supersede)
-    (with-standard-io-syntax
-      (dolist (word (entries-of *dict*)) (print word out)))))
+  (unless (probe-file file)
+    (ensure-directories-exist file))
+  (with-open-file (out file
+                       :direction :output
+                       :if-exists :supersede
+                       :if-does-not-exist :create)
+    (with-dictionary-io-syntax
+        (let ((*package* #.*package*))
+          (dolist (word (entries-of *dict*)) (print word out))))))
 
 (defun dump-dict (&key score-threshold)
   "Print the dictionary in CSV format."
   (let ((output (format nil "~{~{~A~^,~}~%~}"
                         (if (null score-threshold)
-                          (entries-of *dict*) ;score-thresholdが指定されない場合は全件
-                          (remove nil ;score-thresholdが指定された場合は絞り込む
-                                  (mapcar (lambda (e)
-                                            (if (<= (entry-score e) score-threshold)
-                                              e))
-                                          (entries-of *dict*)))))))
+                            (entries-of *dict*) ; score-thresholdが指定されない場合は全件
+                            (delete NIL ; score-thresholdが指定された場合は絞り込む
+                                    (mapcar (lambda (e)
+                                              (if (<= (entry-score e) score-threshold)
+                                                  e
+                                                  NIL))
+                                            (entries-of *dict*)))))))
     #-ABCL (format t "~A" output)
     #+ABCL (|showMessageDialog| |javax.swing.JOptionPane| nil output)))
 
@@ -324,7 +330,6 @@
                 (1 #\N)
                 (2 #\Q)))
 
-
 (defun prompt-read (prompt)
   #-ABCL (progn
            (p "~A: " prompt)

* 実践CLを読了

一番の収穫はCLOSについての理解が深まったこと。
xyzzyで使えないものだから、ちゃんと勉強したことが無くて:beforeとか:afterとかよく分かってなかったんだけど、やっと分かった。(とりあえず使ってみようと思うくらいには)
メソッド結合かぁ〜 よく考えたもんだなぁ〜
C++やJavaなんかのメソッドがクラスに属しているオブジェクト指向とは全くの別物だ。頭を切り替えて使わないといけない。

帰省中に付箋を付けながら読んでいたので、拾い上げておく。
(ReadMore...)

* Common Lisp : 関数とマクロとスペシャルフォームの違い

http://d.hatena.ne.jp/pgf2/20080415/1208269126
ざっくりとした違いは関数は全ての引数が評価される、マクロとスペシャルフォームはそうとは限らないという点です。
関数が評価されるときは、まず引数を前から順番に評価し、全部の引数を評価したらその値を用いて関数のフォームを評価するという風に評価順序が決まっています。
マクロやスペシャルフォームは、評価順序が一定でなかったり、全ての引数が評価されなかったりします。
例えばスペシャルフォームの「if」はTHEN節とELSE節のどちらか一方しか評価されません。
評価規則が決まっている関数の枠組みでは「if」は定義できないということになります。

マクロの特徴は展開できるということです。
展開形は macroexpandmacroexpand-1 で確認できます。

スペシャルフォームは「3.1.2.1.2.1 Special Forms」で挙げられているものです。
マクロとスペシャルフォームの関係は排他的ではないので、マクロでありかつスペシャルフォームというものがある実装もあるかもしれません。

まとめとして setq と set と setf の関係
(setq a "value") と (set (quote a) "value") はほぼ等価。(厳密には違います。xyzzyでその差が出るかどうかは不明)
(setq a "value") と (setf a "value") は等価。
なぜなら
(macroexpand '(setf a "value"))
=>(setq a "value")
だから。

* memo : xyzzy lisp FFI

いつかちゃんとまとめたい。

* 2008-03-13 :

* 2008-03-05 :

More generally, an implementation of Common Lisp has great latitude in deciding exactly when to expand macro calls within a program. For example, it is acceptable for the defun special form to expand all macro calls within its body at the time the defun form is executed and record the fully expanded body as the body of the function being defined.
で、例えば defun での関数定義時にマクロを展開することもOKと書いてありました。

* 2008-03-04 :

* 2008-02-25 :

(ReadMore...)

* lisp : 破壊とか非破壊とかの話

SANOさんとこの補足のつもり。(だけど補足になれているかどうかは不明)
整理し切れていないので箇条書きで。

* 2007-02-24

* 「入門 Common Lisp - 関数型4つの特徴とλ計算」の著者のページを見た

http://nlp.dse.ibaraki.ac.jp/~shinnou/books.html
無茶苦茶な制約があったもんだ。
「Lisp の入門書ではない」「xyzzy と関連させる」「xyzzyマクロを作る本ではない」の3つは同時には成立しないでしょう。
「Lisp の入門書ではない」「xyzzy と関連させる」だったらxyzzyマクロの話しかないし、
xyzzy と関連させる」「xyzzyマクロを作る本ではない」だったらLispの基礎の基礎をやるしかないし、
「Lisp の入門書ではない」「xyzzyマクロを作る本ではない」だったらxyzzyが絡む余地は無いよ。
なんで無理やりxyzzyを絡めようとしたのか謎。

Common Lispは関数型言語としては泥臭すぎて関数型言語の勉強には向かないと思います。
Lisp系ならSchemeを選ぶべきかと。
末尾再帰の最適化の保証が無いから再帰よりもループを使うでしょうし、
値を返さない関数もあるし、
setfが便利すぎて代入文も普通に使うんじゃないでしょうか。

* 「入門Common Lisp〜関数型4つの特徴とλ計算」感想

http://book.mycom.co.jp/book/4-8399-2081-8/4-8399-2081-8.shtml

* 素数を求める

素直じゃない再帰・マクロ(ほぼ)無し
(defun generate-primes (limit)
  (format t "~{ ~D~}~%"
          (funcall (lambda (f c n &optional (x 3) (prime (list 2)))
                     (funcall f f c n x prime))
                   (lambda (f c n x prime)
                     (if (> x n)
                         (nreverse prime)
                       (if (funcall c c x prime)
                           (funcall f f c n (+ x 2) (cons x prime))
                         (funcall f f c n (+ x 2) prime))))
                   (lambda (c x prime)
                     (if (endp prime)
                         t
                       (if (zerop (rem x (car prime)))
                           nil
                         (funcall c c x (cdr prime)))))
                   limit)))

* Scrapbook : LispUser.net : 最高にキモい Lisp コードを書いてみよう with 100 行リーダーマクロ

http://lispuser.net/memo/lisp/2006-03-30-23-58.html

一見Lispに見えないよなぁ

* clispcgi

URIのデコード・エンコードが一応完成。
感想メモ
・whileが無くてびっくり
xyzzy lispとの微妙な差が気になる
・(alphanumericp #\あ) => T ってなんだよ!?

(ReadMore...)