【问题标题】:@@ and |> operators precedence in OCamlOCaml 中的 @@ 和 |> 运算符优先级
【发布时间】:2020-08-04 19:30:00
【问题描述】:

我正在尝试了解 2 个 OCaml 运算符:@@|>

我知道x |> f 只是f(x),但它为什么存在呢?我不明白为什么。 @@ 也是如此,据我了解,这只是普通的功能应用程序

例如:

 match get_ipv4_hlen_version buf |> version with
      | 0x40 -> Ok buf
      | n -> Error (Printf.sprintf "IPv4 presented with a packet that claims a different IP version: %x" n)

为什么不直接写get_ipv4_hlen_version version buf

怎么样

let options_len = nearest_4 @@ Cstruct.len t.options

为什么不let options_len = nearest_4 Cstruct.len t.options

?

我想这与优先级有关,我记得一些来自 Haskell 的东西,但我不知道 Haskell,我只是在某处读过。

我如何知道事物的优先级?

如果需要更多上下文,这两个代码来自https://github.com/mirage/mirage-tcpip/blob/master/src/ipv4/ipv4_packet.ml

【问题讨论】:

标签: functional-programming ocaml


【解决方案1】:

|> 的符号值仅在您有多个嵌套函数应用程序时才会出现。很多人发现这个:

 x |> f a |> g b c |> h d

比这更容易阅读:

 h d (g b c (f a x))

因为不再需要在心理上匹配括号,并且运算是按从左到右的顺序应用的(这对于英语和其他从左到右语言的读者来说可以说是很自然的)。

如果您熟悉 Unix 命令行,将 |> 运算符视为类似于 Unix 管道运算符 | 可能会有所帮助。

@@ 这样的低优先级函数应用运算符也有助于避免括号(及其心理匹配)。很多人发现这个:

f x @@ g a b @@ h c d

比这更容易阅读:

f x ((g a b) (h c d))

@@ 的示例是错误的。这个

let options_len = nearest_4 @@ Cstruct.len t.options

等价于:

let options_len = nearest_4 (Cstruct.len t.options)

和你写的不等价。

运算符的优先级由其第一个字符决定。这又由 OCaml 手册的Section 7.7.1 中的表定义。

(当然,您需要非常仔细地阅读表格前面的文字才能看到优先规则。)

更新

完全披露:我从不在自己的代码中使用|>@@。几个括号我没问题,我一般用let把一个大的表达式分解成小块。

【讨论】:

  • 你说的 'f x @@ g a b @@ h c d' 和 'f x ((g a b) (h c d))' 一样吗?不是和'f x (g a b (h c d))'一样吗?所以也许它不是那么容易阅读。
  • 好吧,你说得有道理!我测试了我的例子,所以我认为它是正确的。我相信这一点仍然有效(也许对于更简单的表达方式)。
  • 另外,f x ((g a b) (h c d))f x (g a b (h c d)) 是同一个表达式,那些括号是不必要的:-)
  • @PatJ 不是第一个应用部分g?不像第二个?当假设ac 有副作用时,这可能会有所不同。
  • @ghilesZ 由于未指定函数参数的求值顺序,并且由于 ocaml 的自动部分应用程序,我转而认为,就语言而言,带有附加括号的版本并且没有的在所需行为方面是等效的。但是在语法(关联性和优先级)方面,f x (g a b (h c d))f x @@ g a b @@ h c d 是完全相同的。
【解决方案2】:

|> 操作符非常方便。它相当于外壳中的管道。它允许您编写如下代码:

let make_string n = 
  Array.init n float_of_int
  |> Array.map (fun x -> x -. 0.5 *. (float_of_int (n-1))) 
  |> Array.map (fun x -> Printf.sprintf "-- %10.6f --" x)
  |> Array.to_list
  |> String.concat "\n"
in
make_string 5

(* Output:
--  -2.000000 --
--  -1.000000 --
--   0.000000 --
--   1.000000 --
--   2.000000 --
*)

在本例中,以|> 开头的每一行都获取上一个转换的输出,因此我们可以看到数据转换的流程,就像在 Bash 中编写类似的东西时一样

ls | grep txt | sort | uniq

@@ 运算符是“反向管道”。它允许删除会使代码可读性降低的括号。例如,假设我们想要制作一个矩阵产品链,如 C = A.B.C.D。您希望代码与数学公式一致,因此您希望以相同的顺序编写它。如果mm A B做A和B的矩阵相乘,那么我们可以写

let mat_C = 
   mm mat_A @@ mm mat_B @@ mm mat_C mat_D

而不是

let mat_C = 
   mm mat_A (mm mat_B (mm mat_C mat_D))

【讨论】:

  • 在管道的情况下,我可以简单地将函数颠倒过来:Array.map ... String.concat "\n"String.concat "\n" ... Array.map rigth?但是管道方式更容易阅读?
猜你喜欢
  • 2011-06-21
  • 2020-03-05
  • 1970-01-01
  • 2021-03-29
  • 2013-07-31
  • 1970-01-01
  • 2011-09-16
  • 2014-11-09
  • 2021-03-16
相关资源
最近更新 更多