blue {
width: 100px;
height: 100px;
background: blue;
float: left;
position: relative;
}
#red {
width: 100px;
height: 50px;
background: red;
float:right;
}
#green {
width: 100px;
height: 50px;
background: green;
float: right;
clear: both;
}
function :: (t2 -> t1) -> (t1 -> t) -> t2 -> t
function f1 f2 x = f2 (f1 x )
function1 :: (t -> t) -> t -> t
function1 f1 x = f1 (f1 x)
任何人都可以解释前两个的类型签名以及为什么最后3个的类型签名是相同的吗?我正在阅读有关高阶函数的内容以及它们没有进入我的脑海。
答案 0 :(得分:4)
有人可以解释前两个的类型签名吗?
让我们为类型变量提供一些人类可读的名称。
function :: (in -> tmp) -> (tmp -> out) -> in -> out
function f1 f2 x = f2 (f1 x )
这种类型说:如果你给我一个功能,将输入输入到临时值,以及一个将临时值输入输出的功能,我会给你一个功能,将输入输入到输出中。您可以想象绘制一个"数据处理管道",其中我们有一堆各种类型的对象,以及将一种类型的值转换为另一种类型的值的操作。例如,在这里我可以直观地绘制您的function
:
in ----- f1 -----> tmp ----- f2 -----> out
\ ^
\ /
`------------- function f1 f2 --------------'
这里的预期解释是,您可以想象在此管道中采用两条路线 - 您首先使用操作f1
,然后使用操作f2
;或者你可以使用function f1 f2
操作一次完成整个事情。
function1 :: (t -> t) -> t -> t
function1 f1 x = f1 (f1 x)
此处,function1
采用函数将t
s转换为其他t
s,并返回一个(可能不同的)函数来转换t
s(再次,进入其他t
s。再次,为此绘制数据处理管道,您可能会得到这样的结果:
t ----- f1 -----> t ----- f1 -----> t
\ ^
\ /
`------------- function1 f1 --------------'
为什么最后3个的类型签名相同?
让我们快速了解最后三个函数的定义:
function2 f1 f2 x = f1 (f1 x)
function3 f1 f2 x = f1 (f1 (f1 x))
function4 f1 f2 x = f1 (f1 (f1 x))
好的,关于function2
,function3
和function4
的第一个有趣的事情是它们有三个参数,f1
,f2
和{ {1}},但是每个人都完全忽略了x
- 它们在f2
的右侧从未被提及过。其次,=
和function3
以完全相同的方式定义,因此它们可以被赋予相同的类型并不奇怪。
考虑到这两个方面,我们可以稍微缩小一下这个问题,以便我们问为什么这两个函数都有类型签名:
function4
具体来说,他们的共享类型签名是:
function2' f x = f (f x)
function3' f x = f (f (f x))
正如我们上面针对(t -> t) -> (t -> t)
所讨论的那样,这意味着它们会在function1
上进行转换,并且它们也会在t
上产生转换。再次考虑数据处理流水线,现在应该很清楚为什么它们具有相同的类型:我们是否在管道中应用我们的函数t
两次或三次并不重要;这种类型只是改变:
f
假设 .------------------------ function3' f ------------------------.
/ \
/ v
t ----- f -----> t ----- f -----> t ----- f -----> t
\ ^
\ /
`------------ function2' f -------------'
是一个从标有f
的节点开始并在标有t
的节点处结束的函数,那么无论我们在停止之前应用t
多少次,我们总是回到一个函数,该函数从标有f
的节点开始,到标有t
的节点结束。
答案 1 :(得分:2)
当一个函数将其他函数作为输入参数或将其他函数作为值返回时,它被称为高阶函数。
在第一种情况下,名为function
的函数有两个函数
类型(t2 -> t1)
和(t1 -> t)
以及类型t2
的值。您可以
与ghci
一起玩,以更好地了解它的作用:
λ> :t id
id :: a -> a
λ> id 3
3
λ> function id id 3
3
λ> function id (\x -> x + 3) 3
6
λ> function (\x -> x + 3) (\x -> x + 3) 3
9
在function1
中,您将单个函数和t
类型的值视为。{
输入
为什么最后3个的类型签名是相同的?
因为他们采取相同的输入。编译器非常智能,可以推断出您传递的第二个参数是类型t -> t1
的函数,即使您没有给出任何括号。我建议你给括号,因为它提高了可读性。
Daniel Wagner指出,你在前三个定义中没有使用f2
输入。所以,你甚至可以传递一个底值,它不需要是一个函数:
λ> function4 id undefined "hello"
"hello"