16

我是 Haskell 的新手。我知道函数被柯里化为带有一个参数的函数。我不明白在这种情况下如何实现对多个值的模式匹配。例如:

假设我们有以下完全任意的函数定义:

myFunc :: Int -> Int -> Int
myFunc 0 0 = 0
myFunc 1 1 = 1
myFunc x y = x `someoperation` y

部分应用的函数是否由myFunc 0本质上返回:

partiallyAppliedMyFunc :: Int -> Int
partiallyAppliedMyFunc 0 = 0
partiallyAppliedMyFunc y = 0 `someoperation` y

从而删除不可能匹配的无关模式?或者....这里发生了什么?

4

1 回答 1

27

实际上,这个问题比表面上看起来更微妙,需要学习一点编译器内部知识才能真正正确回答。原因是我们有点理所当然地认为我们可以在多个术语中拥有嵌套模式和模式,而实际上,为了编译器的目的,你唯一能做的就是在单个的顶级构造函数上分支价值。所以编译器的第一阶段是将嵌套模式(以及超过一个值的模式)转换为更简单的模式。例如,一个简单的算法可能会将你的函数转换成这样的:

myFunc = \x y -> case x of
    0 -> case y of
        0 -> 0
        _ -> x `someoperation` y
    1 -> case y of
        1 -> 1
        _ -> x `someoperation` y
    _ -> x `someoperation` y

你已经可以看到这里有很多次优的东西:这个someoperation术语被重复了很多次,并且函数在开始执行 a 之前就需要两个参数case;请参阅受有限自动机理论启发的术语模式匹配编译器,以讨论如何改进这一点。

无论如何,在这种形式下,实际上应该更清楚地了解柯里化步骤是如何发生的。我们可以直接替换x这个表达式中的来看看做了什么myFunc 0

myFunc 0 = \y -> case 0 of
    0 -> case y of
        0 -> 0
        _ -> 0 `someoperation` y
    1 -> case y of
        1 -> 1
        _ -> 0 `someoperation` y
    _ -> 0 `someoperation` y

由于这仍然是一个 lambda,因此没有进一步减少。你可能希望一个足够聪明的编译器可以做更多的事情,但是 GHC 明确地没有做更多的事情;如果您希望在仅提供一个参数后完成更多计算,则必须更改定义。(这里有时间/空间的权衡,正确选择太难做到可靠。所以 GHC 把它留给程序员来做这个选择。)例如,你可以明确地写

myFunc 0 = \y -> case y of
    0 -> 0
    _ -> 0 `someoperation` y
myFunc 1 = \y -> case y of
    1 -> 1
    _ -> 1 `someoperation` y
myFunc x = \y -> x `someoperation` y

然后myFunc 0会减少到一个更小的表达式。

于 2012-09-30T06:06:30.463 回答