HaskellでLispを書く日記 このページをアンテナに追加 RSSフィード

2007-03-28

lambdaを作る lambdaを作る - HaskellでLispを書く日記 を含むブックマーク はてなブックマーク - lambdaを作る - HaskellでLispを書く日記 lambdaを作る - HaskellでLispを書く日記 のブックマークコメント

これまでは組み込み関数を作ってきたけど、今回からはlamda(クロージャ)を導入してユーザ定義の関数を作れるようにする。

クロージャ型を用意する

クロージャ関数の本体と、その関数が定義された時点の環境セットにしたデータ。なので、ここで用意するClosure型も関数本体と環境の2つのスロットを持たせる。関数本体は(lambda …)の中に書かれたS式だし、環境も今回はS式で作っているので、Closure型のとる値は2つのSexp型になる。

ついでにshowも定義して値の確認も出来るようにしておく。普通Schemeではこんなことはしてないけど、個人的な趣味により#<closure …>の中に定義時のlambda式の中身を表示することにした。簡単に関数ソースコード(?)がみられると便利かな、と。

data Sexp = Nil | Symbol String | Cons Sexp Sexp | Subr (Sexp->Sexp)
          | Closure Sexp Sexp

  show (Clusure a _) = "#<closure " ++ show a ++ ">"

evalをlambdaに対応させる

ここでは(lambda …)で表されたS式評価してクロージャを作成する処理をつくる。クロージャを呼び出して処理をするところではないので注意。

とはいってもここでは大したことはしない。lambdaの引数である関数本体とその時点での環境セットにしてClosure型のデータにまとめておくだけ。

eval (Cons (Symbol "lambda") body) env = Closure body env

関数ziplを作る

クロージャを作るところが出来たので、今度はクロージャを実行するところに行きたいところだが、ここでそのために使う下請け関数をひとつ作っておく。

zipl関数(本当は関数名をzipにしたかったんだけどすでに同名の関数があるので、ここではリスト用のzipということでziplとした)は3つのリスト引数としてとり、1つめの引数リストと2つめの引数リストの各要素を対にして3つめの引数リストに追加する。

例をあげると、3つの引数が(x y z),(a b c),()であったとすると、( (x . a) (y . b) (z . c) )を返す処理を行う。

また、(x . y),(a b c),()の場合は、((x . a)(y . (b c)))を返す(この性質はlambdaで可変長引数関数を定義するのに使える)。

zipe Nil _ e = e
zipe (Cons k ks) (Cons v vs) e = zipe ks vs $ zipe k v e
zipe k v e = Cons (Cons k v) e

applyをClosureに対応させる

上記の補助関数を使って、クロージャの中身を実行する処理を作る。定義する場所はevalではなく、組み込み関数を作るときに用意したapplyになる。(evalの時点では、組み込み関数でもクロージャ関数でも「関数適用をする」という同じ扱いになっており、実際の関数適用であるapplyのところで、組み込み関数クロージャ関数かで分岐することになる。)

具体的処理としては、まずクロージャ型のデータからパターンマッチによって引数変数部と環境を、var,envとして取り出す。またクロージャを呼び出すときにあたえられた引数値をargとして受け取り、varとargのそれぞれの要素を対にしてクロージャが持っていた環境envに追加する(この処理に上記のziplを使う)。こうして作った新しい環境を使ってクロージャの処理本体であるbody評価すれば、body中の引数変数には呼び出し時の引数値が入っていることになる、という寸法。

apply (Closure (Cons var (Cons body _)) env) arg = eval body $ zipl var arg env

テスト

現時点では(Scheme側に)defineの機能がないので、せっかくlambdaを作っても大したテストができない…。

とりあえず、(lambda…)自体の評価と、(lambda…)式を直接埋め込んで関数を実行するテストをしておく。

本来ならcadrと名前をつけるような処理にしているので、(a b c)に対してbを返しているのでOK。

Main> lisp "(lambda (x) (car (cdr x)))"
#<closure ((x) (car (cdr x)))>
Main> lisp "((lambda (x) (car (cdr x))) '(a b c))"
b

今日までの全コード

import Text.ParserCombinators.Parsec

data Sexp = Nil | Symbol String | Cons Sexp Sexp | Subr (Sexp->Sexp)
          | Closure Sexp Sexp

