【问题标题】:Parentheses in OcamlOcaml 中的括号
【发布时间】:2011-10-10 10:10:22
【问题描述】:

我正在评估 Ocaml 顶层的一段非常简单的代码:

let p5 () = print_int 5;;
p5 ();;

print_string "*************************";;

let p4 = print_int 4;;
p4;; 

然后它返回:

val p5 : unit -> unit = <fun>
#   5- : unit = ()
#   *************************- : unit = ()
#   4val p4 : unit = ()
#   - : unit = ()

我的问题是

  1. ()let p5 () = print_int 5;; 中是什么意思?
  2. -()# 5- : unit = () 中是什么意思?
  3. p4 是一个函数吗?
  4. 为什么# 4val p4 : unit = ()的开头有一个4
  5. 看来()可以在Ocaml代码中使用来隐藏副作用,谁能给我一个例子?

【问题讨论】:

  • 你可能想习惯使用print_endline而不是print_string,因为它会自动打印结束线

标签: ocaml


【解决方案1】:

这里有一些答案:

  1. () 是单位类型值。单位类型是只有一个值的类型。这通常用于生成不返回任何有意义或没有任何意义的函数。请记住,在 OCaml 中,所有函数总是必须返回一些内容并接受一些参数,因此使用单元类型来绕过这个限制。这类似于 C、C++ 或 Java 中的 void 类型。
  2. 有两行交错。 5print_int 函数而不是顶层打印。顶层只返回 - : unit = () 而没有 5。顶层告诉你它没有创建任何新的绑定 - 并且最后返回的值是 unit 类型并且具有值 ()
  3. 没有。它不带任何参数,因此不是函数。
  4. 再次有两行交错。 4print_int 函数打印。这时,顶层告诉你,它创建了一个新的绑定p4,这个变量携带一个unit类型的值,并且存储的值是()
  5. 不,() 不用于隐藏副作用。它通常用于创建具有副作用的函数,因此不需要任何类型的参数。

【讨论】:

    【解决方案2】:

    LiKao 已经解释了所有关键点,但我认为如果您一次输入一行定义可能会更有意义,这将显示哪些响应来自哪些输入。此人输入的行以# 开头。

    # let p5 () = print_int 5;;
    val p5 : unit -> unit = <fun>
    

    这将 p5 定义为一个函数,它接受 unit 类型的值并返回 unit 类型的值。只有一个单位类型的值,写为()。所以这些正是你要问的括号(我认为)。请注意,您的定义中出现的() 是函数接受的值的模式。作为模式,() 匹配自身(就像所有用作模式的常量一样)。

    # p5 ();;
    5- : unit = ()
    

    这有点令人困惑。 5 由您的函数 p5 编写。其余的是来自 OCaml 顶层的响应。意思是你的表达式的结果是unit 类型并且具有() 的值。有道理,print_int 的类型是 int -&gt; unit

    # print_string "*************************";;
    *************************- : unit = ()
    

    这里也有类似的混淆。星号*print_string 编写。其余部分显示结果,其类型为unit,值为()

    # let p4 = print_int 4;;
    4val p4 : unit = ()
    

    这里也一样。 4print_int 编写。其余显示顶层定义了一个名为p4 的符号,其类型为unit,其值为()。同样,这是有道理的,因为print_int 返回unit 类型,而() 是该类型的唯一值。从p4 的类型可以看出它不是一个函数。函数在类型中有一个箭头 (-&gt;)。 p4 只是 unit 类型的值。

    # p4;;
    - : unit = ()
    

    在这里,您向顶层询问p4 的类型和值,它(再次)告诉您p4 的类型为unit,并且具有() 的值。

    【讨论】:

      【解决方案3】:

      您的最后一个问题是() 如何用于“隐藏副作用”。您可能指的是函数的延迟评估。这是一个例子:

       let p x = print_string "abc";;
       let q = print_string "abc";;
      

      pq 之间有一个至关重要的区别。区别在于p'a -&gt; unit 类型的函数,而qunit 类型的值。当您定义p 时,不会打印任何内容。仅当您将函数 p 应用于参数时,即当您评估 p 1p "blah" 或其他时,才会打印字符串“abc”。 (函数p 接受任何类型的参数并忽略它们。)所以在p 的情况下,你在函数内部“隐藏了一个副作用”。

      在“p x”的定义中有一个参数“x”是没有意义的,因为根本没有使用“x”。因此,为简单起见,使用了“unit”类型,因此“p”的定义看起来像“let p () = ...”。这是一样的

       let p = fun () -> print_string "abc";;
      

      然后将函数“p”用作“p()”。如果您首先学习 C、Java 等编程语言,其中 () 用于所有函数的参数,这可能会令人困惑。但是在 OCAML 中,() 是一个特殊的符号,表示一个“空值”,而这个值有一个特殊的类型,叫做“单位”。

      当您定义 q 时会发生完全不同的事情:字符串“abc”被立即打印,因为这是评估“print_string”的副作用,并且q 等于 (),因为() 是通过评估“print_string”获得的结果值。

      【讨论】:

        【解决方案4】:

        我迟到了,但我想指出,使用() in

        let p5 () = print_int 5
        

        p5 的参数中有模式匹配。相当于这样:

        let p5 x = match x with () -> print_int 5
        

        或者这个:

        let p5 = function () -> print_int 5
        

        甚至这个:

        let p5 = fun x -> match x with () -> print_int 5
        

        这种区别在以下代码中很重要:

        let f (x, y) = print_int (x + y)
        let g x y = print_int (x + y)
        

        f 只接收一个参数((x, y) 对),而g 接收两个参数。 f 因此具有(int * int) -&gt; unit 类型,g 具有int -&gt; int -&gt; unit 类型。所以f可以这样写:

        let f pair = match pair with (x, y) -> print_int (x + y)
        

        【讨论】:

          猜你喜欢
          • 2012-09-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多