Forth 情報
個人的 Forth 情報の更新版。
実装するための情報
- Moving Forth: Part1 http://www.bradrodriguez.com/papers/moving1.htm Threaded code について
- CS 636 / 536 Spring 2009 The Structure of Programming Languages
講義録。詳しくてとてもよい。
Forth の有用/有名な文章
- eForth http://offete.com/files/zeneForth.htm eForth と禅/ eForth Overview
- Starting Forth http://www.forth.com/starting-forth/ Forth Inc 社に置いてあるオンライン版
- Thinking Forth http://thinking-forth.sourceforge.net/ Thinking Forth
まとめ
- Forth関連の情報サイトのまとめ http://fujim.tumblr.com/post/361291349/forth
- Forth関連のリンクのまとめ http://filmlang.org/computer/forth/links 実装するときに役立ちそう
処理系など
- Gforth マニュアル http://www.h7.dion.ne.jp/~samwyn/forth/gforth/index.html 日本語
- SwiftX Forth.inc 社の商用の Forth クロスコンパイラ
- Forth Programmer's Handbook Forth Inc 社のドキュメント, SwiftForth 評価版の中にあった。
- amforth http://amforth.sourceforge.net/ Arduino (AVR)で動くっぽい
- Forth in Lisp(FormLis Blog) http://formlis.wordpress.com/2010/06/30/forth-in-lisp/ CL で Forth を実装している記事
- https://github.com/larsbrinkhoff/lbForth lbforth
関係代数(3)
Template Haskell でコード生成を行うようにしたので、関係演算の実装に戻る。
project 演算は、集合に対する演算と見なせば以下のように単純に実装できる。
project :: (Eq a, Eq b) => (a -> b) -> R a -> R b project f (R s) = R (S.map f s)
実例は以下の通り。
>>> :t suppliers suppliers :: R (T4 SNO NAME Integer [Char]) >>> project (\ (T4 x y _ _) -> T2 x y) suppliers SNO:THRelational.SNO SNAME:THRelational.NAME -------------------------------------------- S1 Smith S2 Jones S3 Blake S4 Clark S5 Adams
Tutorial D では、もっと簡潔に SUP { SNO, SNAME} と書ける。
Raspberry Pi
購入。
資料
- http://www.lifehacker.jp/2013/02/130226raspberry_pi_guide.html Raspberry Pi初心者のためのOS別セットアップガイド
- http://blog.bnikka.com/raspberrypi/raspberrypi-install.html Raspberry Pi マウス・キーボード無しでインストールに挑戦
- http://shop.cqpub.co.jp/hanbai/books/MIF/MIFZ201304.html お手軽ARMコンピュータ ラズベリー・パイでI/O
Template Haskell の例
追記: さらに更新(6/23)。
追記: ちょっと更新。
Template Haskell を以下のように進めている。
- 生成したいボイラープレートの一部を書き下し、コンパイルが通ることを確認する。
- インデントによるレイアウト規則を使っている場合は、次に行う構文木の調査のために、波括弧とセミコロンで書き直しておく。
- 生成したいコードを runQ に渡し、構文木を調べる。
- 出力された構文木の構造を調べる。
- 出力された構文木をそのまま返す関数を、サンプルとして書く。
- コンパイルが通ることを確認したら、生成したいボイラープレートを意識しつつ、リファクタリングする。
- ボイラープレートを無くす。
以下は実例。
- 生成したいボイラープレートの一部を書き下し、コンパイルが通ることを確認する。
-- 例。この T1 以外に引数が 2つの T2 a b,3つの T3 a b cと多数の類似のコードがあり、TH で生成したい。 instance (Typeable a, Show a) => Tuple (T1 a) where heading (T1 (A a _)) = [a] amap f (T1 (A a x)) = T1 (A (f a) x) tvalue (T1 (A _ x)) = [show x] degree _ = 1 typeNames (T1 (A _ x)) = [typeOf x]
instance (Typeable a, Show a) => Tuple (T1 a) where { heading (T1 (A a _)) = [a] ; amap f (T1 (A a x)) = T1 (A (f a) x); tvalue (T1 (A _ x)) = [show x]; degree _ = 1; typeNames (T1 (A _ x)) = [typeOf x];}
- 生成したいコードを runQ に渡し、構文木を調べる。この際、コードはオックスフォード角括弧と呼ばれるカッコで囲む。必要なコードの種別に応じたカッコを用いる必要があり、今回の場合はトップレベルの宣言なので d を使う。構文木は Q [Dec] 型となる。
>>> :t [d| instance (Typeable a, Show a) => Tuple (T1 a) where { heading (T1 (A a _)) = [a] ; amap f (T1 (A a x)) = T1 (A (f a) x); tvalue (T1 (A _ x)) = [show x]; degree _ = 1; typeNames (T1 (A _ x)) = [typeOf x];} |] [d| instance (Typeable a, Show a) => Tuple (T1 a) where { heading (T1 (A a _)) = [a] ; amap f (T1 (A a x)) = T1 (A (f a) x); tvalue (T1 (A _ x)) = [show x]; degree _ = 1; typeNames (T1 (A _ x)) = [typeOf x];} |] :: Q [Dec] >>> runQ [d| instance (Typeable a, Show a) => Tuple (T1 a) where { heading (T1 (A a _)) = [a] ; amap f (T1 (A a x)) = T1 (A (f a) x); tvalue (T1 (A _ x)) = [show x]; degree _ = 1; typeNames (T1 (A _ x)) = [typeOf x];} |] [InstanceD [ClassP Data.Typeable.Typeable [VarT a_0],ClassP GHC.Show.Show [VarT a_0]] (AppT (ConT RT.Tuple) (AppT (ConT RT.T1) (VarT a_0))) [FunD heading [Clause [ConP RT.T1 [ConP TH.A [VarP a_1,WildP]]] (NormalB (ListE [VarE a_1])) []],FunD amap [Clause [VarP f_2,ConP RT.T1 [ConP TH.A [VarP a_3,VarP x_4]]] (NormalB (AppE (ConE RT.T1) (AppE (AppE (ConE TH.A) (AppE (VarE f_2) (VarE a_3))) (VarE x_4)))) []],FunD tvalue [Clause [ConP RT.T1 [ConP TH.A [WildP,VarP x_5]]] (NormalB (ListE [AppE (VarE GHC.Show.show) (VarE x_5)])) []],FunD degree [Clause [WildP] (NormalB (LitE (IntegerL 1))) []],FunD typeNames [Clause [ConP RT.T1 [ConP TH.A [WildP,VarP x_6]]] (NormalB (ListE [AppE (VarE Data.Typeable.typeOf) (VarE x_6)])) []]]]
- 出力された構文木をそのまま返す関数(ここでは仮に defT1Ex :: Q [Dec]とする)を、サンプルとして書く。GHC の制限で、トップレベルの宣言は import されなければならないので、構文木を返す関数は、利用するファイルとは別のファイルに書いておく。また、構文木に以下の修正を行なう。
- a_0 や a_1, f_1 などの名前は mkName で作った Name データにおきかえる。※変数名のキャプチャに注意。newName を使う方法がより正しい。
- ライブラリ関数や型名も mkName で作った Name データにおきかえる。ただし、mkName "GHC.Show.Show" ではうまくいかない。 mkName "Show" とする。
-- コード生成を定義しているファイル x.hs defT1Ex :: Q [Dec] defT1Ex = return defT1Ex' defT1Ex' = [InstanceD [ClassP typeable [VarT a_0], ClassP showClass [VarT a_0]] -- Cxt (AppT (ConT tuple) (AppT (ConT t1) (VarT a_0))) -- Type [ FunD (mkName "heading") [Clause [ConP t1 [ConP conA [VarP a_1, WildP]]] (NormalB (ListE [VarE a_1])) []], FunD (mkName "amap") [Clause [VarP f_2,ConP t1 [ConP conA [VarP a_3,VarP x_4]]] (NormalB (AppE (ConE t1) (AppE (AppE (ConE conA) (AppE (VarE f_2) (VarE a_3))) (VarE x_4)))) []], FunD (mkName "tvalue") [Clause [ConP t1 [ConP conA [WildP,VarP x_5]]] (NormalB (ListE [AppE (VarE showFunc) (VarE x_5)])) []], FunD (mkName "degree") [Clause [WildP] (NormalB (LitE (IntegerL 1))) []], FunD (mkName "typeNames") [Clause [ConP t1 [ConP conA [WildP,VarP x_6]]] (NormalB (ListE [AppE (VarE typeof) (VarE x_6)])) []]] ] where conA = mkName "A" showClass = mkName "Show" showFunc = mkName "show" typeable = mkName "Data.Typeable.Typeable" typeof = mkName "Data.Typeable.typeOf" tuple = mkName "Tuple" t1 = mkName "T1" a_0 = mkName "a_0" a_1 = mkName "a_1" a_2 = mkName "a_2" a_3 = mkName "a_3" f_1 = mkName "f_1" f_2 = mkName "f_2" x_4 = mkName "x_4" x_5 = mkName "x_5" x_6 = mkName "x_6" -- コード生成を利用するファイル y.hs ... $(defT1Ex) -- トップレベル宣言は $() は省略可能で、 defT1Ex と書いてもよい。 ...
-
- ppr 関数を使うと、展開形を出力できるので適宜用いて確認する。
>>> ppr defT1Ex' instance (Data.Typeable.Typeable a_0, Show a_0) => Tuple (T1 a_0) where heading (T1 (A a_1 _)) = [a_1] amap f_2 (T1 (A a_3 x_4)) = T1 (A (f_2 a_3) x_4) tvalue (T1 (A _ x_5)) = [show x_5] degree _ = 1 typeNames (T1 (A _ x_6)) = [Data.Typeable.typeOf x_6]
-
- ppr 関数は Q モナドに対してはそのままは使えない。runQ で実行したのち pprint する関数 expand を作っておくと簡単に展開形を確認できる。
expand x = runQ x >>= putStrLn . pprint
- コンパイルが通ることを確認したら、生成したいボイラープレートを意識しつつ、リファクタリングする。どう生成したらいいか不明な場合は、サンプルを増やして確認する。複雑な構文木を作る場合は、単純化するために別の関数として切り出すと間違えにくい。
-
- リファクタリング時には等価な、より生成しやすい単純な式に変形することを考える。
- 引数が一つより多い場合の関数適用、たとえば f x0 x1 x2 は (((f x0) x1) x2) である。foldl を使うと簡単に作成できる。
- リファクタリング時には等価な、より生成しやすい単純な式に変形することを考える。
>>> runQ [d| f x0 x1 x2 = (((f x0) x1) x2) |] [FunD f [Clause [VarP x0_9,VarP x1_10,VarP x2_11] (NormalB (AppE (AppE (AppE (VarE f) (VarE x0_9)) (VarE x1_10)) (VarE x2_11))) []]] >>> FunD (mkName "f") [Clause (map (\x -> VarP (mkName ("a" ++ show x))) [1..3]) (NormalB (foldl AppE (VarE (mkName "f")) (map (\x -> VarE (mkName ("a" ++ show x))) [1..3]))) []] FunD f [Clause [VarP a1,VarP a2,VarP a3] (NormalB (AppE (AppE (AppE (VarE f) (VarE a1)) (VarE a2)) (VarE a3))) []] >>> ppr it f a1 a2 a3 = f a1 a2 a3
-
- where 句を使うことで単純にできる場合がある。
- ghci にオプション -ddump-splices を付けると、splice 時に展開結果を出力してくれる。
- ボイラープレートを無くす。
ボイラープレートを生成する関数としてまとめ、不要となったボイラープレートを削除する。
参考資料
- http://haskell.g.hatena.ne.jp/mr_konn/20111218/1324220725 できる!Template Haskell (完) とても参考になる記事。
- http://www.kotha.net/ghcguide_ja/7.6.2/template-haskell.html Template Haskell
関係代数(2)
属性、タプル、リレーショナルを以下のように実装した。いまのところ、5つの属性からなるタプルまでしか実装していない。
type Attr = String -- | Attribute data A a = A Attr a deriving (Eq, Show) -- | Tuple data T1 a = T1 (A a) deriving (Eq, Show) data T2 a b = T2 (A a) (A b) deriving (Eq, Show) data T3 a b c = T3 (A a) (A b) (A c) deriving (Eq, Show) data T4 a b c d = T4 (A a) (A b) (A c) (A d) deriving (Eq, Show) data T5 a b c d e = T5 (A a) (A b) (A c) (A d) (A e) deriving (Eq, Show) -- | Relational data R a = R (Set a) deriving (Eq)
このデータ型に対する関係演算(restrict, join)は以下のように素直に定義できた。
restrict :: (a -> Bool) -> R a -> R a restrict f (R s) = R (S.filter f s) rJoin :: (Eq a, Eq b, Eq c) => (a -> b -> Maybe c) -> R a -> R b -> R c rJoin f (R x) (R y) = R (S.fromList (catMaybes [f a b | a <- elems x, b <- elems y]))
例えば、(部署番号、部署名、予算) からなる関係 DEPTS と、(従業員番号、氏名、部署番号、給与)からなる関係 EMPS に対して、以下のような演算が行える。DNO は DNO String で定義されるデータ型であるため、単純に文字列との比較は出来ない点に注意。このような型に厳密な演算は、 Tutorial D では(煩雑、ではなく)長所と考えられているようだ。
*Relational> :t depts depts :: R (T3 DNO NAME BUDGET) *Relational> depts DNO DNAME BUDGET ---------------- D1 Marketing 10M D2 Development 12M D3 Research 5M -- SELECT * FROM DEPTS WHERE DNO = "D3" に相当 *Relational> restrict (\ (T3 dno _ _) -> dno == (A "DNO" (DNO "D3"))) depts DNO DNAME BUDGET ---------------- D3 Research 5M *Relational> emps ENO ENAME DNO SALARY -------------------- E1 Lopez D1 40K E2 Cheng D1 42K E3 Finzi D2 30K E4 Saito D2 35K -- SELECT D.DNAME, E.ENAME FROM DEPTS D, EMPS E WHERE D.DNO = E.DNO に相当 *Relational> rJoin deptJoin depts emps DNAME ENAME ----------- Marketing Lopez Marketing Cheng Development Finzi Development Saito
ここで、deptJoin 関数は以下のように定義している。
deptJoin (T3 dno dname budget) (T4 eno ename dno' salary) | dno == dno' = Just (T2 dname ename) | otherwise = Nothing
TODO
- Tutorial D では、ナチュラルジョインは属性名で自動的に行われるため、 EMPS JOIN DEPTS のような簡潔な表記ができる。
- Tutorial D では、上記の実現のため、属性名を RENAME する機能を持つ。
関係代数(1)
Haskell の練習に関係代数を実装しよう。
関係代数は強い静的型ととても相性が良さそうだ。データ構造は色々な定義ができそうだけど、可能な限り、シンプルに行こう。
以下はまだ試行錯誤中のデータ構造。
-- 'Database in Depth', Chapter Two. -- Suppliers data SNO = SNO String deriving (Eq, Show, Ord) theSNO (SNO x) = x data NAME = NAME String deriving (Eq, Show, Ord) type S = (SNO, NAME, Integer, String) suppliers = fromList [ ((SNO "S1"), (NAME "Smith"), 20, "London"), ((SNO "S2"), (NAME "Jones"), 10, "Paris"), ((SNO "S3"), (NAME "Blake"), 30, "Paris"), ((SNO "S4"), (NAME "Clark"), 20, "London"), ((SNO "S5"), (NAME "Adams"), 30, "Athens") ] :: Set S
参考
- "Database in Depth: Relational Theory for Practitioners" http://www.amazon.com/Database-Depth-Relational-Theory-Practitioners/dp/0596100124
quickCheck 英文生成
英文生成
PAIP (Paradigms of Artificial Intelligence Programming) の第2章 「簡単な Lisp プログラムの例」では、ランダムな英文を生成するプログラムを取り上げています。勉強のため、この英文生成プログラムを Haskell で書いてみました。コードは以下に置きました。