输入函数式编程 (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 个参数 X
和 Y
,并输出 Z
。我们通常这样写是
f(X, Y) = Z
让我们以不同的方式来看这个 - 我们有一个函数 f
,如果我们给它 X
,然后 Y
,它会给我们 Z
.如果我们只给 f
1 个参数 X
会发生什么?让我们来测试一下!
let plus a b = a + b;;
此代码定义了一个函数 plus
,它接受 2 个参数 a
和 b
以及 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 个参数,n
和 m
,并输出一个接受 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
,而 x
和 some 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 函数是多态的(您称之为泛型)并且可以处理多种类型。
我正在学习函数式编程,我不了解 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 个参数 X
和 Y
,并输出 Z
。我们通常这样写是
f(X, Y) = Z
让我们以不同的方式来看这个 - 我们有一个函数 f
,如果我们给它 X
,然后 Y
,它会给我们 Z
.如果我们只给 f
1 个参数 X
会发生什么?让我们来测试一下!
let plus a b = a + b;;
此代码定义了一个函数 plus
,它接受 2 个参数 a
和 b
以及 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 个参数,n
和 m
,并输出一个接受 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
,而 x
和 some 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 函数是多态的(您称之为泛型)并且可以处理多种类型。