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

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