instance Eq Sexp where
  (==) Nil Nil = True
  (==) (Symbol a) (Symbol b) = a==b
  (==) (Cons a as) (Cons b bs) = a==b && as==bs
  (==) _ _ = False

instance Show Sexp where
  show (Nil) = "()"
  show (Symbol a) = a
  show (Cons a b) = "(" ++ show a ++ showCdr b ++ ")"
  show (Subr _) = "#<subr>"
  show (Closure a _) = "#<closure " ++ show a ++ ">"

showCdr (Nil) = ""
showCdr (Cons a b) = " " ++ show a ++ showCdr b
showCdr a = " . " ++ show a

instance Read Sexp where
  readsPrec _ s = case parse sexpParser "" s of Right a -> [(a,"")]

sexpParser = spaces >>
  (    do { string "("; listParser }
   <|> do { string "'"; a<-sexpParser; return (Cons (Symbol "quote") (Cons a Nil)) }
   <|> do { a<-many1 $ noneOf "'( )"; return (Symbol a) } )

listParser = spaces >>
  (    do { string ")"; return Nil }
   <|> do { string "."; a<-sexpParser; listParser; return a }
   <|> do { a<-sexpParser; b<-listParser; return (Cons a b) } )

true = Symbol "#t"
false = Symbol "#f"

eval (Nil) env = Nil
eval (Symbol s) env = assoc s env
eval (Cons (Symbol "quote") (Cons a _)) _ = a
eval (Cons (Symbol "lambda") body) env = Closure body env
eval (Cons (Symbol "if") (Cons p (Cons t (Cons e _)))) env = if eval p env /= false then eval t env else eval e env
eval (Cons fun arg) env = apply (eval fun env) (evals arg env)

evals (Cons a b) env = Cons (eval a env) (evals b env)
evals a env = eval a env

apply (Subr fun) arg = fun arg
apply (Closure (Cons var (Cons body _)) env) arg = eval body $ zipl var arg env

assoc s (Cons (Cons (Symbol k) v) e) = if s==k then v else assoc s e
assoc s _ = error $ "unbound variable: " ++ show s

zipl Nil _ e = e
zipl (Cons k ks) (Cons v vs) e = zipl ks vs $ zipl k v e
zipl k v e = Cons (Cons k v) e

define key val env = Cons (Cons (Symbol key) val) env
env =
 define "car" (Subr (\(Cons (Cons a _) _)->a)) $
 define "cdr" (Subr (\(Cons (Cons _ a) _)->a)) $
 define "cons" (Subr (\(Cons a (Cons b _))->Cons a b)) $
 define "eq?" (Subr (\(Cons a (Cons b _))->if a==b then true else false)) $
 define "pair?" (Subr (\(Cons a _)->case a of Cons _ _ -> true; _ -> false )) $
   read "((x . (a b c)) (y . a) (#t . #t) (#f . #f))"

lisp str = print $ eval (read str) env

main = do
  lisp "(lambda (x) (car (cdr x)))"
  lisp "((lambda (x) (car (cdr x))) '(a b c))"

次回予定

defineを作る

トラックバック - http://haskell.g.hatena.ne.jp/haskelisp/20070328

2007-03-17

述語関数を作る 述語関数を作る - HaskellでLispを書く日記 を含むブックマーク はてなブックマーク - 述語関数を作る - HaskellでLispを書く日記 述語関数を作る - HaskellでLispを書く日記 のブックマークコメント

前回はcar,cdr,consのリスト操作関数を作ったので、今回は真か偽を返す述語関数を作る。

具体的には、2つの引数が等しいかを判定するeq?と引数リスト(正確にはConsセル)かどうかを判定するpair?を作る。

なぜこの2つかというと、純LISPの基本関数がcar,cdr,cons,eq,atomの5つだから。eqはそのままschemeeq?と対応するけど、atomの方はschemeだとアトムかどうかを判定するかわりにリストかどうかを判定するpair?になる。まあ、真偽が逆転するだけで出来ることは同じだろう、と。

とにかくこれが出来れば胸を張って純LISPと同じレベルまで来ましたといえるんじゃないだろうか。

真偽値の定義

実体はただのシンボルで代用なんだけど、trueとfalseを定義しておく。

これってHaskell的には別の型を起こした方がいいのかな…。でもshowとかをまた別に書く事になるわりには書くこと同じだしなあ。今回はいいや。

true = Symbol "#t"
false = Symbol "#f"

eq?を環境に追加

