Related
I'm trying to implement an interpreter for the lambda calculus that has constant intergers and supports the addition operation. The interpreter should use the call-by-value small-step operational semantics. So I've implemented a step that should be able to reduce a lambda term by one step. However, the stepper is losing the surrounding program of the reduced subterm when reduced.
This is my implementation in F#:
type Exp =
| Cst of int
| Var of string
| Abs of string * Exp
| App of Exp * Exp
| Arith of Oper * Exp * Exp
and Oper =
Plus
and the stepper looks like this:
let rec step (exp : Exp) (env : Map<string, Exp>) : Exp =
match exp with
| Cst _ | Abs(_) -> exp
| Var x ->
match Map.tryFind x env with
| Some v -> v
| None -> failwith "Unbound variable"
| App(e1, e2) ->
match step e1 env with
| Abs(x, e) ->
let newEnv = Map.add x (step e2 env) env
step e newEnv
| e1' -> failwithf "%A is not a lambda abstraction" e1'
| Arith(Plus, Cst a, Cst b) -> Cst (a + b)
| Arith(Plus, e1, Cst b) -> Arith(Plus, step e1 env, Cst b)
| Arith(Plus, Cst a, e2) -> Arith(Plus, Cst a, step e2 env)
| Arith(Plus, a, b) -> Arith(Plus, step a env, step b env)
So, given the following example of a program (\x.(\y.y x) 21 + 21) \x.x + 1
App
(Abs
("x", App (Abs ("y", App (Var "y", Var "x")), Arith (Plus, Cst 21, Cst 21))),
Abs ("x", Arith (Plus, Var "x", Cst 1)))
I expect the step function to only reduce the 21 + 21 while keeping the rest of the program i.e. I expect the following output after one step (\x.(\y.y x) 42) \x.x + 1. However, I'm not able to retain the surrounding code around the Cst 42. How should I modify the program such that it reduction only steps once while maintaining the rest of the program?
I think there are two things that you should do differently if you want to implement standard small-step CBV lambda calculus.
First, you want to always perform just one step. This means that you should always call step recursively only once. For example, you have Arith(Plus, step a env, step b env) - but this means that if you have an expression representing (1+2)+(2+3), you will reduce this in "one step" to 3+5 but this is really two steps in one.
Second, I don't think your way of handling variables will work. If you have (\x.x+2) 1, this should reduce to 1+2 using variable substitution. You could reduce this to x+2 and remember the assignment x=1 on the side, but then your function would need to work on expression alongside with variable assignment Exp * Map<string, Exp> -> Exp * Map<string, Exp>. It is easier to use normal substitution, at least for the start.
So, I would first define subst x repl exp which substitutes all free occurences of x in the expression exp with repl:
let rec subst (n : string) (repl : Exp) (exp : Exp) =
match exp with
| Var x when x = n -> repl
| Cst _ | Var _ -> exp
| Abs(x, _) when x = n -> exp
| Abs(x, b) -> Abs(x, subst n repl b)
| App(e1, e2) -> App(subst n repl e1, subst n repl e2)
| Arith(op, e1, e2) -> Arith(op, subst n repl e1, subst n repl e2)
Now you can implement your step function.
let rec step (exp : Exp) =
match exp with
// Values - do nothing & return
| Cst _ | Abs _ -> exp
// There should be no variables, because we substituted them
| Var x -> failwith "Unbound variable"
// App #1 - e1 is function, e2 is a value, apply
| App(Abs(x, e1), (Cst _ | Abs _)) -> subst x e2 e1
// App #2 - e1 is not a value, reduce that first
| App(e1, e2) -> App(step e1, e2)
// App #3 - e1 is value, but e2 not, reduce that
| App(Abs(x,e1), e2) -> App(Abs(x,e1), step e2)
// Similar to App - if e1 or e2 is not value, reduce e1 then e2
| Arith(Plus, Cst a, Cst b) -> Cst (a + b)
| Arith(Plus, Cst a, e2) -> Arith(Plus, Cst a, step e2)
| Arith(Plus, a, b) -> Arith(Plus, step a, b)
Using your example:
App
(Abs
("x", App (Abs ("y", App (Var "y", Var "x")), Arith (Plus, Cst 21, Cst 21))),
Abs ("x", Arith (Plus, Var "x", Cst 1)))
|> step
|> step
|> step
|> step
I get:
App (Cst 42, Abs ("x", Arith (Plus, Var "x", Cst 1)))
And if I'm correctly making sense of your example, this is correct - because now you are trying to treat a number as a function, which gets stuck.
Code:
let isPrime x =
let checkZero d = match (x mod d, x mod d + 2, intRoot x < d) with
| (0,_,_) -> false
| (_,0,_) -> false
| (_,_,true) -> true
| _ -> checkZero (d + 6) in
match x with
| 0 -> false
| 1 -> true
| 2 -> true
| 3 -> true
| _ -> match (x mod 2, x mod 3) with
| (0,_) -> false
| (_,0) -> false
| _ -> checkZero 5
Error:
line 9, characters 24-33:
Error: Unbound value checkZero
Which refers to the recursive call checkZero (d+6)
I've tried placing the checkZero function as a let ... in in the final checkZero 5 call and added/removed the x parameter in the checkZero function in case there was an error with the definition.
(Running OCaml downloaded in the past week on OSX through homebrew)
If you want a function to be able to call itself you need to declare it as recursive:
let rec checkZero d ...
I'm working on a mathematical expression simplifier (rather basic, no exponents, logs, roots, fractions etc) and I have it mostly working. Of the 19 tests I've used 14 of them pass. For the 5 remaining, I have written multiple other statements in my simplify function but it doesn't seem to change a thing.
Below is code (copy and paste into an interpreter and it works just fine)
//expression types
type Expression =
| X
| Y
| Const of float
| Neg of Expression
| Add of Expression * Expression
| Sub of Expression * Expression
| Mul of Expression * Expression
// formats string
let exprToString expr =
let rec recExprStr parens expr =
let lParen = if parens then "(" else ""
let rParen = if parens then ")" else ""
match expr with
| X -> "x"
| Y -> "y"
| Const n -> n.ToString()
| Neg e -> lParen + "-" + recExprStr true e + rParen
| Add (e1, e2) -> lParen + recExprStr true e1 + "+" + recExprStr true e2 + rParen
| Sub (e1, e2) -> lParen + recExprStr true e1 + "-" + recExprStr true e2 + rParen
| Mul (e1, e2) -> lParen + recExprStr true e1 + "*" + recExprStr true e2 + rParen
recExprStr false expr
//simplification function
let rec simplify expr =
match expr with
//addition
| Add(Const(ex1), Const(ex2)) -> Const(ex1 + ex2)
| Add(ex1, Const(0.)) -> ex1 |> simplify
| Add(Const(0.), ex1) -> ex1 |> simplify
| Add(Const(num), ex1) -> Add(ex1, Const(num)) |> simplify
| Add(ex1, Neg(ex2)) -> Sub(ex1, ex2) |> simplify
| Add(Neg(ex1), ex2) -> Sub(ex2, ex1) |> simplify
//subtraction
| Sub(Const(num1), Const(num2)) -> Const(num1 - num2)
| Sub(ex1, Const(0.)) -> ex1 |> simplify
| Sub(Const(0.), ex1) -> Neg(ex1) |> simplify
//multiplication
| Mul(Const(num1), Const(num2)) -> Const(num1 * num2)
| Mul(ex1, Const(1.)) -> ex1 |> simplify
| Mul(Const(1.), ex1) -> ex1 |> simplify
| Mul(ex1, Const(0.)) -> Const(0.)
| Mul(Const(0.), ex1) -> Const(0.)
| Mul(ex1, Const(num1)) -> Mul(Const(num1), ex1) |> simplify
| Mul(Neg(ex1), ex2) -> Neg(Mul(ex1, ex2)) |> simplify
| Mul(ex1, Neg(ex2)) -> Neg(Mul(ex1, ex2)) |> simplify
//negation involving a number
| Neg(Const(0.)) -> Const(0.)
| Neg(Neg(ex1)) -> ex1 |> simplify
| _ -> expr
//Tests
printfn "---Provided Tests---"
let t1 = Add (Const 5.0, Const 3.0)
let t2 = Sub (Const 5.0, Const 3.0)
let t3 = Mul (Const 5.0, Const 3.0)
let t4 = Neg (Const 4.0)
let t5 = Neg (Const -9.0)
let t6 = Add (X, Const 0.0)
let t7 = Add (Const 0.0, Y)
let t8 = Sub (X, Const 0.0)
let t9 = Sub (Const 0.0, Y)
let t10 = Sub (Y, Y)
let t11 = Mul (X, Const 0.0)
let t12 = Mul (Const 0.0, Y)
let t13 = Mul (X, Const 1.0)
let t14 = Mul (Const 1.0, Y)
let t15 = Neg (Neg X)
let t16 = Sub (Mul (Const 1.0, X), Add (X, Const 0.0))
let t17 = Add (Mul (Const 4.0, Const 3.0), Sub (Const 11.0, Const 5.0))
let t18 = Sub (Sub (Add (X, Const 1.0), Add (X, Const 1.0)), Add (Y, X))
let t19 = Sub (Const 0.0, Neg (Mul (Const 1.0, X)))
//Output goes here!
//5 + 3 = 0
printfn "t1 Correct: 8\t\tActual: %s" (exprToString (simplify t1))
//5-3 = 2
printfn "t2 Correct: 2\t\tActual: %s" (exprToString (simplify t2))
//5 * 3 = 15
printfn "t3 Correct: 15\t\tActual: %s" (exprToString (simplify t3))
//-(4) = -4
printfn "t4 Correct: -4\t\tActual: %s" (exprToString (simplify t4))
//-(-9) = 9
printfn "t5 Correct: 9\t\tActual: %s" (exprToString (simplify t5))
//x + 0 = x
printfn "t6 Correct: x\t\tActual: %s" (exprToString (simplify t6))
//0 + y = y
printfn "t7 Correct: y\t\tActual: %s" (exprToString (simplify t7))
//x - 0 = x
printfn "t8 Correct: x\t\tActual: %s" (exprToString (simplify t8))
//0 - y = -y
printfn "t9 Correct: -y\t\tActual: %s" (exprToString (simplify t9))
//y - y = 0
printfn "t10 Correct: 0\t\tActual: %s" (exprToString (simplify t10))
//x * 0 = 0
printfn "t11 Correct: 0\t\tActual: %s" (exprToString (simplify t11))
//0 * y = 0
printfn "t12 Correct: 0\t\tActual: %s" (exprToString (simplify t12))
//x * 1 = x
printfn "t13 Correct: x\t\tActual: %s" (exprToString (simplify t13))
//1 * y = y
printfn "t14 Correct: y\t\tActual: %s" (exprToString (simplify t14))
//-(-x) = x
printfn "t15 Correct: x\t\tActual: %s" (exprToString (simplify t15))
// (1 * x) - (x + 0) = 0
printfn "t16 Correct: 0\t\tActual: %s" (exprToString (simplify t16))
//(4 * 3) + (11 - 5) = 18
printfn "t17 Correct: 18\t\tActual: %s" (exprToString (simplify t17))
// ((x + 1) - (x + 1)) - (y+x) = -y -x
printfn "t18 Correct: -y -x\t\tActual: %s" (exprToString (simplify t18))
// 0 - (-(1 * x)) = x
printfn "t19 Correct: x\t\tActual: %s" (exprToString (simplify t19))
// (x + 1) * (-2y + x)
The output of the program is as follows, I have marked the 6 tests that fail (correct is the proper answer, actual is what i'm returning)
t1 Correct: 8 Actual: 8
t2 Correct: 2 Actual: 2
t3 Correct: 15 Actual: 15
t4 Correct: -4 Actual: -4
t5 Correct: 9 Actual: --9 //FAILS
t6 Correct: x Actual: x
t7 Correct: y Actual: y
t8 Correct: x Actual: x
t9 Correct: -y Actual: -y
t10 Correct: 0 Actual: y-y //FAILS
t11 Correct: 0 Actual: 0
t12 Correct: 0 Actual: 0
t13 Correct: x Actual: x
t14 Correct: y Actual: y
t15 Correct: x Actual: x
t16 Correct: 0 Actual: (1*x)-(x+0) //FAILS
t17 Correct: 18 Actual: (4*3)+(11-5) //FAILS
t18 Correct: -(y + x) Actual: ((x+1)-(x+1))-(y+x) //FAILS
t19 Correct: x Actual: x
I'm a bit perplexed as to how I might solve the final 4 (16,17,18) but It seems to me like what I have for #5 and #10 should work.
For test 5, I included | Neg(Neg(ex1)) -> ex1 |> simplify which I thought would catch my double negative but doesn't.
For test 10, I figured something like | Sub(ex1, ex2) -> (ex1 - ex2) would work, but it turns out that's not even valid syntax.
I've looked through a half dozen or so resources on simplification, and even copying and pasting some of their work my tests still fail. It know I must just be missing a case or two, but I'm pulling my hair trying to figure what I might have left out! I greatly appreciate any input!
(Original post had a test 20, I have since removed it for answer simplification purposes. Given my current code, I realized I couldn't possibly simplify it)
5: Neg(Const -9) does not get simplified. It is not a negative of a negative. You need a rule Neg(Const x) -> Const(-x) to replace the Neg(Const 0.) one.
10: Sub(x,y) when y = x -> Const(0)
16: You are not simplifying the inner parts. I would do this before simplifying the outer parts. E.g. Sub(x,y) -> let x',y' = simplify x, simplify y and then match x',y' with....
17: Solution to 16 would fix this.
18: Solution to 10 and 16 would fix this.
Also I cannot resist suggesting let t = [Add (Const 5.0, Const 3.0); Sub (Const 5.0, Const 3.0)...] and then t |> List.iteri ... .
I am using an ad hoc algebraic simplifier which does OK but would like to create a much more serious one. If anyone is seriously intersted in working on this please let me know.
I have the following error from OCaml and I don't understand why. I'm trying to define an interpreter in OCaml. I have some types and functions to evaluate these types. I paste the relevant code.
I have these types:
type ide = string
type exp = Eint of int
| Ebool of bool
| Var of ide
| Prod of exp * exp
| Sum of exp * exp
| Diff of exp * exp
| Eq of exp * exp
| Minus of exp
| Iszero of exp
| Or of exp * exp
| And of exp * exp
| Not of exp
| Ifthenelse of exp * exp * exp
| Let of ide * exp * exp
| Fun of ide list * exp
| Funval of exp * exp env
| Appl of exp * exp list
| Dot of ide * field_name
|Field of ide * exp
| Record of ide * exp list;;
type 'a env = Env of (ide * 'a) list;;
I have a function eval used to eval exp. It works correctly.
let rec eval ((e: exp), (r: exp env)) =
match e with
| Eint(n) -> Eint(n)
| Ebool(b) -> Ebool(b)
| Var(i) -> lookup r i
| Iszero(a) -> iszero(eval(a, r))
| Eq(a, b) -> equ(eval(a, r),eval(b, r))
| Prod(a, b) -> mult(eval(a, r), eval(b, r))
| Sum(a, b) -> plus(eval(a, r), eval(b, r))
| Diff(a, b) -> diff(eval(a, r), eval(b, r))
| Minus(a) -> minus(eval(a, r))
| And(a, b) -> et(eval(a, r), eval(b, r))
| Or(a, b) -> vel(eval(a, r), eval(b, r))
| Not(a) -> non(eval(a, r))
| Ifthenelse(a, b, c) -> let g = eval(a, r) in
if typecheck("bool", g) then
(if g = Ebool(true) then eval(b, r) else eval(c, r))
else failwith ("nonboolean guard")
| Let(i, e1, e2) ->
eval(e2, bind (r, i, eval(e1, r)))
| Fun(x, a) -> Funval(e, r)
| Appl(e1, e2) -> match eval(e1, r) with
| Funval(Fun(x, a), r1) ->
eval(a, bind_list r1 x e2)
| _ -> failwith("no funct in apply")
let eval_field (field:exp) (r: exp env)= match field with
| Field (id, e) -> Field (id, (eval e r))
| _ -> failwith ("Not a Field");;
And finally I have a function to evaluate fields of record:
let eval_field (field:exp) (r: exp env)= match field with
| Field (id, e) -> Field (id, (eval e r))
| _ -> failwith ("Not a Field");;
The problem is with eval_field: OCaml signals me ths error:
Characters 22-24:
let f1 = Field ("f1", e1);;
^^
Error: This expression has type exp/1542
but an expression was expected of type exp/2350
What could be wrong?
Thank you very much for your help.
The compiler is trying to tell you that you have two different types named exp and that you have one of them where the other is expected.
I only see one definition for the type exp in this code. I suspect your environment isn't clean. You might try loading the code up in a new OCaml interpreter. Or perhaps the problem is with some code you're not showing.
Here's a session showing how the error is produced:
$ ocaml
OCaml version 4.02.1
# type abc = A | B | C;;
type abc = A | B | C
# let f (x: abc) = x = A;;
val f : abc -> bool = <fun>
# type abc = A | B | C;;
type abc = A | B | C
# f (C: abc);;
Error: This expression has type abc/1024
but an expression was expected of type abc/1018
My guess is that you have a function (like f here) that was defined using an old definition of your exp type. But you're calling it with a value from the new definition of the type (as here).
I have the following code in OCaml.I have defined all necesar functions and tested them step by step the evalution should work good but I didn't succed to manipulate the variables inside of while.How can I make x,vn,v to change their value?I think I should rewrite the while like a rec loop but can't figure out exactly:
Here is the rest of code: http://pastebin.com/Ash3xw6y
Pseudocode:
input : f formula
output: yes if f valid
else not
begin:
V =set of prop variables
eliminate from f => and <=>
while (V is not empty)
choose x from V
V =V -{x}
replace f with f[x->true]&&f[x->false]
simplify as much as possible f
if f is evaluated with true then return true
else if (not f) is evaluated true then return false
end if
end while
return false
end
type bexp = V of
| string
| B of bool
| Neg of bexp
| And of bexp * bexp
| Or of bexp * bexp
| Impl of bexp * bexp
| Eqv of bexp * bexp
module StringSet=Set.make(String)
let is_valide f=
let v= stringset_of_list (ens f []) in (*set of all variables of f *)
let g= elim f in (*eliminate => and <=> *)
let quit_loop=ref false in
while not !quit_loop
do
let x=StringSet.choose v in
let vn=StringSet.remove x v in
if StringSet.is_empty vn=true then quit_loop:=true;
let h= And( replace x (B true) g ,replace x (B false) g)in
let j=simplify h in
if (only_bools j) then
if (eval j) then print_string "yes"
else print_string "not"
done
(New form)
let tautology f =
let rec tautology1 x v g =
let h= And( remplace x (B true) g ,remplace x (B false) g)in
let j= simplify h in
if not (only_bools j) then tautology (StringSet.choose (StringSet.remove x v) (StringSet.remove x v) j
else
if (eval1 j) then print_string "yes \n " else
if (eval1 (Neg (j))) then print_string "not \n";
in tautology1 (StringSet.choose (stringset_of_list (ens f [])) (stringset_of_list (ens f [])) (elim f);;
while loop belongs to imperative programming part in OCaml.
Basically, you can't modify immutable variables in while or for loops or anywhere.
To let a variable to be mutable, you need to define it like let var = ref .... ref is the keyword for mutables.
Read these two chapters:
https://realworldocaml.org/v1/en/html/a-guided-tour.html#imperative-programming
https://realworldocaml.org/v1/en/html/imperative-programming-1.html
You can define x,vn,v as refs, but I guess it will be ugly.
I suggest you think your code in a functional way.
Since you haven't placed functions ens etc here, I can't produce an example refine for u.