task5
This commit is contained in:
parent
dce7bb6a2a
commit
7b42215aa1
3 changed files with 226 additions and 0 deletions
194
task5/eval.ml
Normal file
194
task5/eval.ml
Normal file
|
@ -0,0 +1,194 @@
|
|||
(* Абстрактный синтаксис для λ-исчисления:
|
||||
|
||||
e ::= x
|
||||
| λx.e
|
||||
| e e
|
||||
|
||||
В нашем конкретном синтаксисе,
|
||||
|
||||
1. λx.e заменено на fun x -> e
|
||||
2. Допускается (е). Парсер превращает это в e
|
||||
3. Допускается let x = e in e'. Парсер преврящает в (λx.e') e *)
|
||||
type e =
|
||||
| Variable of string
|
||||
| Lambda of string * e
|
||||
| Apply of e * e
|
||||
|
||||
(* От абстрактного синтаксиса обратно в конкретный. *)
|
||||
let rec show e =
|
||||
match e with
|
||||
| Variable x -> x
|
||||
| Lambda (x, e) -> Printf.sprintf "(fun %s -> %s)" x (show e)
|
||||
| Apply (e1, e2) -> Printf.sprintf "(%s %s)" (show e1) (show e2)
|
||||
|
||||
|
||||
|
||||
(* Считывает весь стандартный вход. *)
|
||||
let rec read () =
|
||||
match Stdlib.read_line () with
|
||||
| exception End_of_file -> ""
|
||||
| s -> s ^ " " ^ (read ())
|
||||
|
||||
(* Очень плохой токенизатор. На самом деле надо использовать ocamllex или
|
||||
sedlex. *)
|
||||
let tokenize s =
|
||||
let rec insert token strings =
|
||||
match strings with
|
||||
| s::s'::strings -> s::token::(insert token (s'::strings))
|
||||
| _ -> strings
|
||||
in
|
||||
String.split_on_char '(' s
|
||||
|> insert "("
|
||||
|> List.map (String.split_on_char ')')
|
||||
|> List.map (insert ")")
|
||||
|> List.flatten
|
||||
|> List.map (String.split_on_char ' ')
|
||||
|> List.flatten
|
||||
|> List.map String.trim
|
||||
|> List.filter ((<>) "")
|
||||
|
||||
(* Сам парсер. Первые две функции находят последовательность приложений
|
||||
функций, то есть e1 e2 e3 e4... Третья разбирает каждую e1, e2, ...,
|
||||
определяя, это переменная, лямбда или скобка, содержащая общее выражение. *)
|
||||
let rec parse_general input =
|
||||
let (e, input) = parse_non_apply input in
|
||||
match input with
|
||||
| ")"::_ | "in"::_ | [] -> (e, input)
|
||||
| _ -> parse_apply ~e1:e input
|
||||
|
||||
and parse_apply ~e1 input =
|
||||
let (e2, input) = parse_non_apply input in
|
||||
let e = Apply (e1, e2) in
|
||||
match input with
|
||||
| ")"::_ | "in"::_ | [] -> (e, input)
|
||||
| _ -> parse_apply ~e1:e input
|
||||
|
||||
and parse_non_apply input =
|
||||
match input with
|
||||
| "("::input ->
|
||||
let (e, input) = parse_general input in
|
||||
begin match input with
|
||||
| ")"::input -> (e, input)
|
||||
| _ -> failwith "unmatched '('"
|
||||
end
|
||||
| "fun"::x::"->"::input ->
|
||||
let (e, input) = parse_general input in
|
||||
(Lambda (x, e), input)
|
||||
| "let"::x::"="::input ->
|
||||
let (e1, input) = parse_general input in
|
||||
begin match input with
|
||||
| "in"::input ->
|
||||
let (e2, input) = parse_general input in
|
||||
(Apply (Lambda (x, e2), e1), input)
|
||||
| _ ->
|
||||
failwith "expected 'in'"
|
||||
end
|
||||
| x::input ->
|
||||
(Variable x, input)
|
||||
| [] ->
|
||||
failwith "expected an expression"
|
||||
|
||||
let parse s =
|
||||
s
|
||||
|> tokenize
|
||||
|> parse_general
|
||||
|> fst
|
||||
|
||||
|
||||
|
||||
(* Интерпретатор. *)
|
||||
|
||||
(* Под λx.e, в e заменяет x на y. Если е содержит λ как подвыражение, и эта
|
||||
λ затеняет x, там замена не происходит. *)
|
||||
let rec alpha_vary x y e =
|
||||
match e with
|
||||
| Variable z when z = x -> Variable y
|
||||
| Variable z -> Variable z
|
||||
| Lambda (z, e) when z = x -> Lambda (z, e)
|
||||
| Lambda (z, e) -> Lambda (z, alpha_vary x y e)
|
||||
| Apply (e1, e2) -> Apply (alpha_vary x y e1, alpha_vary x y e2)
|
||||
|
||||
(* Свободные переменные выражения e. То есть те, которые не происходит от
|
||||
окружающей λ. Например, в λx.x y, у свободная переменная, потому что не
|
||||
происходит от окружающей её λ. Свободные переменные могут быть захвачены
|
||||
внешней λ если в λ их не переименовать, то есть если наивно вычислить
|
||||
(λx.λy.x у) у, результат будет λу.у у, но внешняя у не должна ссылаться на
|
||||
внутреннюю y в λy. Для этого во время замены в λy.x у, внутреннюю y надо
|
||||
α-переименовать используя alpha_vary в, например _у1, и результат вычисления
|
||||
будет λ_у1.у _у1. *)
|
||||
let rec free_variables e =
|
||||
match e with
|
||||
| Variable x -> [x]
|
||||
| Lambda (x, e) -> List.filter ((<>) x) (free_variables e)
|
||||
| Apply (e1, e2) -> (free_variables e1) @ (free_variables e2)
|
||||
|
||||
(* Добавляет приблизительно уникальный суффикс к переменной. Парсер не
|
||||
проверяет, что такие суффиксы не могут быть введены вручную. *)
|
||||
let unique =
|
||||
let last_suffix = ref 0 in
|
||||
fun x ->
|
||||
incr last_suffix;
|
||||
Printf.sprintf "_%s%i" x !last_suffix
|
||||
|
||||
(* Заменяет в e все случаи x на e'. Избегает захвата свободных переменных.
|
||||
Смотрите комметарий выше у free_variables. *)
|
||||
let rec substitute e e' x =
|
||||
match e with
|
||||
| Variable y when y = x -> e'
|
||||
| Variable y -> Variable y
|
||||
| Lambda (y, e'') ->
|
||||
if y = x then
|
||||
Lambda (y, e'')
|
||||
else
|
||||
let (y, e'') =
|
||||
if List.mem y (free_variables e') then
|
||||
let y' = unique y in
|
||||
let e'' = alpha_vary y y' e'' in
|
||||
(y', e'')
|
||||
else
|
||||
(y, e'')
|
||||
in
|
||||
Lambda (y, substitute e'' e' x)
|
||||
| Apply (e1, e2) -> Apply (substitute e1 e' x, substitute e2 e' x)
|
||||
|
||||
(* Вычисляет следующий шаг e' вычисления e. Если таковой имеется, результат
|
||||
Some e'. Если вычисление невозможно, результат None. *)
|
||||
let rec step e =
|
||||
match e with
|
||||
| Apply (Apply (Variable "print", Variable x), e) ->
|
||||
print_endline x;
|
||||
Some e
|
||||
| Apply (Lambda (x, e1), e2) ->
|
||||
begin match step e2 with
|
||||
| Some e2' -> Some (Apply (Lambda (x, e1), e2'))
|
||||
| None -> Some (substitute e1 e2 x)
|
||||
end
|
||||
| Apply (e1, e2) ->
|
||||
begin match step e1 with
|
||||
| Some e1' -> Some (Apply (e1', e2))
|
||||
| None ->
|
||||
match step e2 with
|
||||
| Some e2' -> Some (Apply (e1, e2'))
|
||||
| None -> None
|
||||
end
|
||||
| _ -> None
|
||||
|
||||
(* Многократно применяет step пока вычисление не становится невозможным. *)
|
||||
let rec eval e =
|
||||
match step e with
|
||||
| Some e' -> eval e'
|
||||
| None -> e
|
||||
|
||||
(* Аналогичен eval, но выводит шаги *)
|
||||
let rec trace e =
|
||||
show e |> print_endline;
|
||||
match step e with
|
||||
| Some e' -> trace e'
|
||||
| None -> e
|
||||
|
||||
let _ =
|
||||
read ()
|
||||
|> parse
|
||||
|> eval
|
||||
|> show
|
||||
|> print_endline
|
31
task5/numeral.lambda
Normal file
31
task5/numeral.lambda
Normal file
|
@ -0,0 +1,31 @@
|
|||
let 0 =
|
||||
fun f -> fun init ->
|
||||
init
|
||||
in
|
||||
|
||||
let +1 =
|
||||
fun n ->
|
||||
fun f -> fun init ->
|
||||
f (n f init)
|
||||
in
|
||||
|
||||
let 1 = +1 0 in
|
||||
let 2 = +1 1 in
|
||||
let 3 = +1 2 in
|
||||
let 4 = +1 3 in
|
||||
let 5 = +1 4 in
|
||||
|
||||
let print_n =
|
||||
fun n ->
|
||||
n (fun accumulator -> print foo accumulator) (fun x -> x)
|
||||
in
|
||||
|
||||
let + =
|
||||
fun n -> fun n' -> n +1 n'
|
||||
in
|
||||
|
||||
let * =
|
||||
fun n -> fun n' -> n (n' +1) 0
|
||||
in
|
||||
|
||||
print_n (* 4 3)
|
1
task5/test.lambda
Normal file
1
task5/test.lambda
Normal file
|
@ -0,0 +1 @@
|
|||
(fun x -> fun y -> x) z w
|
Reference in a new issue