如何在 F# 中使用可区分联合编写算术表达式?

How to write a arithmetic expression using a discriminated union in F#?

最近,我开始学习 F#,但在受歧视的联合和函数签名方面有些挣扎。

我正在尝试将算术表达式(求和、乘积、乘法、平均值等)定义为判别并集,并编写一个对其求值的函数。 但是,我无法弄清楚我做错了什么。谁能指出我正确的方向?这是我到目前为止尝试过的:

尝试 1

type Expr = 
    | Sum of int * int
    | Avg of int * int
    | Mul of int * int

let Evaluate (input : Expr)  =
    match input with
    | Sum(a,b) -> a + b

printfn "%A" (Sum(5,10))

我的输出是:

Sum (5,10)

尝试 2

我也试过类似的东西:

type Expr = int -> int -> int

let evaluate (a : Expr) (b : Expr) = 
    match a,b with
    | a,b -> (fun a -> a + b)

printfn "%A" (evaluate 5 10)

因为所有常见的算术表达式(如求和、乘积、乘法、平均值等)都采用两个整数输入并输出一个整数。

我收到以下错误:'This expression was expected to have type Expr but here has type Int'。

编辑 1

let evaluate (input : Expr)  =
    match input with
    | Sum(a,b) -> a + b
    | Avg(a,b) -> a + b / 2
    | Mul(a,b) -> a * b

let evaluate = function
    | Sum(a,b) -> a + b
    | Avg(a,b) -> a + b / 2
    | Mul(a,b) -> a * b

尝试 1 看起来是正确的,但是有限制,因为它不允许您创建像 1+2+3.

这样的表达式

相反,您可以尝试这样的操作:

type Expr =
  | Constant  of int
  | Add       of Expr*Expr
  | Sub       of Expr*Expr
  | Mul       of Expr*Expr
  | Div       of Expr*Expr

更灵活一些,支持 xy 等绑定:

type Expr =
  | Constant  of int
  | Binding   of string
  | BinaryOp  of string*(int -> int -> int)*Expr*Expr
  | UnaryOp   of string*(int -> int)*Expr

你的第一次尝试接近了。

type Expr = 
    | Sum of int * int
    | Avg of int * int
    | Mul of int * int

let evaluate = function
    | Sum(a,b) -> a + b
    | Avg(a,b) -> a + b / 2
    | Mul(a,b) -> a * b

如评论所述,您遗漏了表达式的计算。 也没有理由使用 printfn "%A",因为我们知道 return 类型。

Sum(5,10)        // Expr
|> evaluate      // int
|> printfn "%i"  // unit

在你的第二次尝试中,你有点搞混了。

type Expr = int -> int -> int

正如 Lee(再次)正确指出的那样,此签名表示具有 2 个参数的函数。

let add : Expr = fun a b -> a + b

或shorthand

let add : Expr = (+)

由于Expr表示一个算术运算符,下面的求值函数将它们结合起来。

let evaluate (a : Expr) (b : Expr) = 
    match a,b with
    | a,b -> (fun a -> a + b)
  • 如果您打算对两个输入整数求和,a 和 b 应该是 int 类型。
  • 如果你想组合多个表达式,FuneSnabel 的建议是可行的。

例如:(1 + 2) * (3 + 4)

type Expr =
    | Cte of int
    | Add of Expr * Expr
    | Mul of Expr * Expr

let rec eval = function
    | Cte(i)   -> i
    | Add(a,b) -> eval a + eval b
    | Mul(a,b) -> eval a * eval b

Mul( Add(Cte 1,Cte 2) , Add(Cte 3,Cte 4) )
|> eval
|> printfn "%i"