!kiyoka.blog.2009_06 RSSPLAIN

Related pages: !kiyoka.blog.list
1551555333333333330333555555555545555555555555554555555555555555555555555555555555555555555555055555555555555555555555555555555555555555555555555555555555505555555555555555555555555555555550555555555555555555555555555555555555555555555555555555555555555555505555555552555555555555555555555555555555555555555555555555555555555555555555555555555555055555555555555505
1

kiyoka日記。NendoSekkaの開発や、最近思うことなど

5

最新10件!kiyoka.blog   過去記事一覧!kiyoka.blog.list

5

kiyoka.blog_header 

1

このブログを書いている人: 西山 清香(kiyoka) - twitter: @kiyokaEXT

5

5

 

5

 

3

kiyoka.2009_06_30[本] ひげぽんさんに習って、Test Driven Development : By Example読んでみようかな

3

Lisp処理系を作り始めて、RSpecでテストを繰返しつつリファクタリングする習慣がついた。

3

そして、TDDの良さが体感出来てきたところ。

3
 Test Driven Development: By Example 読み終わった - ひげぽん OSとか作っちゃうかMona-EXT
3
 0321146530  Test Driven Development: By Example (Addison-Wesley Signature Series): Kent Beck: 洋書
3
 一方で、Kent Beck がどれだけの気持ちで Green を維持しようとするか、そ
3
 の間隔や感覚はどれほどのものか?頭にたくさんの事が入らないのでノートに
3
 メモを推奨している。みたいな話は後者(この本)を読まないと伝わってこない。
3

ひげぽんさんが書いて下さっている、この部分に興味がある。

3

自分がアカウントをもってるSafari bookshelfを見たら、この本が登録されていることを知ったのでちょっと読んでみるつもり。

3

 

0

comment (disabled)

3

3

 

3

 

5

kiyoka.2009_06_29[Nendo] ついにorとandを定義できた

5

私がRubyで書いているLisp方言、 Nendoの開発状況続き。

5

 

5

macroで構文をどんどん定義している。

5

ついに or と and が定義できた。ついでに let1 も。

5

先日定義したcondも使っている。すごい勢いで積み上がっていく感じが楽しいぞ。

5
 33-1210497807Redl
