1- def do_algebra : Unit :=
2- ()
1+ inductive Op where
2+ | add
3+ | sub
4+ | mul
5+ | div
6+ | pow
7+
8+ namespace Op
9+
10+ instance : ToString Op where
11+ toString
12+ | add => "+"
13+ | sub => "-"
14+ | mul => "*"
15+ | div => "/"
16+ | pow => "^"
17+
18+ def prio : Op → Nat
19+ | add | sub => 1
20+ | mul | div => 2
21+ | pow => 3
22+
23+ def leftAssoc : Op → Bool
24+ | add | sub => true
25+ | mul | div => true
26+ | pow => false
27+
28+ def interpret : Op → (Nat → Nat → Nat)
29+ | add => (· + ·)
30+ | sub => (· - ·)
31+ | mul => (· * ·)
32+ | div => (· / ·)
33+ | pow => (· ^ ·)
34+
35+ end Op
36+
37+ inductive Expr where
38+ | lit (n : Nat)
39+ | app (op : Op) (arg₁ arg₂ : Expr)
40+ deriving Inhabited
41+
42+ namespace Expr
43+
44+ instance : ToString Expr where
45+ toString :=
46+ let rec go
47+ | .lit n => s! "{ n} "
48+ | .app op arg₁ arg₂ => s! "({ go arg₁} { op} { go arg₂} )"
49+ go
50+
51+ def eval : Expr → Nat
52+ | lit n => n
53+ | app op arg₁ arg₂ => op.interpret arg₁.eval arg₂.eval
54+
55+ structure ParseState where
56+ ops : List Op := []
57+ args : List Nat := []
58+ hold : List Op := []
59+ output : List Expr := []
60+ deriving Inhabited
61+
62+ namespace ParseState
63+
64+ def pushArg (σ : ParseState) (arg : Nat) : ParseState :=
65+ { σ with output := (lit arg) :: σ.output }
66+
67+ @[simp]
68+ theorem pushArg_ops (σ : ParseState) : (σ.pushArg arg).ops = σ.ops := rfl
69+
70+ def pushOp (σ : ParseState) (op : Op) : ParseState :=
71+ match σ.hold with
72+ | [] => { σ with hold := [op] }
73+ | top :: hold =>
74+ match compare top.prio op.prio, top.leftAssoc with
75+ | .lt, _ | .eq, false => { σ with hold := op :: top :: hold }
76+ | .gt, _ | .eq, true =>
77+ match σ.output with
78+ | arg₂ :: arg₁ :: out => { σ with hold := op :: hold, output := .app top arg₁ arg₂ :: out }
79+ | _ => σ -- Trash value chosen to easily satisfy `pushOp_ops`.
80+
81+ @[simp]
82+ theorem pushOp_ops (σ : ParseState) : (σ.pushOp op).ops = σ.ops := by
83+ rw [pushOp]
84+ repeat (split <;> try rfl)
85+
86+ def finalize (σ : ParseState) : ParseState :=
87+ match _ : σ.hold, σ.output with
88+ | op :: hold, arg₂ :: arg₁ :: out => finalize { σ with hold, output := .app op arg₁ arg₂ :: out }
89+ | _, _ => σ
90+ termination_by σ.hold
91+ decreasing_by simp_all +arith
92+
93+ end ParseState
94+
95+ -- Parses an expression based on the "shunting yard algorithm".
96+ def parse! (ops : List Op) (args : List Nat) : Expr :=
97+ go { ops, args } |>.output[0 ]!
98+ where
99+ go (σ : ParseState) : ParseState :=
100+ match _ : σ.ops, σ.args with
101+ | op :: ops, arg :: args => { σ with ops, args } |>.pushArg arg |>.pushOp op |> go
102+ | [], [arg] => { σ with args := [] } |>.pushArg arg |>.finalize
103+ | _, _ => panic! "Invalid parse state"
104+ termination_by σ.ops
105+ decreasing_by simp_all +arith
106+
107+ end Expr
108+
109+ def doAlgebra (ops : List Op) (args : List Nat) : Nat :=
110+ Expr.parse! ops args |>.eval
111+
112+ example : doAlgebra [.add, .mul, .sub] [2 , 3 , 4 , 5 ] = 9 := by native_decide
113+ example : doAlgebra [.pow, .mul, .add] [2 , 3 , 4 , 5 ] = 37 := by native_decide
114+ example : doAlgebra [.add, .mul, .sub] [2 , 3 , 4 , 5 ] = 9 := by native_decide
115+ example : doAlgebra [.div, .mul] [7 , 3 , 4 ] = 8 := by native_decide
3116
4117/-!
5118## Prompt
@@ -8,16 +121,16 @@ def do_algebra : Unit :=
8121
9122def do_algebra(operator, operand):
10123 """
11- Given two lists operator, and operand. The first list has basic algebra operations, and
12- the second list is a list of integers. Use the two given lists to build the algebric
124+ Given two lists operator, and operand. The first list has basic algebra operations, and
125+ the second list is a list of integers. Use the two given lists to build the algebric
13126 expression and return the evaluation of this expression.
14127
15128 The basic algebra operations:
16- Addition ( + )
17- Subtraction ( - )
18- Multiplication ( * )
19- Floor division ( // )
20- Exponentiation ( ** )
129+ Addition ( + )
130+ Subtraction ( - )
131+ Multiplication ( * )
132+ Floor division ( // )
133+ Exponentiation ( ** )
21134
22135 Example:
23136 operator['+', '*', '-']
@@ -56,4 +169,4 @@ def check(candidate):
56169 assert True, "This prints if this assert fails 2 (also good for debugging!)"
57170
58171```
59- -/
172+ -/
0 commit comments