お仕事にまったく役にたたない内容のコードレビューやりたいと思います。
はじめます。
ちょっと 草植えときますね型言語 Grassコードレビュー 本日の内容 ちょっと 草植えときますね型言語 Grassコードレビュー _, ._ ( ・ω・) んもー ○={=}〇, |:::::::::\, ', ´ 、、、、、し 、、、(((.@)wvwwWWwvwwWwwvwwwwWWW wwWwwWWWWWWwwwwWwwvwWWwWwwvwWWW
でも
今回レビューするコード wWwwwWwwwwwWWWWwvwvwwWWWWwWWWWwwwvwwWWwwWwwvwwWWWwWWWwvwWWwWwvwWWwwwwwWwwvwwWWwWWWwvWwvwwwWWWwwWwwWWWWwvWwwwWWWWwWWWWwwwWwwWWWWWwWWWWWWwvwwWWwWWWwvWwwWWwwwwWWWwwwwwwWWWWWWWWWWwwwwwwwwWwwWWwwwwWWWwwwwwwvwwwWWWwwWwwWWWWwvWwwwwwwWWwwwwWWWwwwwWWWWwWWWWWWWWWWWWWWWWWWWwwwwwWwwWWwWWWwWWWWwvwWWWWWWWWWWWWWWWWWWWWWWWWWWWWWwwwwwwwwwWwwwwwwwwwWwwwwwwWwwwwwwwWwwwwwwwWwwwwwwwwwwwwwwwwwwwWwwwwwwwwwwwwwwwwwwwwwwvwwvWWwWwwWwwwwwwWwwwwwwWwwwwwwwwwWwwwwwwwwwwwwwwwwwwWwwwwwwwwwwwwwwwwwv
本日の内容 昔Grassの処理系を実装したので、その経験を ネタにコードレビューします。 Grassの言語仕様の解説もします。 ラムダ計算とかもさわりだけ解説します。 知らない人向けです。 アリエルには詳しい人がいっぱいいそうなので、 おかしな箇所は突っ込みお願いします。
ちょっと草(略)Grassとは ID:UenoB作 型無しラムダ計算ベースの関数型言語 形式的定義 使用する文字はwWvの三文字だけ それ以外の文字は全てコメント扱い。 だから↓こんなのも立派なソースコード _, ._ (・ω・ ) ○={=}〇, |:::::::::\, ', ´ wvwwWW、、、、、、 し 、、、(((.@)wvwwWWwvwwWwwvwwwwWWwwWwwWWWWWWwwwwWwwvwWw
ラムダ計算って何? ググれカス
でも手元にパソコンが無いので λf x.f x
ラムダ計算のなげやりな概要 アロンゾ・チャーチとスティーヴン・コール・ク リーネって人が考案 関数の定義と実行を抽象化した計算体系 LispとかHaskellとか関数型言語の基盤理論 要約すると「全てのもの(データも)は関数」 チューリングマシンが「全てのもの(関数も)は データ」なのでその対極っぽいもの
ラムダ計算の例 ■チャーチ数 ZERO := λf x. x ONE := λf x. f x TWO := λf x. f (f x) THREE := λf x. f (f (f x)) #「数」とは関数fを何回xに適用するかと定義 # f = (x + 1), x = 0 でもとの数字に ■チャーチ真理値 TRUE := λx y. x FALSE := λx y. y # XとYを受けとって必ずXを返すのがTRUE、 Yを返すのがFALSE
Grassの文法 使う文字はW,w,vだけ。 他はの文字は無視。関数定義より前も無視。 操作は関数定義と関数適用の2つだけ wから始まるのが関数定義 Wから始まるのが関数適用 関数定義や関数適用はvで区切る 関数適用が連続する場合はvが省略可能
Grassの操作 ■関数適用 App(n, m) ・WWwwwみたいなの ・Wの数(n)は関数の位置 ・wの数(m)は引数の位置 ■関数適用 Abs(n, C) ・wWWWwwWWwwみたいなの ・最初のwの数(n)が引数の数 ・その後ろ関数のBodyで0個以上の関数適用
Grassの状態 Grassの状態は(C,E,D)で表わされる C = コード E = 環境(スタック) D = リターン先を記憶 Grassの初期状態(C0,E0,D0) C0 = 実行しようとするプログラム E0 = プリミティブ D0 = (App(1,1)::ε,ε)::(ε,ε)::ε
プリミティブ E0 = Out :: Succ :: w :: In :: ε ・w 文字"w"(コード119). 関数として使うと自身と引数をeq ・Out 文字を表示する関数. ・In 文字を入力する関数. ・Succ 次の文字を返す関数.ただし255の次は0.
Grassの処理ルール (App(m, n) :: C, E, D) → (Cm, (Cn, En) :: Em, (C, E) :: D) where E = (C1, E1) :: (C2, E2) :: … :: (Ci, Ei) :: E' (i = m, n) (Abs(n, C') :: C, E, D) → (C, (C', E) :: E, D) if n = 1 (Abs(n, C') :: C, E, D) → (C, (Abs(n - 1, C')::ε, E) :: E, D) if n > 1 (ε, f :: E, (C', E') :: D) → (C', f :: E', D) イミフwww
ルール1:コードの先頭が関数定義で、引数が1つ 処理1:コードの先頭からAbs(1, C')を取り除く 処理2:(C', E) を E にpush => クロージャを作成して環境にpush
ルール2:コードの先頭が関数定義で、引数が2つ以上 処理1:コードの先頭からAbs(n, C')を取り除く 処理2:(Abs(n - 1, C')::ε, E)を E にpush => 「引数の数を一つ減らした関数定義」をBodyに持つクロージャを作成して環境にpush (カリー化)
ルール3:コードの先頭が関数適用 処理1:コードの先頭からApp(m, n)を取り除く 処理2:Dに(C, E)をpush 処理3:Eの先頭からm番目(Cm, Em)を参照 処理4:CをCmに置き換える 処理5:Eを(Cn, En)::Emに置き換える => Dに現在の状態(リターン先)を記録しておく => コードを 実行する関数のBody に 環境を 引数::関数定義時の環境 に置き換える
ルール4:コードが空になる 処理1:Dの先頭から(C', E')を取り出す 処理2:CをC'に置き換える 処理3:Eの先頭からfを取り出す 処理4:Eをf::E'に置き換える => コードが空になったのでリターン処理をする # Dの先頭(C',E') は 関数を実行した時の状態 # f は戻り値。戻り先のスタックにpush
れっつりーでぃんぐ