■
ラムダ計算論の本読んでたので勢い余って関数型言語ocamlでラムダ計算機を書きました。
これでチャーチ数定義して計算すれば、ノイマン型の命令型計算機で計算している人を見かけた時に
「ぷっダサwww」と言えます。
一応このラムダ計算機の使い方
- ラムダ式の記法は (\x y. x y) のようにλを\と書く
- ベータ変換は最左変換戦略
(ちょっと最左最内変換戦略で書いてたら正規化されなくて悩んでたけど、ラムダ式がβ-nfを持つのときに最左変換ではβ-nfに変換される事が定理で保証されているけど、最左最内では保証されていなかったので当たり前だった)
それ以上β変換されなくなったらそれが正規形です。
非正規型の場合、引数の数値を上げても永遠に計算が終わりません
本計算機での計算例
例.1 succ
(\n f x. f (n f x)) (\f x. f (f (f x)))) -> (\f x. f (f (f (f x))))
例.2 plus
(\m n f x. m f (n f x)) (\f x. f(f x)) (\f x. f x) -> (\f x. f (f (f x)))
例.3 pred
(\n f x. n (\g h. h(g f))(\u. x)(\u. u)) (\f x. f( f (f x)))) -> (\f x. (f (f x)))
※余計な括弧があるけどλ式としては無い場合と等価
例.4 mult
(\m n f. m (n f)) (\f x. f (f x)) (\f x. f (f (f x))) -> (\f . (\x . f (f (f (f (f (f x )))))))
※(\x y. M N)は (\x. (\y. (M N))の略記なので上記は (\f x. f (f (f (f (f (f x))))))と等価
ソース
type char_type = LPAREN | RPAREN | LAMBDA | DOT | OTHER type lambda_v = string list type tree = App of lambda_v | Lambda of lambda_v * tree list let channel = open_in "lambada.lm" let token_buf = Buffer.create 64 (* utilities *) let get_l (a, b) = a let get_r (a, b) = b let rec each f e = fun x -> match x with [] -> e | x::rest -> ( f x; each f e rest ) let rec contain list n = match list with x::rest -> if x = n then true else contain rest n | [] -> false let empty lambda = match lambda with App([]) -> true | Lambda([], []) -> true | _ -> false let default_indent = " " let rec show_list_native list = match list with [] -> () | x::rest -> print_string (x ^ " "); show_list_native (rest) let rec show_lm_native lm indent = match lm with App(l) -> print_string (indent ^ "App:"); show_list_native l; print_string "\n" | Lambda(l, list) -> print_string (indent ^ "Lambda \n"); print_string (indent ^ " Variables:"); show_list_native l; print_string "\n"; each (fun x -> show_lm_native x (indent ^ default_indent)) () list let rec show_list list = match list with [] -> () | "paren"::rest -> show_list rest | x::rest -> print_string (x ^ " "); show_list (rest) let rec show_lm lm indent = match lm with App(l) -> show_list l; | Lambda(l, list) -> match l with [] -> each (fun x -> show_lm x (indent ^ default_indent)) () list | ["paren"] -> ( print_string "("; each (fun x -> show_lm x (indent ^ default_indent)) () list; print_string ")" ) | _ -> ( print_string "(\\"; show_list l; print_string ". "; each (fun x -> show_lm x (indent ^ default_indent)) () list; print_string ")" ) let get_v_lm lm = match lm with Lambda(v, l) -> v | App(v) -> [""] let get_children_lm lm = match lm with Lambda(v, l) -> l | App(v) -> [] let add_lm_list_pre lm new_lm = if not(empty new_lm) then match lm with Lambda([], []) -> new_lm | Lambda(l, r) -> Lambda(l, [new_lm]@r) | _ -> lm else lm let add_variable_list_pre x value = match x with Lambda(l, r) -> Lambda(value::l, r) | _ -> x let add_lm_list lm new_lm = if not(empty new_lm) then match lm with Lambda([], []) -> new_lm | Lambda(l, r) -> Lambda(l, r@[new_lm]) | _ -> lm else lm let add_variable_list x value = match x with Lambda(l, r) -> Lambda(l@[value], r) | _ -> x let rec reduce_paren lm = let rec reduce_paren_list lmlist = match lmlist with [] -> lmlist | lm1::lmlist1 -> (reduce_paren lm1)::(reduce_paren_list lmlist1) in match lm with App(v) -> lm | Lambda(["paren"], lmlist') -> ( match lmlist' with [Lambda(["paren"], lmlist'')] -> Lambda(["paren"], (reduce_paren_list lmlist'')) | [Lambda(vlist, lmlist''')] -> Lambda(vlist, (reduce_paren_list lmlist''')) | _ -> Lambda(["paren"], (reduce_paren_list lmlist')) ) | Lambda(vlist, lmlist') -> Lambda(vlist, (reduce_paren_list lmlist')) (* get token from lambda string *) let check_type str = match str with "(" -> LPAREN | ")" -> RPAREN | "\\" -> LAMBDA | "." -> DOT | _ -> OTHER let get_tokens ch = let add_token token_buf token tokens = if Buffer.length token_buf != 0 then ( Buffer.clear token_buf; token::tokens ) else ( tokens ) in let rec get_char in_token tokens = try let c = input_char ch in match check_type (String.make 1 c) with LPAREN|RPAREN|LAMBDA|DOT -> let token = Buffer.contents token_buf in get_char false ((String.make 1 c)::(add_token token_buf token tokens)) | OTHER -> match c with '\r' | '\n' | '\t' | ' ' -> let token = Buffer.contents token_buf in get_char false (add_token token_buf token tokens) | _ -> if in_token then ( Buffer.add_char token_buf c; get_char false ((Buffer.contents token_buf)::tokens) ) else ( Buffer.add_char token_buf c; get_char true tokens ) with End_of_file -> tokens in get_char false [] (* parse. create Lambda tree *) let rec parse tokens lm cur_lm is_lambda = match tokens with [] -> ( match cur_lm with Lambda([], []) -> (lm, []) | App(a) -> ((add_lm_list lm cur_lm), []) | _ -> (lm, []) ) | token::rest -> match check_type token with LPAREN -> let (lm', next) = parse rest (Lambda([], [])) (Lambda(["paren"], [])) false in parse next (add_lm_list (add_lm_list lm cur_lm) lm') (Lambda([], [])) false | LAMBDA -> parse rest lm cur_lm true | DOT -> parse rest lm cur_lm false | RPAREN -> ((add_lm_list lm cur_lm), rest) | OTHER -> if is_lambda then parse rest lm (add_variable_list cur_lm token) is_lambda else parse rest lm (add_lm_list cur_lm (App([token]))) is_lambda (* Alpha Conversion *) let rec get_replaced_v v depth = if depth = 0 then v else get_replaced_v (v ^ "0") (depth - 1) let rec convert_vlist list x replace = match list with [] -> [] | y::rest -> if x == y then replace::rest else y::(convert_vlist rest x replace) let rec alpha_convert x lmlist depth do_replace = let replace_lm src replace lm depth = match lm with App(vlist) -> if contain vlist src then (App(convert_vlist vlist src replace), true) else (lm, false) | Lambda(vlist, lmlist') -> let (lmlist'', do_replace'') = (alpha_convert x lmlist' (depth + 1) do_replace) in if do_replace'' then (Lambda((convert_vlist vlist x (get_replaced_v src depth)), lmlist''), true) else (Lambda(vlist, lmlist''), false) in match lmlist with [] -> ([], do_replace) | lm::lmlist_rest -> let replace = get_replaced_v x depth in let (lm', do_replace') = replace_lm x replace lm depth in if do_replace then (lm'::(get_l(alpha_convert x lmlist_rest depth do_replace)), true) else let (lmlist', do_replace'') = (alpha_convert x lmlist_rest depth do_replace) in (lm::lmlist', do_replace'') let rec alpha = fun lm -> match lm with App(v) -> lm | Lambda(vlist, lmlist) -> match vlist with ["paren"]|[] -> print_endline "alpha1"; each (fun x -> alpha x) (Lambda([],[])) lmlist | x::vlist_rest -> print_endline "alpha2"; let (lmlist', do_replace) = alpha_convert x lmlist 0 false in if do_replace then ( print_endline "alpha2 suc"; Lambda(vlist, lmlist') ) else ( add_variable_list_pre (alpha (Lambda(vlist_rest, lmlist))) x ) (* Beta Reduction *) let rec beta_reduction = fun lm -> let rec trans_lms lmlist x lm do_trans = (* lmlist u App:f do_trans *) match lmlist with [] -> ([], do_trans) | lm'::rest -> match lm' with App(vlist) -> if (contain vlist x) then ( let (lmlist1, do_trans1) = (trans_lms rest x lm true) in ((lm::lmlist1), true) ) else ( let (lmlist1, do_trans1) = (trans_lms rest x lm do_trans) in (lm'::lmlist1, true) ) | Lambda(l, r) -> if (contain l x) then (* for alpha conversion *) ( (lmlist, do_trans) ) else let (lmlist1, do_trans1) = (trans_lms r x lm do_trans) in let (lmlist2, do_trans2) = (trans_lms rest x lm do_trans) in (Lambda(l, lmlist1)::lmlist2, (do_trans1 || do_trans2)) in let rec trans_lm lm1 lm2 do_trans = let rec trans_lmlist lmlist lm2 do_trans = match lmlist with [] -> (lmlist, false) | lm::rest -> let (lm', do_trans') = trans_lm lm lm2 do_trans in let (lmlist', do_trans'') = trans_lmlist rest lm2 do_trans in (lm'::lmlist', (do_trans'||do_trans'')) in match lm1 with App(vlist) -> (lm1, false) | Lambda(vlist, lmlist) -> match vlist with [] -> (lm1, false) | ["paren"] -> let (lm'', do_trans'') = beta_reduction lm1 in (*trans_lmlist lmlist lm2 do_trans in*) if do_trans'' then (Lambda(["paren"], lm''::lm2::[]), true) else let (lmlist', do_trans''') = trans_lmlist lmlist lm2 do_trans in if do_trans''' then (Lambda(["paren"], lmlist'), true) else (lm1, false) | [v1] -> let (lmlist', do_trans') = trans_lms lmlist v1 lm2 false in if do_trans' then (Lambda(["paren"], lmlist'), true) else (lm1, false) | "paren"::v2::rest -> let (lmlist', do_trans') = trans_lms lmlist v2 lm2 false in if do_trans' then (Lambda("paren"::rest, lmlist'), true) else (lm1, false) | v1::rest -> let (lmlist', do_trans') = trans_lms lmlist v1 lm2 false in if do_trans' then (Lambda(rest, lmlist'), true) else (lm1, false) in match lm with App(vlist) -> (lm, false) | Lambda([], []) -> (lm, false) | Lambda(vlist, lmlist) -> match lmlist with [] -> (lm, false) | [lm'] -> let (lm', do_trans') = beta_reduction lm' in if do_trans' then (Lambda(vlist, [lm']), true) else (lm, false) | lm1::lm2::rest -> let (lm'', do_trans) = trans_lm lm1 lm2 false in let lmlist' = if (get_v_lm lm'') = ["paren"] then ( (* debug_print lm lm1 lm'' lm2 vlist do_trans *) get_children_lm lm'' ) else [lm''] in if do_trans then (Lambda(vlist, lmlist'@rest), true) else let (lm1', do_trans') = beta_reduction lm1 in if do_trans' then (Lambda(vlist, lm1'::lm2::rest), true) else let (lm''', do_trans'') = beta_reduction (Lambda(vlist, lm2::rest)) in if do_trans'' then (Lambda(vlist, lm1::(get_children_lm lm''')), true) else (lm, false) let rec beta_n n lm do_trans = match n with 0 -> (lm, do_trans) | _ -> let (lm', do_trans') = (beta_reduction lm) in if do_trans' then ( beta_n (n - 1) (reduce_paren lm') do_trans' ) else (lm', false) let _ = let count = int_of_string Sys.argv.(1) in let tokens = get_tokens channel in let (lm, _) = parse (List.fold_left (fun x y -> y::x) [] tokens) (Lambda(["paren"],[])) (Lambda([], [])) false in (* show_lm_native (alpha lm) default_indent; *) let (lm', do_trans) = beta_n count lm false in show_lm (reduce_paren lm') default_indent; print_endline ""
ocaml初なのでmap使えよwとか突っ込みどころ満載です。
そういえばこないだ会社行く途中の電車で「計算論」読んでたら乗り換え駅で降り忘れて2駅ほど先に進んでしまった。
その駅が会社から近かったんで、走れば間に合うだろうと思って全力疾走してたら逆走している事に気づいて結局タクった。
マジ計算論危険 有給無くなって遅刻できないやつにはお勧め出来ない。