組み込み関数なので前回と同じように、シンボル環境に追加しつつ無名関数として実体も定義する。

eq?の場合はパターンマッチで取り出した2の引数aとbに対して、Eqクラス(同じeqで紛らわしいな)のインスタンスとするために定義した(==)をそのまま使える。

等しかったら上で定義したtrueを、等しくなかったらfalseを返す。

 define "eq?" (Subr (\(Cons a (Cons b _))->if a==b then true else false)) $

pair?を環境に追加

次はリストかどうかを判定するpair?。あたえられた引数をCons _ _とパターンマッチさせればCons型かどうかがわかる。マッチすればtrueを返し、そうでなければfalseを返す。

 define "pair?" (Subr (\(Cons a _)->case a of Cons _ _ -> true; _ -> false )) $

テスト

今回はxが(a b c)、つまりリスト、yがa、つまりアトムになっている。

なので、pair?についてはxが真、yが偽になっていて予定通り。ちなみにNilもCons型ではないので偽になる。

eq?については、(a b c)とaは当然等しくないが、(a b c)のcarをとったものはaなのでちゃんと等しいと判定している。

Main> lisp "x"
(a b c)
Main> lisp "y"
a
Main> lisp "(pair? x)"
#t
Main> lisp "(pair? y)"
#f
Main> lisp "(pair? ())"
#f
Main> lisp "(eq? x y)"
#f
Main> lisp "(eq? (car x) y)"
#t

今日までの全コード 今日までの全コード - HaskellでLispを書く日記 を含むブックマーク はてなブックマーク - 今日までの全コード - HaskellでLispを書く日記 今日までの全コード - HaskellでLispを書く日記 のブックマークコメント

import Text.ParserCombinators.Parsec

data Sexp = Nil | Symbol String | Cons Sexp Sexp | Subr (Sexp->Sexp)

instance Eq Sexp where
  (==) Nil Nil = True
  (==) (Symbol a) (Symbol b) = a==b
  (==) (Cons a as) (Cons b bs) = a==b && as==bs
  (==) _ _ = False

instance Show Sexp where
  show (Nil) = "()"
  show (Symbol a) = a
  show (Cons a b) = "(" ++ show a ++ showCdr b ++ ")"
  show (Subr _) = "#<subr>"

showCdr (Nil) = ""
showCdr (Cons a b) = " " ++ show a ++ showCdr b
showCdr a = " . " ++ show a

instance Read Sexp where
  readsPrec _ s = case parse sexpParser "" s of Right a -> [(a,"")]

sexpParser = spaces >>
  (    do { string "("; listParser }
   <|> do { string "'"; a<-sexpParser; return (Cons (Symbol "quote") (Cons a Nil)) }
   <|> do { a<-many1 $ noneOf "'( )"; return (Symbol a) } )

listParser = spaces >>
  (    do { string ")"; return Nil }
   <|> do { string "."; a<-sexpParser; listParser; return a }
   <|> do { a<-sexpParser; b<-listParser; return (Cons a b) } )

true = Symbol "#t"
false = Symbol "#f"

eval (Nil) env = Nil
eval (Symbol s) env = assoc s env
eval (Cons (Symbol "quote") (Cons a _)) _ = a
eval (Cons (Symbol "if") (Cons p (Cons t (Cons e _)))) env = if eval p env /= false then eval t env else eval e env
eval (Cons fun arg) env = apply (eval fun env) (evals arg env)

evals (Cons a b) env = Cons (eval a env) (evals b env)
evals a env = eval a env

apply (Subr fun) arg = fun arg

assoc s (Cons (Cons (Symbol k) v) e) = if s==k then v else assoc s e
assoc s _ = error $ "unbound variable: " ++ show s

define key val env = Cons (Cons (Symbol key) val) env
env =
 define "car" (Subr (\(Cons (Cons a _) _)->a)) $
 define "cdr" (Subr (\(Cons (Cons _ a) _)->a)) $
 define "cons" (Subr (\(Cons a (Cons b _))->Cons a b)) $
 define "eq?" (Subr (\(Cons a (Cons b _))->if a==b then true else false)) $
 define "pair?" (Subr (\(Cons a _)->case a of Cons _ _ -> true; _ -> false )) $
   read "((x . (a b c)) (y . a) (#t . #t) (#f . #f))"

lisp str = print $ eval (read str) env

