fromJust定义中的模式匹配

时间:2011-06-08 13:08:02

标签: performance haskell pattern-matching

fromJust中的Data.Maybe函数以这种方式定义:

fromJust          :: Maybe a -> a
fromJust Nothing  = error "Maybe.fromJust: Nothing"
fromJust (Just x) = x

根据我对模式匹配的理解(从上到下匹配收益),我会改变两个定义的顺序。由于Nothing-part通常在it-is-sure-a-Just情况下不匹配,但在达到第二个定义之前总是会检查它。

请您澄清我在推理中的错误?感谢。

修改

实施例: 假设我的文件中每行有一百万个Int类型的数据,并且在我的程序中y需要这些数字(Int,而不是String)。

import qualified Data.ByteString.Lazy.Char8 as L

readInt = fst . fromJust . L.readInt
-- more stuff

根据fromJust的上述定义,我需要更多时间来阅读这些数字,不是吗?

3 个答案:

答案 0 :(得分:9)

我认为这个问题与性能有关。虽然语义模式匹配是从上到下进行测试的,但大多数Haskell编译器会优化ADT构造函数的匹配,使其等效于C switch语句。

您可以认为ADT的数据表示形式有一个“标记”,表示它的构造函数,以及每个参数的指针。例如,Nothing可能表示为0 (null)Just 42表示为1 (pointer to 42)

然后在这样的函数中:

squash :: Maybe (Maybe Int) -> Int
squash (Just Nothing) = 0
squash Nothing = 0
squash (Just (Just x)) = x

编译器会设置决策树:

squash x = 
   check tag of x:
       0 -> 0
       1 y -> check tag of y:
           0 -> 0
           1 z -> z

每个标记都是通过跳转表或其他内容计算的,因此将0作为1进行检查并不是更昂贵。请注意,无论我们的定义中的模式的原始顺序如何,都会生成相同的决策树。

但是,当使用保护而不是在构造函数上匹配时,模式最有可能从上到下进行检查(编译器必须非常智能来优化)。所以如果我们用这种神秘的方式写fromJust

fromJust x
    | isNothing x = error "fromJust: Nothing"
    | isJust x = case x of { Just y -> y }

然后,这可能会轮流检查每个构造函数,我们可以通过切换案例的顺序进行优化。幸运的是,写作方式很重要: - )。

答案 1 :(得分:6)

这里要实现两个重要的事情:首先,Haskell编译器完全清楚任何值都只能是NothingJust x。因此,它只会测试其中一个。其次,GHC使用指针标记,允许它快速区分指向各种构造函数的指针(details)。

因此,GHC为模式匹配生成了相当不错的代码。以下是fromJust返回代码的外观,直接来自程序集转储:

        andq $7,%rax
        cmpq $2,%rax
        jae .LcO4

这将获取返回值,然后使用位掩码操作(7 = 111二进制)提取标记。如果此标记为2,则代码知道它指向Just x构造函数。因此它跳转到适当的代码。否则,它只会继续处理Nothing

的代码

认为甚至可以使用程序中只有一个Nothing闭包的知识进一步优化。因此,您可以使用单个地址比较替换它,从而在实际代码中保存位掩码操作甚至寄存器。不确定为什么GHC不会这样做。

答案 2 :(得分:0)

匹配Nothing的内容无法匹配Just。所以两个订单都有效。

如果您通过Just 5fromJust,则Just 5Nothing不匹配。但Just 5Just x匹配。

它的工作方式完全相同。当函数没有在其中一个参数上进行模式匹配时,事情才开始变得棘手。