如何解决Haskell:无咖喱,咖喱,成分
输入此代码时,我对理解编译器的工作存在疑问:
(curry . uncurry) (+) 1 2
据我了解,编译器首先使用uncurry,这意味着会发生错误,因为uncurry函数需要这样的输入:
(curry . uncurry) (+) (1,2)
但是显然第一个是对的。我不明白为什么。
编译器对此进行评估时究竟要采取什么步骤?
(uncurry . curry) (+) (1,2)
不起作用?
解决方法
据我了解,编译器不费吹灰之力。
否,(curry . uncurry)
如果我们评估(curry . uncurry)
函数,我们将看到以下缩写:
\x -> curry (uncurry x)
所以(curry . uncurry) (+) 1 2
函数的缩写为:
(\x -> curry (uncurry x)) (+) 1 2
或因此:
(curry (uncurry (+))) 1 2
和uncurry :: (a -> b -> c) -> (a,b) -> c
和curry :: ((a,b) -> c) -> a -> b -> c
从而转换一个函数。因此,这意味着uncurry (+)
实际上期望一个2元组:
uncurry (+) :: Num a => (a,a) -> a
但现在通过curry
函数传递此函数:
curry (uncurry (+)) :: Num a => a -> a -> a
因此curry
函数撤消了uncurry
对(+)
函数的转换。因此,这意味着curry (uncurry (+))
与(+)
相同,因此:
(curry (uncurry (+))) 1 2
等效于:
(+) 1 2
,因此等同于3
。
curry . uncurry
并不完全等同于id
,因为它具有以下类型:
curry . uncurry :: (a -> b -> c) -> a -> b -> c
因此,这意味着它将功能限制为类型a -> (b -> c)
的功能。
您的表达式解析为
(((curry . uncurry) (+)) 1) 2
因此,它将构建函数(curry . uncurry) (+)
并将其应用于1
,然后将结果函数应用于2
。
因此,我们从(curry . uncurry) (+)
开始,这意味着curry (uncurry (+))
。为简单起见,假设(+)
的实现方式为
(+) = \x y -> ...
请注意,上面是一个咖喱函数,以x
然后以y
作为单独的参数。然后我们有:
curry (uncurry (+))
= { definition + }
curry (uncurry (\x y -> ...)) -- note the separate arguments
= { definition uncurry }
curry (\(x,y) -> ...) -- only one pair argument
= { definition curry }
(\x y -> ...) -- back to two arguments
= { definition + }
(+)
以上,uncurry
将函数+
转换为一个接受单个对参数而不是两个对的参数。 curry
颠倒了这种转换过程,它将对参数分成两个独立的参数。
实际上,curry . uncurry
是对二进制咖喱函数的身份转换,因为它应用了转换及其逆运算。因此,它对结果或所涉及函数的类型没有影响。
我们得出结论,您的表达式等同于((+) 1) 2
,其结果为3
。
这个问题得到了很好的答案,但我想添加关于curry . uncurry
的注释。
您可能听说过SKI
-微积分。如果您还没有,那是我们与3个组合器一起工作的一个演算:
Sabc = ac(bc)
Kab = a
Ia = a
众所周知,图灵满满。
此外,I
组合器是多余的。让我们尝试根据S
和K
组合器写下来。
主要思想是应将I的唯一参数作为K
的第一个参数传递,该参数的第二个参数被占用。正是S
组合器所做的:如果将K
作为S
的第一个参数传递,我们将让它返回S
的第三个参数:
SKbc = Kc(bc) = c
我们已经证明(K*ab = b
):
K* = SK
因此,我们只需要选择第二个参数:K
和S
都可以:
I = SKK = SKS
正如人们所看到的,组合器I
对应于id
;
组合器K
对应于const;
组合器S
对应于(<*>) :: (e -> a -> b) -> (e -> a) -> e -> b
。
I = SKK
对应于const <*> const :: a -> a
。
由于输入以下内容,我们的其他结果(I = SKS
和K* = SK
在Haskell中不成立:
GHCi> :t (<*>) const (<*>) {- id -}
(<*>) const (<*>) {- id -}
:: Applicative f => f (a -> b) -> f (a -> b)
GHCi> :t (const <*>) {- flip const -}
(const <*>) {- flip const -} :: (b -> a) -> b -> b
正如人们所看到的,我们的实现在它们的域中充当必需的功能,但是我们缩小了域的范围。
如果我们向读者指定(<*>) const (<*>)
,我们将得到您记为curry . uncurry
-id
的函数。
curry . uncurry
的另一种方法是($)
:
f $ x = f x
($) f x = f x
($) f = f
($) = id
您发现的功能非常有趣-寻找其他类似工作可能是一个好习惯(我不知道那里是否还有其他值得注意的工作)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。