[英]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不在函数参数中。
任何对书籍,链接的引用都欢迎
pick: 'a -> 'a -> bool -> int -> int = <fun>
map2: ('a->'b->'c) -> 'a list -> 'b list -> 'c list
您在这里看到的是函数编程中称为currying的过程 。 为了理解这一点,让我们考虑一个更简单的示例。 假设您有一个函数f
,它接受2个参数X
和Y
,并输出Z
我们通常的写法是
f(X, Y) = Z
让我们以不同的方式看待这一点-我们有一个函数f
,如果给它X
,然后给Y
,它将给我们Z
如果我们仅给f
1自变量X
会发生什么? 让我们测试一下!
let plus a b = a + b;;
这段代码定义了一个plus
函数,该函数接受2个参数a
和b
并返回它们的总和。 如果键入plus 1 1;;
进入utop
,它将给您2
。 现在,键入时的输出
plus 1;;
是
- : int -> int = <fun>
这意味着plus(1)
实际上会产生一个函数,该函数接受一个int
并输出一个int
! 等一下,最初我们有一个产生整数的函数,突然之间,同一函数产生了...功能? 这是怎么回事?
这里的关键思想是将函数视为一个过程,该过程一个接一个地消耗参数 。 在这种精神的功能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个参数n
和m
,并输出函数f
接受2个参数b
和x
。 f
的定义取决于比较。 如果n > m
,则它输出一个函数fun bx
其定义是if b then x+1 else x*2
。 否则,它输出一个函数fun bx
其定义是if b then x+2 else x*4
。
如果我们根据上述理解为pick
写一个粗略的签名,它将类似于:
pick: (n, m) -> (function f that takes in b and x, and output some number)
根据我们对curring的理解, 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
,而x
和some number
应该是int
,OCaml具有一个称为类型推断的功能 。 基本上,编译器会查看您对变量执行的操作,并尝试猜测它们的类型。 例如,我查看代码中的if b
,所以b
可能应该是bool
。
总而言之 ,写函数签名的晦涩方式被称为currying ,而OCaml如何知道b
是bool
是通过OCaml中的一种称为类型推断的特征来实现的。 这应该使搜索更容易。
如果查看pick,则会看到它接受2个参数,然后返回一个函数。 它返回的是(与其他乐趣相同):
(fun b x -> if b then x+1 else x*2)
“ if”构造的形式为if <bool> then <'a> else <'a>
。 所以b必须是布尔值,而x仍然可以是'a。 更深入的是x+1
。 所以x必须int
,结果是int
了。
因此,上面的函数是bool -> int -> int
并且在pick类型中显示hat。
至于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函数是多态的(您称为泛型),可以使用许多类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.