Haskell: Uncurry, Curry, composition
Haskell: Uncurry, Curry, composition
当我输入以下内容时,我无法理解编译器的功能:
(curry . uncurry) (+) 1 2
根据我的理解,编译器首先使用 uncurry,这意味着会发生错误,因为 uncurry 函数需要这样的输入:
(curry . uncurry) (+) (1,2)
但显然第一个是对的。不过我不明白为什么。
编译器在评估时具体采取了哪些步骤?
另一个主题包含的问题:
为什么
(uncurry . curry) (+) (1,2)
不起作用?
After my understanding the compiler goes with uncurry.
不,它与 (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
and 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
反转,它将 pair 参数分成两个单独的参数。
确实,curry . uncurry
是二进制柯里化函数的恒等变换,因为它应用了变换及其逆。因此,它对结果或所涉及函数的类型没有影响。
我们得出结论,您的表达式等价于 ((+) 1) 2
,计算结果为 3
。
这个问题得到了很好的回答,但我想添加一个关于 curry . uncurry
的注释。
您可能听说过 SKI
-微积分。如果你还没有,这是一个我们使用 3 个组合器的微积分:
Sabc = ac(bc)
Kab = a
Ia = a
广为人知的是Turing-full。
此外,I
组合子是多余的。让我们试着用 S
和 K
组合子来写下来。
主要思想是 I 的唯一参数应该作为 K
的第一个参数传递,K
的第二个参数被占用。这正是 S
组合器所做的:如果我们将 K
作为 S
的第一个参数传递,我们将 return 作为 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 (<*>)
指定给 reader,我们将得到您写下的函数 curry . uncurry
- id
的函数。
curry . uncurry
的另一个类似方法是 ($)
:
f $ x = f x
($) f x = f x
($) f = f
($) = id
您找到的函数非常有趣 - 寻找其他类似的函数可能是一个很好的练习(我不知道是否还有其他值得注意的函数)。
当我输入以下内容时,我无法理解编译器的功能:
(curry . uncurry) (+) 1 2
根据我的理解,编译器首先使用 uncurry,这意味着会发生错误,因为 uncurry 函数需要这样的输入:
(curry . uncurry) (+) (1,2)
但显然第一个是对的。不过我不明白为什么。
编译器在评估时具体采取了哪些步骤?
另一个主题包含的问题: 为什么
(uncurry . curry) (+) (1,2)
不起作用?
After my understanding the compiler goes with uncurry.
不,它与 (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
and 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
反转,它将 pair 参数分成两个单独的参数。
确实,curry . uncurry
是二进制柯里化函数的恒等变换,因为它应用了变换及其逆。因此,它对结果或所涉及函数的类型没有影响。
我们得出结论,您的表达式等价于 ((+) 1) 2
,计算结果为 3
。
这个问题得到了很好的回答,但我想添加一个关于 curry . uncurry
的注释。
您可能听说过 SKI
-微积分。如果你还没有,这是一个我们使用 3 个组合器的微积分:
Sabc = ac(bc)
Kab = a
Ia = a
广为人知的是Turing-full。
此外,I
组合子是多余的。让我们试着用 S
和 K
组合子来写下来。
主要思想是 I 的唯一参数应该作为 K
的第一个参数传递,K
的第二个参数被占用。这正是 S
组合器所做的:如果我们将 K
作为 S
的第一个参数传递,我们将 return 作为 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 (<*>)
指定给 reader,我们将得到您写下的函数 curry . uncurry
- id
的函数。
curry . uncurry
的另一个类似方法是 ($)
:
f $ x = f x
($) f x = f x
($) f = f
($) = id
您找到的函数非常有趣 - 寻找其他类似的函数可能是一个很好的练习(我不知道是否还有其他值得注意的函数)。