Function 如何从类型签名实现函数?
我在Haskell中有以下两种类型的签名:Function 如何从类型签名实现函数?,function,haskell,types,functional-programming,implementation,Function,Haskell,Types,Functional Programming,Implementation,我在Haskell中有以下两种类型的签名: foo :: (a -> (a,b)) -> a -> [b] bar :: (a -> b) -> (a -> b -> c) -> a -> c 我想写这两个函数的具体实现,但我真的很难理解从哪里开始 我知道foo接受函数(a->(a,b))并返回a和包含b的列表 而bar接受一个函数(b->c),该函数返回一个函数(a->b->c),该函数最终返回a和c 有人能给我举一个具体实现的例子吗
foo :: (a -> (a,b)) -> a -> [b]
bar :: (a -> b) -> (a -> b -> c) -> a -> c
我想写这两个函数的具体实现,但我真的很难理解从哪里开始
我知道foo
接受函数(a->(a,b))
并返回a
和包含b
的列表
而bar
接受一个函数(b->c)
,该函数返回一个函数(a->b->c)
,该函数最终返回a
和c
有人能给我举一个具体实现的例子吗
我如何知道从何处开始,以及定义的左侧是什么?您有一些误解: 我知道
foo
接受函数(a->(a,b))
并返回a
和包含b
的列表
不,它不会返回a
。它期望它作为该函数之外的另一个参数
而bar
接受一个函数(b->c)
,该函数返回一个函数(a->b->c)
,该函数最终返回a
和c
这里也一样。给定g::a->b
,bar
返回函数bar g:(a->b->c)->a->c
。这个函数,反过来,给定一个函数h::(a->b->c)
,返回一个类型为a->c
的函数。事情就是这样
就像玩拼图一样:
foo :: (a -> (a,b)) -> a -> [b]
-- g :: a -> (a,b)
-- x :: a
-- g x :: (a,b)
foo g x = [b] where
(a,b) = g x
bar :: (a -> b) -> (a -> b -> c) -> a -> c
-- g :: a -> b
-- x :: a
-- g x :: b
-- h :: a -> b -> c
-- h x :: b -> c
-- h x (g x) :: c
bar g h x = c where
c = ....
我们在这里没有多少自由选择。尽管如此,对于foo
,有更多的方法可以获取更多类型b
的值。与其忽略(a,b)=gx
中的a
,我们可以在g
的更多应用中使用它,因此实际上有更多的可能性,比如
foo2 :: (a -> (a,b)) -> a -> [b]
foo2 g x = [b1,b2] where
(a1,b1) = g x
(a2,b2) = g a1
还有更多。尽管如此,类型还是指导了可能的实现foo
甚至可以根据以下类型在其实现中使用foo
:
foo3 :: (a -> (a,b)) -> a -> [b]
foo3 g x = b : bs where
(a,b) = g x
bs = ...
所以现在,在这个实现中,前两个成为它的特例:
foogx==take1(foo3gx)
和foo2gx==take2(foo3gx)
。有最一般的定义可能是最好的。你有一些误解:
我知道foo
接受函数(a->(a,b))
并返回a
和包含b
的列表
不,它不会返回a
。它期望它作为该函数之外的另一个参数
而bar
接受一个函数(b->c)
,该函数返回一个函数(a->b->c)
,该函数最终返回a
和c
这里也一样。给定g::a->b
,bar
返回函数bar g:(a->b->c)->a->c
。这个函数,反过来,给定一个函数h::(a->b->c)
,返回一个类型为a->c
的函数。事情就是这样
就像玩拼图一样:
foo :: (a -> (a,b)) -> a -> [b]
-- g :: a -> (a,b)
-- x :: a
-- g x :: (a,b)
foo g x = [b] where
(a,b) = g x
bar :: (a -> b) -> (a -> b -> c) -> a -> c
-- g :: a -> b
-- x :: a
-- g x :: b
-- h :: a -> b -> c
-- h x :: b -> c
-- h x (g x) :: c
bar g h x = c where
c = ....
我们在这里没有多少自由选择。尽管如此,对于foo
,有更多的方法可以获取更多类型b
的值。与其忽略(a,b)=gx
中的a
,我们可以在g
的更多应用中使用它,因此实际上有更多的可能性,比如
foo2 :: (a -> (a,b)) -> a -> [b]
foo2 g x = [b1,b2] where
(a1,b1) = g x
(a2,b2) = g a1
还有更多。尽管如此,类型还是指导了可能的实现foo
甚至可以根据以下类型在其实现中使用foo
:
foo3 :: (a -> (a,b)) -> a -> [b]
foo3 g x = b : bs where
(a,b) = g x
bs = ...
所以现在,在这个实现中,前两个成为它的特例:
foogx==take1(foo3gx)
和foo2gx==take2(foo3gx)
。拥有最一般的定义可能是最好的。除了@will-nes的答案外,将(->)视为右关联中缀运算符也很有用。所以类似于f:a->b->c
的东西与f:a->(b->c)
是一样的。也就是说,f
是一个函数,它接受类型为a
的值,并返回类型为b->c
的值,这是另一个函数,它接受类型为b
的值,并返回类型为c
的值
因此,可以按如下方式重新编写示例中的类型
foo :: (a -> (a,b)) -> (a -> [b])
bar :: (a -> b) -> ((a -> (b -> c)) -> (a -> c))
类似地,您也可以将函数的参数看作是分段的,例如左关联(如+和-),尽管在这种情况下没有显式运算符fooabcdde
与(((fooa)b)c)d)e
相同。例如,假设我们有一个函数f:Int->Int->Int
(与f:Int->(Int->Int)
相同)。您不必同时提供两个参数。因此,您可以编写g=f1
,其类型为(Int->Int)
。然后您可以为g
提供一个参数,比如g2
,它的类型为Int
F12
和让G2中的g=F1
大致相同。下面是一个更具体的例子,说明这是如何工作的:
Prelude> f = (+)
Prelude> g = f 1
Prelude> g 2
3
Prelude> :t f
f :: Num a => a -> a -> a
Prelude> :t g
g :: Num a => a -> a
Prelude> :t g 2
g 2 :: Num a => a
在@will-nes的示例实现示例中,他使用前面的所有参数定义函数,但您不必这样想。只需将f:a->b->c
想象为获取类型为a
的值并返回另一个函数。虽然您遇到的大多数方法都会预先使用它们的所有参数,但在某些情况下,您可能不想这样做。下面是一个例子:
veryExpensive :: A -> B
unstagedFun :: A -> (B -> C) -> C
unstagedFun a f = f (veryExpensive a)
stagedFun :: A -> (B -> C) -> C
stagedFun a = let b = veryExpensive a in \f -> f b
(您也可以将后者重写为让b=veryExpensive a in($b)
)
当然,对于编译器优化,如果未老化的版本自动升级,我不会感到惊讶,但希望这能提供一些动机,让人们认为函数没有多个参数,而是作为一个参数,但它们可能会返回其他函数,而这些函数本身可能会返回函数(但也只需要一个s)