main = do
  lisp "(pair? x)"
  lisp "(pair? y)"
  lisp "(pair? ())"
  lisp "(eq? x y)"
  lisp "(eq? (car x) y)"

次回予告 次回予告 - HaskellでLispを書く日記 を含むブックマーク はてなブックマーク - 次回予告 - HaskellでLispを書く日記 次回予告 - HaskellでLispを書く日記 のブックマークコメント

lambdaを作る

トラックバック - http://haskell.g.hatena.ne.jp/haskelisp/20070317

2007-03-14

組み込み関数対応 組み込み関数対応 - HaskellでLispを書く日記 を含むブックマーク はてなブックマーク - 組み込み関数対応 - HaskellでLispを書く日記 組み込み関数対応 - HaskellでLispを書く日記 のブックマークコメント

やっぱりlisp(LISt Processing)というからには、car,cdr,consが使えないと。というわけでこれらの組み込み関数を使えるようにする。

組み込み関数は、これまでのquoteやifのように個別対応をするのではなく、carであろうがconsであろうがインタプリタとしては統一的な扱いですませられるようにする。

例えば、x=a,y=(b c)の時に(cons x y)を評価する場合を例にとると、

1.関数部分(リストの頭)を評価シンボルconsを評価して、consという変数の中に入っている「1番目の引数であたえられる値をを2番目の引数であたえられるリストに追加するという処理」を取り出す。

2.引数部分(リストの残り)を評価→全ての引数(ここではxとy)を評価して、それぞれaと(b c)を取り出す。

3.関数部分にあった処理を引数部分リスト適用(=apply)する→上記1.で取り出した「処理」を使って(b c)にaを追加したリストを求める。

という扱いにする。具体的には以下のようなものを想定。

eval (Cons fun arg) env = apply (eval fun env) (evals arg env)

apply (Subr fun) arg = fun arg

つまり、インタプリタ関数によってどんな処理をするかということを知っているわけではなく、単にcarとかcdrとかいう変数の中身を取り出して(eval fun env)、そこにあった処理をapplyで呼び出しているだけ。

データコンストラクタSubr

組み込み関数を表すデータコンストラクタとしてSubrを追加する。Subrが持つ値はS式引数にとってS式を返す関数なのでSexp->Sexp型の関数になる。

なんでSubrにしたかというと、gaucheでcarを評価したら#<subr car>と表示されたから。subrはサブルーチンの略(?)らしい。

deriving Eqでは関数Sexp->Sexpの比較は出来ないと怒られるので、(==)関数も自前で作ることにする。あとおまけでshowも追加。

data Sexp = Nil | Symbol String | Cons Sexp Sexp | Subr (Sexp->Sexp)

instance Eq Sexp where
  (==) Nil Nil = True
  (==) (Symbol a) (Symbol b) = a==b
  (==) (Cons a as) (Cons b bs) = a==b && as==bs
  (==) _ _ = False

  show (Subr _) = "#<subr>"

car,cdr,consを環境に追加

上記Subr型を値にもつ変数car,cdr,consを環境に追加する。

ちょっとだけキータイプ数を減らすためにユーティリティ関数としてdefineを定義する。

defineは3つの引数key,val,envをとり、keyシンボル化したものとvalを対にして環境envに追加した環境を返すもの。(なので、defineの返値にさらにdefineを連続して使う。)

define key val env = Cons (Cons (Symbol key) val) env
env =
 define "car" (Subr (\(Cons (Cons a _) _)->a)) $
 define "cdr" (Subr (\(Cons (Cons _ a) _)->a)) $
 define "cons" (Subr (\(Cons a (Cons b _))->Cons a b)) $
   read "((x . (a b c)) (#t . #t) (#f . #f))"

引数評価用evals

関数適用するためには事前に全ての引数評価が行われていなければいけないので、リストになっている引数を全て評価したリストを返す関数evalsを作る。

汎用的なmap関数を作って引数関数evalをとるようにしてもいいのかもしれないけど、他にmapを使い回す予定がいまのところはないので、普通にevalsを作る。

evals (Cons a b) env = Cons (eval a env) (evals b env)
evals a env = eval a env

テスト

