【发布时间】:2021-03-17 20:28:44
【问题描述】:
我正在学习 OCaml,我正在阅读的文档和书籍在某些方面不是很清楚。
简单来说,两者有什么区别
-10
和
~-10
在我看来,它们似乎是一样的。我遇到过其他资源试图解释这些差异,但它们似乎用我还不熟悉的术语来解释,因为到目前为止我唯一知道的是变量。
【问题讨论】:
我正在学习 OCaml,我正在阅读的文档和书籍在某些方面不是很清楚。
简单来说,两者有什么区别
-10
和
~-10
在我看来,它们似乎是一样的。我遇到过其他资源试图解释这些差异,但它们似乎用我还不熟悉的术语来解释,因为到目前为止我唯一知道的是变量。
【问题讨论】:
事实上,- 是一个二元运算符,所以某些表达式可能是模棱两可的:f 10 -20 被视为(f 10) - 20。例如,让我们想象一下这个虚拟函数:
let f x y = (x, y)
如果我想生成元组(10, -20),我会天真地写类似f 10 -20 的东西,这会导致我出现以下错误:
# f 10 -20;;
Error: This expression has type 'a -> int * 'a
but an expression was expected of type int
因为表达式被评估为(f 10) - 20(所以是一个函数的减法!!)所以你可以这样写表达式:f 10 (-20),这是有效的或f 10 ~-20,因为~-(和@987654332 @ 和 ~-. ~+. 用于浮点数)是一元运算符,正确遵守前置。
【讨论】:
- 并不总是二进制的,例如,在1 - -2 中,- 同时出现为一元和二进制(OCaml 用语中的前缀或中缀)
从查看用户定义的一元 (~-) 运算符的工作原理开始会更容易。
type quaternion = { r:float; i:float; j:float; k:float }
let zero = { r = 0.; i = 0.; j = 0.; k = 0. }
let i = { zero with i = 1. }
let (~-) q = { r = -.q.r; i = -.q.i; j = -. q.j; k = -. q.k }
在这种情况下,当解析明确时,一元运算符-(和+)是~-(和~+)的简写。例如,用
-i
let mi = -i
有效,因为这个- 不能是二元运算符-。
尽管如此,二元运算符的优先级高于一元-,因此
let wrong = Fun.id -i
读作
let wrong = (Fun.id) - (i)
在这种情况下,我可以使用完整形式 ~-
let ok' = Fun.id ~-i
或添加一些括号
let ok'' = Fun.id (-i)
回到文字类型(例如整数或浮点数),对于这些类型,一元 + 和 - 符号可以是文字本身的一部分(例如 -10),而不是运算符。例如重新定义 ~- 和 ~+ 不会改变整数文字的含义
let (~+) = ()
let (~-) = ()
let really = -10
let positively_real = +10
这可以“用来”创建一些相当模糊的表达式:
let (~+) r = { zero with r }
let (+) x y = { r = x.r +. y.r; i = x.i +. y.i; k = x.k +. x.k; j =x.k +. y.j }
let ( *. ) s q = { r = s *. q.r; i = s *. q.i; j = s *. q.j; k = s *. q.k }
let this_is_valid x = +x + +10. *. i
【讨论】:
OCaml 有两种operators - 前缀和中缀。前缀运算符 precede 表达式和中缀出现在两个表达式之间 in,例如,在!foo 中,我们在表达式 foo 之前有前缀运算符 !在2 + 3 中,我们在表达式2 和3 之间有中缀运算符+。
运算符与普通函数类似,只是它们具有不同的应用程序语法(也称为调用),而函数使用简单的并置语法应用于任意数量的参数,例如f x1 x2 x3 x41 sup>,运算符只能有一个(前缀)或两个(中缀)参数。前缀运算符非常接近普通函数,参见f x 和!x,但它们比普通函数应用具有更高的优先级(绑定更紧密)。相反,中缀运算符,因为它们放在两个表达式之间,所以可以实现更自然的语法,例如,x + y 与 (+) x y,但比普通函数应用程序具有更低的优先级(绑定不那么紧)。此外,它们可以将多个运算符串联成一行,例如,x + y + z 被解释为(x + y) + z,这比add (add (x y) z) 更具可读性。
OCaml 中的运算符纯粹在语法上进行区分。这意味着运算符的类型完全由该运算符的第一个字符定义,而不是由特殊的编译器指令定义,就像在其他一些语言中那样(即 OCaml 中没有 infix + 指令)。如果运算符以prefix-symbol 序列开头,例如!、?@、~%,则将其视为前缀,如果以infix-symbol 开头,则相应地是中缀运算符。
- 和-. 运算符被特殊处理,可以作为前缀和中缀出现。例如,在1 - -2 中,- 出现在中缀和前缀位置。但是,只有当-(和-.)运算符与其他运算符(中缀或前缀)一起出现时,才有可能消除中缀和前缀版本的歧义,但是当我们有一个通用表达式时,@ 987654349@ 运算符被视为中缀。例如,max 0 -1 被解释为(max 0) - 1(请记住,运算符的优先级低于函数应用程序,因此当它们两个不带括号出现时,首先应用函数,然后应用运算符)。另一个例子,Some -1,它被解释为Some - 1,而不是Some (-1)。要消除此类代码的歧义,您可以使用括号,例如 max 0 (-1),或仅使用前缀的版本,例如 Some ~-1 或 max 0 ~-1。
就个人风格而言,我实际上更喜欢括号,因为在阅读代码时通常很难记住这些规则。
1) 纯粹主义者会说 OCaml 中的函数只能有一个参数,而 f x1 x2 x3 x4 只是 ((f x1) x2) x3) x4,这是一个完全正确的说法,但与当前的说法有点无关讨论。
【讨论】: