Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2007-10-21

リーダーマクロでPerlのインデックスのまね事

| 13:01 | リーダーマクロでPerlのインデックスのまね事 - わだばLisperになる を含むブックマーク はてなブックマーク - リーダーマクロでPerlのインデックスのまね事 - わだばLisperになる

今回は、Perlの$foo[1]や、$bar[i]などという、インデックス指定の構文を真似してみることにしました。

作ってみたい構文

(let ((list '(0 1 2 3)))
  [1]list)
;=> 1

最初は、ディスパッチマクロ文字が引数を取れることを利用して組んでみましたが、これだと、10進の数しか引数に取れず変数が使えないのでは面白くないのでリーダーマクロで作ることに方針変更。

試行錯誤しているうちにできたものの、なんだか良く分からないものができました。

(set-macro-character #\[
		     (lambda (stream char)
		       (declare (ignore char))
		       (cache-arg (read stream t nil t))
		       (values)))

(set-macro-character #\]
		     (lambda (stream char)
		       (declare (ignore char))
		       (list 'elt-with-cached-index (read stream t nil t))))

(let (arg)
  (defun cache-arg (n)
    (setq arg n))
  (defmacro elt-with-cached-index (lst)
    `(elt ,lst ,arg)))
[1]list

というのを単純に

(elt list 1)

に変換したかっただけなのですが、いまいちうまい展開方法が分からず、

  1. #\[は、cache-argという次の文字をキャッシュする関数を実行するが、副作用が欲しいだけで、何かが展開されると逆に不都合なので、(values)を実行し、何も痕跡を残さない。
  2. #\]は、chache-argがキャッシュした引数でnthを実行するという、elt-with-cached-indexに展開させる。
  3. cache-argからelt-with-cached-indexに引数を受け渡す手段がないので、両者をクロージャで包み変数を共有させた。
  4. nth-with-cached-indexは展開された引数が、展開される文脈中で実行される必要があるので?(うまく説明できない(;´Д`)…)マクロにした。

という流れです。特にリーダーマクロが(values)を最後に評価して消えたりするところが非常にアレな気がします。

;; 動作
(progn
  [1]'(1 2 3))
; => 2

(my i 3
  (print [(+ i 3)](srfi:iota 10)))
; => 5

(my str "123456789"
  [3]str)
; => 4

(progn 
  [(+ 3 3)]'(0 1 2 3 4 5 6 7 8 9 10))
; => 6

(map 'string (lambda (x) [x]"abcdefghij")
     '(2 3 8 0 1 2 3))
; => "cdiabcd"  

(loop :for i :below 10 
      :do (print [i]'(a b c d e f g i h j k)))
; =>
;A 
;B 
;C 
;D 
;E 
;F 
;G 
;I 
;H 
;J 
;NIL 

ちなみに

(my lst '(foo bar baz)
 [1

 (print "hello")

 ]lst)
; -> "hello"
; => BAR

のようなものの対策は施していません。

雑感

自分の期待とは裏腹に使えなさそうなものに仕上がりました。

こういう場合の定番の解決手法ってどういうものがあるんでしょう…。