5
(define let1
5
  (macro (var expr body)
5
    (list 'let (list (list var expr)) body)))
4

 

5
(define or
5
  (macro lst
5
    (define (or_iter lst)
5
      (cond
5
       ((eq? 0 (length lst))
5
        false)
5
       ((eq? 1 (length lst))
5
        (let1 sym (gensym)
5
              (list 'let1 sym (car lst)
5
                    (list 'if sym sym false))))
5
       (else
5
        (let1 sym (gensym)
5
              (list 'let1 sym (car lst)
5
                    (list 'if sym sym (or_iter (cdr lst))))))))
5
    (or_iter lst)))
4

 

5
(define and
5
  (macro lst
5
    (define (and_iter lst)
5
      (cond
5
       ((eq? 0 (length lst))
5
        true)
5
       ((eq? 1 (length lst))
5
        (car lst))
5
       (else
5
        (list 'if (list 'not (list 'eq? 'false (car lst)))
5
              (and_iter (cdr lst))
5
              'false))))
5
    (and_iter lst)))
5

 

5

これが実行結果

5
bash-3.2$ ./nendo
5
nendo> (or)
5
false
5
nendo> (or 1 2 3)
5
1
5
nendo> (or false false 3)
5
3
5
nendo> (and)
5
true
5
nendo> (and true)
5
true
5
nendo> (and 1 2 3)
5
3
5
nendo> (and false 2) 
5
false
5
nendo> (and true  2)
5
2
5
nendo> (and 1 2 (print "3\n") (print "4\n") 5)
5
3
5
4
5
5
5
nendo> (and 1 2 false (print "3\n") (print "4\n") 5)
5
false
5

 

5

実は、gensym関数の実装はかなりサボっているのだ。

5

gensymで生成されるシンボルは若干ぶつかりにくいネーミングルールになっているだけ。いつかイタイ目に会うのかな。

5
nendo> (macroexpand '(or 1 2))
5
(let ((__gensym__10 1)) (if __gensym__10 __gensym__10 (let ((__gensym__11 2)) (if __gensym__11 __gensym__11 false))))
5

このへんがToy言語っぽい。何とかしたい...

5

 

0

comment (disabled)

5

5

 

5

 

5

kiyoka.2009_06_28[Nendo] defineをmacroで実装した

5

私がRubyで書いているLisp方言、 Nendoの開発状況続き。

5

 

5

lambdaとmacroの可変長引数のサポートできたので、それを利用してdefineもNendo上のmacroで実装した。

5

いままでは、defineをRubyで実装していたが、Rubyでのリスト処理が煩雑だったのだ。macroでスッキリ書けた。

5

初期化ライブラリの init.nnd の冒頭に定義した。

5
(set! define
5
      (macro (arg . body)
5
        (if (not (pair? arg))
5
            (cons 'set!
5
                  (cons arg
5
                        body))
5
            (cons 'set!
5
                  (cons (car arg)
5
                        (list
5
                         (cons 'lambda
5
                               (cons  (cdr arg)
5
                                      body))))))))
5

ここでは、使える部品が少ないので、定義は非常に冗長になってしまう。

5

quasiquoteはまだ使えないし、appendはdefineを使って定義したいのでこうならざるを得ないのかな。

5

ブートストラップとはそういうものですよ、多分。

5

 

5

ところで、昔、仕事で組込CPUのブートローダーをアセンブラでたくさん書いた事を思い出す。

5

組込CPUのブートストラップは、ROM RAMチェックが完了するまではスタック(RAM)が使えないので少ないレジスタを使いまわしてメモリチェックをしないといけない。

5

RAMチェックが完了して初めてスタックポインタを設定し、 C言語のエントリポイントである _main を呼ぶことができる。

5

_mainまで来ればC言語で書けるので一気に楽になるのだ。

5

 

5

Nendoの初期化ライブラリ init.nnd も同様にビルディングブロックが積みあがるみたいに、後になればなる程、コードが簡潔になっていくのだろう。

5

condもmacro定義したけど、この段階でもまだまだ使える部品が少なくて冗長だ。

5
(define cond
5
  (macro lst
5
    (define (caseblock elem . elseblock)
5
      (let ((condition (car elem))
5
            (body      (cdr elem)))
5
        (append
5
         (list 'if
5
               (if (eq? 'else condition)
5
                   true
5
                   condition)
5
               (cons 'begin body))
5
         (if (< 0 (length elseblock))
5
             elseblock
5
             '()))))
5
5
    (define (cond_iter lst)
5
      (if (eq? 0 (length lst))
5
          '()
5
          (if (eq? 1 (length lst))
5
              (caseblock (car lst) '())
5
              (caseblock (car lst)
5
                         (cond_iter (cdr lst))))))
5
5
    (cond_iter lst)))
5

 

5

次はこのcondを使ってor と and を定義してみよう。

5

もうそろそろ ミニLisp処理系を実装している人とブートストラップの話をしてみたいなぁ。

5

 

0

comment (disabled)

5

5

 

5

 

5

kiyoka.2009_06_24[Nendo] 可変長引数のサポート

5

私がRubyで書いているLisp方言、 Nendoの開発状況続き。

5

 

5

lambdaとmacroの可変長引数のサポートができた。

5

これで、いろんなScheme処理系みたいに初期化ライブラリがガンガン書けるぜぃ。

5

 

5
 例えば、list関数同様の関数は次の様に定義できる。
5
nendo> (define mylist (lambda lst lst))
5
#<Proc:0x00063880@(stdin):1>
5
nendo> (mylist 'a 'b 'c)
5
(a b c)
5

 

5
 1個以上の可変長引数もこの通り
5
nendo> (define firstAndRest (lambda ( first . rest )  (list first rest)))
5
#<Proc:0x00050460@(stdin):9>
5
nendo> (firstAndRest 1 2)
5
(1 (2))
5
nendo> (firstAndRest 1 2 3)
5
(1 (2 3))
5

 

5

これが動くまでにいろんなバグが見つかってなかなか本題の or と and をmacroで書く所まで行けてない。

5

そんなわけで、思ったよりもLisp処理系を作るのは簡単ではないことが分かりつつあるのだが、書いてはリファクタリングを続けることで頭の中でLispというものがどんどん整理されて行く気がする。これが悟り体験のような気がしてウレシイ。

5

実際にコードを書いてみて冗長に感じたり、直感的に場合分けが大すぎると感じたりした場合は、だいたいリファクタリングしたらゴッソリと短いコードに置き換わる。

5

nendo.rbが1000行を超えては1000行に納まるという繰返し。

5

この感覚なんだろう。

5

ぷよぷよとかテトリスの連鎖する感じ?

5
 1-1211550382Aga9
5

もうそろそろRubyで書く部分は減ってきて Nendoで書く init.nnd の行数が増えて行くのだろう。

5

quasiquoteが動く日も近いのではないかな?

5

 

0

comment (disabled)

5

5

 

5

 

5

kiyoka.2009_06_15[Nendo] 伝統的なmacroが動いた

5

ここまで長い道程だったが、macroが動いた。

5

実際にmacroを実装してみるとmacroがどういうものかやっと理解できた気がする。

5

 

5

伝統的なmacroの例

5

 

5
 引数に指定された変数をインクリメントするmacroを定義
5
nendo> (define inc (macro (x) (list 'set! x (list '+ '1 x))))
5
#<LispMacro:0x00083fcc@(stdin):1>
5

 

5
 変数aを準備
5
nendo> (define a 10)
5
10
5

 

5
 macroの展開結果を見る
5
nendo> (macroexpand1 '(inc a))
5
(set! a (+ 1 a))
5

 

5
 実際にインクリメントしてみる
5
nendo> (inc a)
5
11
5

 

5
 引数に指定した処理を2回実行するmacroを定義
5
nendo> (define twice (macro (x) (list 'begin x x)))
5
#<LispMacro:0x000720d8@(stdin):6>
5

 

5
 (inc a)をマクロで2回実行
5
nendo> (twice (inc a))
5
13
5

 

5
 macroの展開結果を見る(1回だけマクロ展開)
5
nendo> (macroexpand1 '(twice (inc a)))
5
(begin (inc a) (inc a))
5

 

5
 macroの展開結果を見る(2回のマクロ展開)
5
nendo> (macroexpand1 (macroexpand1 '(twice (inc a))))
5
(begin (set! a (+ 1 a)) (set! a (+ 1 a)))
5

 

5

さて、orとandをmacroで実装してみよう。と思ったが、可変長引数をサポートしていないので、書けないという問題に気がついた。orz.

5

やっぱり可変長引数は必須なのか...

5

 

5

 

5

macroの可変長引数サポートについて

5

その後、macroに可変長引数をサポートしようとして,

5
(define name (macro (arg1 arg2) body))
5

という形式ではゼロ個以上の可変長引数がサポートできないことが分かった... o.rz.

5

要するに、lambdaが入る部分にmacroというキーワードが入る形で考えていた。

5

そうすると、なにが問題かというと、ゼロ個以上の可変長引数を書きたい場合は、気持ち的には

5
(define name (macro (. rest) body))
5

だが、S式として成立しない。 (. rest) の部分がS式としてエラーとなる。

5

 

5

TinySchemeはmacro定義をつぎの形式で行なうが、この形式ならうまくいく。

5
(macro (name arg1 arg2) body)
5

または、

5
(macro name (lambda (arg1 arg2) body)
5

となっている。

5

 

5

この形式なら

5
(macro (name . rest) body)
5

とかけるのね。

5

つまり、defineと同じイメージでmacroキーワードを組み立てないといけないのだな。

5

 

5

COMMENTshiro

(macro rest body) じゃまずいですか? > ゼロ個以上の可変長引数

5

COMMENTkiyoka

> (macro rest body) じゃまずいですか?

おっと、今R5RSの仕様をみたら、lambdaがその形式を取るんですね。

初めて知りました。

macroも同じ形式で問題ないです。

というか、lambdaのほうもその形式をサポートしないといけないですね。

ありがとうございます。

0

comment (disabled)

5

5

 

5

 

5

kiyoka.2009_06_02[Nendo] 伝統的なmacroの実装に向けて

5

私がRubyで書いているLisp方言、 Nendoの開発状況続き。

5

 

5

macroの実装方法調査中。

5

とにかくmacro機能が無いと何も実用的な構文は何も定義できないということを今になって痛感。(遅すぎ)

5

逆にmacro機能さえ有れば速度さえ気に為なければいくらでも必要な構文が量産定義できるのだった。

2

最近、 On Lisp 4274066371の『マクロ』の章を読み返して、なには無くともquasiquoteを動かすべし、と思っているところ。

5

そんなこんなで、Rubyのライブラリを呼びだすという楽しい部分は後回しにしたい。

5

 

5

TinyScheme 1.39のソースコード(init.scm)に含まれるこのquasiquoteが動くとうれしい。というのも、こんな複雑な処理を自分で書くのは骨が折れるので割愛したい。

5

これくらい自分で書かないと行けないのかなとも思うけど。

5

 

5

しかし、これを動かそうとするだけでもapply とか eqv? とか色々足りない関数が有る。

5

しかも、condが使われているのでそのまま動かそうとするとcondをRubyで実装するしか無いのかだろうか。

5

 

5

いろいろ手は有るけど、このうちどの案にしようか。

5
quasiquoteの定義中でcondを使っている箇所をifに書きなおす(Lisperらしくないかも!)
5
condをビルトインにする(Rubyで実装する)
5
condをquasiquote無しでmacro定義する
5

動作速度にこだわらないToy言語ということで、3番が有力かなぁ。

5

 

5
;; The following quasiquote macro is due to Eric S. Tiedemann.
5
;;   Copyright 1988 by Eric S. Tiedemann; all rights reserved.
5
;;
5
;; Subsequently modified to handle vectors: D. Souflis
5
5
(macro
5
 quasiquote
5
 (lambda (l)
5
   (define (mcons f l r)
5
     (if (and (pair? r)
5
              (eq? (car r) 'quote)
5
              (eq? (car (cdr r)) (cdr f))
5
              (pair? l)
5
              (eq? (car l) 'quote)
5
              (eq? (car (cdr l)) (car f)))
5
         (if (or (procedure? f) (number? f) (string? f))
5
               f
5
               (list 'quote f))
5
         (if (eqv? l vector)
5
               (apply l (eval r))
5
               (list 'cons l r)
5
               )))
5
   (define (mappend f l r)
5
     (if (or (null? (cdr f))
5
             (and (pair? r)
5
                  (eq? (car r) 'quote)
5
                  (eq? (car (cdr r)) '())))
5
         l
5
         (list 'append l r)))
5
   (define (foo level form)
5
     (cond ((not (pair? form))
5
               (if (or (procedure? form) (number? form) (string? form))
5
                    form
5
                    (list 'quote form))
5
               )
5
           ((eq? 'quasiquote (car form))
5
            (mcons form ''quasiquote (foo (+ level 1) (cdr form))))
5
           (#t (if (zero? level)
5
                   (cond ((eq? (car form) 'unquote) (car (cdr form)))
5
                         ((eq? (car form) 'unquote-splicing)
5
                          (error "Unquote-splicing wasn't in a list:"
5
                                 form))
5
                         ((and (pair? (car form))
5
                               (eq? (car (car form)) 'unquote-splicing))
5
                          (mappend form (car (cdr (car form)))
5
                                   (foo level (cdr form))))
5
                         (#t (mcons form (foo level (car form))
5
                                         (foo level (cdr form)))))
5
                   (cond ((eq? (car form) 'unquote)
5
                          (mcons form ''unquote (foo (- level 1)
5
                                                     (cdr form))))
5
                         ((eq? (car form) 'unquote-splicing)
5
                          (mcons form ''unquote-splicing
5
                                      (foo (- level 1) (cdr form))))
5
                         (#t (mcons form (foo level (car form))
5
                                         (foo level (cdr form)))))))))
5
   (foo 0 (car (cdr l)))))
5

macroが動いたら玩具Toy言語といえどももかなり遊べる様になるな。楽しみ^_^

5

 

5

P.S.

5
 ちなみにchibi-schemeは伝統的macroではなくdefine-syntaxでquasiquoteを実
5
 現しているが、define-syntaxの実装は難しそうなのでnendoではパスするつも
5
 り。
5

 

0

comment (disabled)

5

5

 

5

 

5

kiyoka.2009_06_01[本][Nendo] コードの世界

5
 4822234312  まつもとゆきひろ コードの世界‾‾スーパー・プログラマになる14の思考法
5

2章まで読んだ。本当にいい本です。オススメ。

5

オレ言語を作ったりしている人はたぶん買っているだろうけど、それ以外の人にも薦めるぞ。

5

2章まで読んだだけでも、なぜ、RubyにLispのようなmacroを導入しなかったのかを感じ取ることができる。

5

Lispのようなmacroで無限の柔軟性を手に入れたいのは一部の人だけで、もっと広く使われるためにはもう少しマイルドな代替手段を使って攻める必要があるという狙いがあるようだ。

5

たしかに、Rubyのmethod_missingの仕組みや、メタプログラミングの仕組みを使えばかなり読み書きしやすいDSLが作れる。

5

でも、個人的にはその仕組みが複数あったり、それぞれになんとなく固有の限界が見え隠れしたりして全容を理解するまでが、遠いという感じがある。でも、これは好みの問題か。

5

LisperはLispマクロの単純なルールで限界を突破するけれども、普通のひとにはたしかにコードをパッと見ただけで拒否反応を起こすのも現実なのは何度も経験している。

5

うーん。Rubyのバランス感覚はすばらしい。

5

でも、それをあえて崩してみるのも楽しいかなと思って作っているのが、Nendoというわけ。さてどんなものになるのやら。

5

 

0

comment (disabled)

5