输入函数式编程 (OCaml)

Type in functional programming (OCaml)

我正在学习函数式编程,我不了解 OCaml 中的类型,而且我没有发现任何真正有用的东西。

我有这个代码:

let rec map2 f l1 l2 =
          match (l1,l2) with
          ([], _) -> []
        | (_, []) -> []
        | (x::l1s,y::l2s) -> (f x y) :: map2 f l1s l2s;;


let pick n m =
      if n > m then (fun b x -> if b then x+1 else x*2)
      else (fun b x -> if b then x+2 else x*4);;

map2 (pick 7 9) [true;false] [3;4;5;6];;

我觉得很难理解的步骤来了解这种函数的类型(map2,pick)。

我知道一点签名的工作原理,正确的关联 属性 和符号“'”指的是泛型。

解决方案:

pick: 'a -> 'a -> bool -> int -> int = <fun>
map2: ('a->'b->'c) -> 'a list -> 'b list -> 'c list

我不明白为什么 bool -> int 以及为什么 map 中的 bool 不在函数参数中。

欢迎参考任何书籍,link

pick: 'a -> 'a -> bool -> int -> int = <fun>
map2: ('a->'b->'c) -> 'a list -> 'b list -> 'c list

您在这里看到的是函数式编程中的一个过程,称为 currying。为了理解这一点,让我们考虑一个更简单的例子。假设您有一个函数 f,它接受 2 个参数 XY,并输出 Z。我们通常这样写是

f(X, Y) = Z

让我们以不同的方式来看这个 - 我们有一个函数 f,如果我们给它 X,然后 Y,它会给我们 Z.如果我们只给 f 1 个参数 X 会发生什么?让我们来测试一下!

let plus a b = a + b;;

此代码定义了一个函数 plus,它接受 2 个参数 ab 以及 returns 它们的总和。如果你在 utop 中输入 plus 1 1;;,它会给你 2。现在,输入

时的输出
plus 1;;

- : int -> int = <fun>

这意味着 plus(1) 实际上产生了一个接受 int 并输出 int 的函数!等一下,最初我们有一个生成整数的函数,突然之间相同的函数正在生成... FUNCTION?怎么回事?

这里的关键思想是将函数视为一个一个地消耗参数的过程。本着这种精神,上面的函数 plus 就像一个消耗 2 个参数的进程:如果你只给它 1 个参数,它会停止并等待第二个参数。这个停滞的过程与消耗 1 个参数的过程完全相似:给它剩余的成分,它会开始研磨以提供预期的输出。

要了解此观点如何帮助您理解示例中函数签名的晦涩编写方式,让我们看一下函数 pick:

let pick n m =
  if n > m then (fun b x -> if b then x+1 else x*2)
  else (fun b x -> if b then x+2 else x*4);;

pick 接受 2 个参数,nm,并输出一个接受 2 个参数 b 和 [=46] 的函数 f =]. f的定义依赖于比较。 如果n > m,那么它输出一个定义为if b then x+1 else x*2的函数fun b x。 否则,它输出一个定义为 if b then x+2 else x*4.

的函数 fun b x

如果按照上面的理解,给pick写一个粗略的签名,大概是这样的:

pick: (n, m) -> (function f that takes in b and x, and output some number)

根据我们对柯里化的理解,pick 就像一个消耗 n,然后 m 的过程。所以签名也可以这样写:

pick: n -> m -> (function f that takes in b and x, and output some number)

哎呀,这个函数f也可以认为是一个过程先消费b再消费x,所以我们也可以这样写:

pick: n -> m -> (b -> x -> some number)

与以下内容惊人相似:

pick: 'a -> 'a -> bool -> int -> int = <fun>

现在,OCaml 到底是怎么知道 b 应该是 bool,而 xsome number 应该是 int, OCaml 有一个叫做 type inference 的特性。基本上来说,编译器会查看您对变量执行的操作并尝试猜测它们的类型。例如。我在代码中看到 if b,所以 b 可能应该是 bool.

总而言之,函数签名的那种晦涩的写法叫做currying,而OCaml是怎么知道的b 是一个 bool 是通过 OCaml 中称为 类型推断 的功能。这样应该更容易搜索。

如果您查看 pick,您会发现它需要 2 个参数,然后 returns 一个函数。 returns 是什么(其他乐趣也一样):

(fun b x -> if b then x+1 else x*2)

"if" 构造的形式为 if <bool> then <'a> else <'a>。所以 b 必须是 bool 而 x 仍然可以是 'a。再往深一点就是x+1。所以 x 必须是 int,结果也是 int

所以上面的函数就是bool -> int -> int并且帽子在pick类型中显示

至于 map2:map2 函数可以与任何形式为 'a -> 'b -> 'c 的函数一起作为第一个参数。在您的示例中,您传递 bool -> int -> int 但这并不会将 map2 限制为该类型。您的代码可以继续

let pick_f n m =
      if n > m then (fun b x -> if b then x +. 1. else x *. 2.)
      else (fun b x -> if b then x +. 2. else x *. 4.);;

map2 (pick_f 7 9) [true;false] [3.;4.;5.;6.];;

pick_f: 'a -> 'a -> bool -> float -> float = <fun>

使用相同的 map2 函数,但这里使用 bool -> float -> float 作为第一个参数的类型。 map2 函数是多态的(您称之为泛型)并且可以处理多种类型。