简体   繁体   English

输入函数式编程(OCaml)

[英]Type in functional programming (OCaml)

I am learning functional programming and I am not understanding the types in OCaml, and I have not found anything real helpful. 我正在学习函数式编程,并且不了解OCaml中的类型,并且我还没有发现真正有用的东西。

I have this code: 我有以下代码:

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];;

What I find difficult to understand the steps to know the type of this kind of functions (map2, pick). 我发现很难理解了解此类功能类型的步骤(map2,pick)。

I know a little bit how signature works, the right associative property and that the symbol " ' " refers to a generic type. 我知道一点点的签名是如何工作的,正确的关联属性和符号“'”指的是一个泛型类型。

solution: 解:

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

I don't understand why bool -> int and why in map bool isn't in the function parameter. 我不明白为什么bool-> int以及为什么map bool不在函数参数中。

Any reference to books, link, is welcomed 任何对书籍,链接的引用都欢迎

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

What you see here is a process in functional programming called currying . 您在这里看到的是函数编程中称为currying的过程 To make sense of this, let's consider a simpler example. 为了理解这一点,让我们考虑一个更简单的示例。 Let's say you have a function f that takes 2 arguments X and Y , and output Z . 假设您有一个函数f ,它接受2个参数XY ,并输出Z How we usually write this is 我们通常的写法是

f(X, Y) = Z

Let's see this in a different way - we have a function f , and if we give it X , and then Y , it will gives us Z . 让我们以不同的方式看待这一点-我们有一个函数f ,如果给它X ,然后给Y ,它将给我们Z What would happen if we only give f 1 argument, X ? 如果我们仅给f 1自变量X会发生什么? Let's test it out! 让我们测试一下!

let plus a b = a + b;;

This code defines a function plus , which takes 2 arguments a and b and returns their sum. 这段代码定义了一个plus函数,该函数接受2个参数ab并返回它们的总和。 If you type plus 1 1;; 如果键入plus 1 1;; into utop , it will give you 2 . 进入utop ,它将给您2 Now, the output when you type 现在,键入时的输出

plus 1;;

is

- : int -> int = <fun>

This means plus(1) actually produces a FUNCTION that takes an int and output an int ! 这意味着plus(1)实际上会产生一个函数,该函数接受一个int并输出一个int Wait a minute, initially we have a function that produces integer, and suddenly the same function is producing ... FUNCTION? 等一下,最初我们有一个产生整数的函数,突然之间,同一函数产生了...功能? What's going on? 这是怎么回事?

The key idea here is to think of a function as a process that consumes the arguments one by one . 这里的关键思想是将函数视为一个过程,该过程一个接一个地消耗参数 In the spirit of this, the function plus above is like a process that consumes 2 arguments: if you give it just 1 argument, it would stall and wait for the 2nd argument. 在这种精神的功能plus上面就是这样消耗2个参数的方法:如果你给它只是1周的说法,这将拖延和等待第二个参数。 And this stalled process is exactly similar to a process that consumes 1 argument: give it the remaining ingredient and it would start grinding to give you the expected output. 这个停顿的过程与消耗1个参数的过程完全相似:给它剩余的成分,它将开始磨碎以提供预期的输出。

To see how this perspective can help you make sense of the obscure way in which function signature is written in your example, let's look at the function pick : 为了了解这种观点如何帮助您理解示例中编写函数签名的晦涩方式,让我们看一下函数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 takes in 2 arguments, n and m , and output a function f that takes in 2 arguments b and x . pick接受2个参数nm ,并输出函数f接受2个参数bx The definition of f depends on a comparison. f的定义取决于比较。 If n > m , then it outputs a function fun bx whose definition is if b then x+1 else x*2 . 如果n > m ,则它输出一个函数fun bx其定义是if b then x+1 else x*2 Else, it outputs a function fun bx whose definition is if b then x+2 else x*4 . 否则,它输出一个函数fun bx其定义是if b then x+2 else x*4

If we were to write a rough signature for pick based on the above understanding, it would be something like: 如果我们根据上述理解为pick写一个粗略的签名,它将类似于:

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

In light of our understanding of currying, pick is like a process that consumes n , and then m . 根据我们对curring的理解, pick就像一个消耗n ,然后消耗m So the signature can be written in this way too: 所以签名也可以这样写:

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

Oh hey, this function f can also be thought of as a process that consumes b and then x , so we can also write: 哦,嘿,这个函数f也可以看作是一个先消耗b然后再消耗x的进程,所以我们也可以这样写:

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

which looks strikingly similar to: 看起来非常类似于:

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

Now, as of how the heck does OCaml know that b is supposed to be bool , and x and some number is supposed to be int , OCaml has a featured called type inference . 现在,关于OCaml到底是怎么知道b应该是bool ,而xsome number应该是int ,OCaml具有一个称为类型推断的功能 Basically speaking, the compiler looks at the operations you perform on the variables and try to make a guess of their types. 基本上,编译器会查看您对变量执行的操作,并尝试猜测它们的类型。 Eg I see if b in the code, so probably b should be a bool . 例如,我查看代码中的if b ,所以b可能应该是bool

In summary , that obscure way in which the function signature is written is called currying , and how OCaml knows b is a bool is through a feature called type inference in OCaml. 总而言之 ,写函数签名的晦涩方式被称为currying ,而OCaml如何知道bbool是通过OCaml中的一种称为类型推断的特征来实现的。 This should makes it easier for searching. 这应该使搜索更容易。

If you look at pick then you see that it takes 2 argument and then returns a function. 如果查看pick,则会看到它接受2个参数,然后返回一个函数。 What it returns is (same for the other fun): 它返回的是(与其他乐趣相同):

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

The "if" construct is of the form if <bool> then <'a> else <'a> . “ if”构造的形式为if <bool> then <'a> else <'a> So b must be bool while x can still be 'a. 所以b必须是布尔值,而x仍然可以是'a。 Going deeper there is x+1 . 更深入的是x+1 So x must be int and the result is int too. 所以x必须int ,结果是int了。

So the above function is bool -> int -> int and hat is shown in the type of pick. 因此,上面的函数是bool -> int -> int并且在pick类型中显示hat。

As for map2: The map2 function can work with any function of the form 'a -> 'b -> 'c as first argument. 至于map2:map2函数可以使用形式为'a -> 'b -> 'c任何函数作为第一个参数。 In your example you pass bool -> int -> int but this does not limit map2 to that type. 在您的示例中,您传递了bool -> int -> int但这并不将map2限制为该类型。 Your code could continue with 您的代码可以继续

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>

The same map2 function is used but here with bool -> float -> float as type of the first argument. 使用相同的map2函数,但此处将bool -> float -> float作为第一个参数的类型。 The map2 function is polymorphic (what you called generic) and can work with many types. map2函数是多态的(您称为泛型),可以使用许多类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM