我正在编写一个ocaml程序来处理基本的算术命令和符号数学命令。然而,代码目前给我一个奇怪的类型错误。我觉得可能会出现这种情况,因为有两种不同的变体使用binop类型,但我不确定。
open Core.Std
type binop =
|Add
|Subtract
|Multiply
|Divide
let apply_binop_int a b = function
|Add -> a + b
|Subtract -> a - b
|Multiply -> a * b
|Divide -> a / b
let rec apply_binop a b op =
match (a,b) with
|ExprTerm (Const a), ExprTerm (Const b) -> apply_binop_int a b op
|BinopExpr (op1,a1,b1),_ -> apply_binop (apply_binop a1 b1 op1) b op
|_,BinopExpr (op1,a1,b1) -> apply_binop a (apply_binop a1 b1 op1) op
let precedence = function
|Add |Subtract -> 0
|Multiply |Divide -> 1
type term =
|Const of int
|Var of string
type token =
|Term of term
|Operator of binop
type expr =
|ExprTerm of term
|BinopExpr of binop * expr * expr
let operator_of_string = function
|"+" -> Add
|"-" -> Subtract
|"*" -> Multiply
|"/" -> Divide
|_ -> failwith "Unidentified operator"
let token_of_string s =
try Term (Const (int_of_string s))with
|_ -> Operator (operator_of_string s)
let tokens s =
String.split ~on:' ' s
|> List.map ~f:token_of_string
let process_operator ops exprs =
match (ops,exprs) with
|op::op_tl,b::a::expr_tl -> op_tl,(BinopExpr (op,a,b))::expr_tl
|_,_ -> failwith "Malformed expression"
let rec pop_stack (ops,exprs) =
match (ops,exprs) with
|_::_, _::_::_ -> pop_stack (process_operator ops exprs)
|_,[x] -> x
|_,_ -> failwith "Malformed expression"
let build_expr ts =
let rec aux ops exprs toks =
match (toks,ops) with
|Term t::tl,_ -> aux ops ((ExprTerm t)::exprs) tl
|Operator op2::tl,op::_ when precedence op >= precedence op2 ->
let ops,exprs = process_operator ops exprs in
aux ops exprs toks
|Operator op::tl,_ -> aux (op::ops) exprs tl
|[],_ -> pop_stack (ops,exprs) in
aux [] [] ts
let expr s = build_expr (tokens s)
let rec eval = function
|BinopExpr (op,a,b) ->
apply_binop (eval a) (eval b) op
|ExprTerm t -> t
我得到的错误:
utop # #use "calc.ml";;
type binop = Add | Subtract | Multiply | Divide
val apply_binop_int : int -> int -> binop -> int = <fun>
File "calc.ml", line 18, characters 63-66:
Error: This expression has type binop/1405061 but an expression was expected of type
binop/1740597
OCaml惯例是总是定义类型,然后定义在这些类型上操作的函数。另外,我喜欢在默认情况下将我的类型定义为相互递归,因为它使代码更容易阅读:
type binop =
Add of term * term
| Subtract of term * term
| Multiply of term * term
| Divide of term * term
and term =
Const of int
| Var of string;;
ivg是正确的,OCaml只允许你在程序中定义函数和类型之后使用它们,但是正如上面的例子所示,在你的定义中使用and
可以覆盖这一点。
对于这个错误:
Error: This expression has type binop/1405061 but an expression was expected of type
binop/1740597
这实际上是OCaml不变性的副作用:当你在顶层重新加载文件时,你实际上并没有改变任何第一次加载文件时建立的绑定,你只是创建了新的绑定。下面的例子演示了OCaml的这个属性:
utop # let x = 7;;
val x : int = 7
utop # let arg_times_7 arg = arg * x;;
val arg_times_7 : int -> int = <fun>
utop # arg_times_7 6;;
- : int = 42
utop # let x = 6912;;
val x : int = 6912
utop # arg_times_7 6;;
- : int = 42
简而言之,您的代码包含了许多您错过的错误,因为您正在逐步将代码发送到顶层。
例如,在apply_binop_int
中,您引用的是ExprTerm
构造函数,它没有定义(稍后将定义它,但您可以只引用之前词法上出现的定义)。这就是为什么当您加载文件时,它会给您一个错误,并且没有定义apply_binop
。但是类型是定义好的。在第二次尝试中定义了apply_binop
,因为在前一次尝试中定义了所需的类型。但是一旦定义了apply_binop
,就可以用新的定义隐藏expr
类型。