ここでもズボラ化。いちいちeval (read …と書くのがめんどくさくなってきたので、ユーティリティ関数lispを定義する。

lisp str = print $ eval (read str) env

で、テスト

Main> lisp "x"
(a b c)
Main> lisp "(car x)"
a
Main> lisp "(cdr x)"
(b c)
Main> lisp "(cons x x)"
((a b c) a b c)
Main> lisp "(cons (car x) x)"
(a a b c)

今日までの全コード 今日までの全コード - HaskellでLispを書く日記 を含むブックマーク はてなブックマーク - 今日までの全コード - HaskellでLispを書く日記 今日までの全コード - HaskellでLispを書く日記 のブックマークコメント

import Text.ParserCombinators.Parsec

data Sexp = Nil | Symbol String | Cons Sexp Sexp | Subr (Sexp->Sexp)

instance Eq Sexp where
  (==) Nil Nil = True
  (==) (Symbol a) (Symbol b) = a==b
  (==) (Cons a as) (Cons b bs) = a==b && as==bs
  (==) _ _ = False

instance Show Sexp where
  show (Nil) = "()"
  show (Symbol a) = a
  show (Cons a b) = "(" ++ show a ++ showCdr b ++ ")"
  show (Subr _) = "#<subr>"

showCdr (Nil) = ""
showCdr (Cons a b) = " " ++ show a ++ showCdr b
showCdr a = " . " ++ show a

instance Read Sexp where
  readsPrec _ s = case parse sexpParser "" s of Right a -> [(a,"")]

sexpParser = spaces >>
  (    do { string "("; listParser }
   <|> do { string "'"; a<-sexpParser; return (Cons (Symbol "quote") (Cons a Nil)) }
   <|> do { a<-many1 $ noneOf "'( )"; return (Symbol a) } )

listParser = spaces >>
  (    do { string ")"; return Nil }
   <|> do { string "."; a<-sexpParser; listParser; return a }
   <|> do { a<-sexpParser; b<-listParser; return (Cons a b) } )

eval (Nil) env = Nil
eval (Symbol s) env = assoc s env
eval (Cons (Symbol "quote") (Cons a _)) _ = a
eval (Cons (Symbol "if") (Cons p (Cons t (Cons e _)))) env = if eval p env /= Symbol "#f" then eval t env else eval e env
eval (Cons fun arg) env = apply (eval fun env) (evals arg env)

evals (Cons a b) env = Cons (eval a env) (evals b env)
evals a env = eval a env

apply (Subr fun) arg = fun arg

assoc s (Cons (Cons (Symbol k) v) e) = if s==k then v else assoc s e
assoc s _ = error $ "unbound variable: " ++ show s

define key val env = Cons (Cons (Symbol key) val) env
env =
 define "car" (Subr (\(Cons (Cons a _) _)->a)) $
 define "cdr" (Subr (\(Cons (Cons _ a) _)->a)) $
 define "cons" (Subr (\(Cons a (Cons b _))->Cons a b)) $
   read "((x . (a b c)) (#t . #t) (#f . #f))"

lisp str = print $ eval (read str) env

main = do
  lisp "x"
  lisp "(car x)"
  lisp "(cdr x)"
  lisp "(cons x x)"
  lisp "(cons (car x) x)"

次回予告 次回予告 - HaskellでLispを書く日記 を含むブックマーク はてなブックマーク - 次回予告 - HaskellでLispを書く日記 次回予告 - HaskellでLispを書く日記 のブックマークコメント

もうちょっと組み込み関数を増やす

トラックバック - http://haskell.g.hatena.ne.jp/haskelisp/20070314

2007-03-06

evalの作成(if編) evalの作成(if編) - HaskellでLispを書く日記 を含むブックマーク はてなブックマーク - evalの作成(if編) - HaskellでLispを書く日記 evalの作成(if編) - HaskellでLispを書く日記 のブックマークコメント

なんとなくifが出来ると言語っぽい処理をしている気がしてくるので、今回はifを作ることにする。

ifは(if p t e)と3つの引数をとり、pの評価結果が真のとき(#tをはじめとして#f以外の全ての値)にt(thenのつもり)を評価して返し、pの評価結果が偽のとき(#fのときのみ)にe(elseのつもり)を評価して返す。

真偽値の登録

今回作っているlispは一応scheme系を自称しているので真偽値は#t/#fで表すことにする。パース結果としての#t/#fを評価したときに評価結果としても#t/#fとなってほしいので、#tと#fを環境に登録する。

env = read "((x . a) (y . b) (#t . #t) (#f . #f))"

Eqクラスインスタンス

最終的にifの内部処理ではSexp型の値としての#f(=Symbol "#f")との比較をすることになるので、Sexp型自体で同値判定(==)の演算が出来るようにEqクラスインスタンスとする。

deriving宣言を使ってみたらうまく動いているようなので今回はこれを使って楽をする。

data Sexp = Nil | Symbol String | Cons Sexp Sexp deriving Eq

if用eval部分

ifの処理をするのは、4つの要素をもつリストであり最初の要素がifになっている場合のとき。なので、4段階にConsされたものにおいて、順番に(Symbol "if")、p、t、eにパターンマッチさせる。そしてマッチしたならば、pについてevalし、その結果がSymbol "#f"と等しくなかった場合はtについてevalし、等しい場合はeについてevalする。

eval (Cons (Symbol "if") (Cons p (Cons t (Cons e _)))) env = if eval p env /= Symbol "#f" then eval t env else eval e env

テスト

ifの処理をテストしてみる。とはいっても、まだ真偽値を返す関数を1つも持っていないので、寂しいけど定数をあたえてテストする。

真を表す#tのときは、xを評価したaを返す。偽を表す#fのときはyを評価したbを返す。

他の値でも試してみる。()、'foo等も#fではないので真の扱いになりx側を評価する。

最後にifの入れ子を試してみる。

Main> eval (read "(if #t x y)") env
a
Main> eval (read "(if #f x y)") env
b
Main> eval (read "(if () x y)") env
a
Main> eval (read "(if 'foo x y)") env
a
Main> eval (read "(if (if #t #f #t) x y)") env
b

エラー処理解決 エラー処理解決 - HaskellでLispを書く日記 を含むブックマーク はてなブックマーク - エラー処理解決 - HaskellでLispを書く日記 エラー処理解決 - HaskellでLispを書く日記 のブックマークコメント

前回assoc関数中でerror関数を使って例外を発生させようとしたのに出来なかった件が解決。今回から以下の修正版に差し替える。単に++の優先度を見落としていただけだったorz

assoc s _ = error $ "unbound variable: " ++ show s

今日までの全コード 今日までの全コード - HaskellでLispを書く日記 を含むブックマーク はてなブックマーク - 今日までの全コード - HaskellでLispを書く日記 今日までの全コード - HaskellでLispを書く日記 のブックマークコメント

import Text.ParserCombinators.Parsec

data Sexp = Nil | Symbol String | Cons Sexp Sexp deriving Eq

instance Show Sexp where
  show (Nil) = "()"
  show (Symbol a) = a
  show (Cons a b) = "(" ++ show a ++ showCdr b ++ ")"

showCdr (Nil) = ""
showCdr (Cons a b) = " " ++ show a ++ showCdr b
showCdr a = " . " ++ show a

instance Read Sexp where
  readsPrec _ s = case parse sexpParser "" s of Right a -> [(a,"")]

sexpParser = spaces >>
  (    do { string "("; listParser }
   <|> do { string "'"; a<-sexpParser; return (Cons (Symbol "quote") (Cons a Nil)) }
   <|> do { a<-many1 $ noneOf "'( )"; return (Symbol a) } )

listParser = spaces >>
  (    do { string ")"; return Nil }
   <|> do { string "."; a<-sexpParser; listParser; return a }
   <|> do { a<-sexpParser; b<-listParser; return (Cons a b) } )

eval (Nil) env = Nil
eval (Symbol s) env = assoc s env
eval (Cons (Symbol "quote") (Cons a _)) _ = a
eval (Cons (Symbol "if") (Cons p (Cons t (Cons e _)))) env = if eval p env /= Symbol "#f" then eval t env else eval e env

assoc s (Cons (Cons (Symbol k) v) e) = if s==k then v else assoc s e
assoc s _ = error $ "unbound variable: " ++ show s

env = read "((x . a) (y . b) (#t . #t) (#f . #f))"

main = do
  print $ eval (read "(if #t x y)") env
  print $ eval (read "(if #f x y)") env
  print $ eval (read "(if () x y)") env
  print $ eval (read "(if 'foo x y)") env
  print $ eval (read "(if (if #t #f #t) x y)") env

次回予告 次回予告 - HaskellでLispを書く日記 を含むブックマーク はてなブックマーク - 次回予告 - HaskellでLispを書く日記 次回予告 - HaskellでLispを書く日記 のブックマークコメント

組み込み関数を入れる

トラックバック - http://haskell.g.hatena.ne.jp/haskelisp/20070306