Haskell中的复杂嵌套if else语句(性能)

时间:2017-01-17 17:30:34

标签: haskell

如果我想处理数亿个数字并通过if-else嵌套传递它们(在程序中):

if( n > 0 ){
    if( n > 100 ) {
        if( n > 1000 ) {
           return 1
        }else{
           return 2
        }
    }else{
        if( n > 50 ) {
           return 3
        }else{
           return 4
        }          
}else{
    //more nested else cases
}

在程序上,达到任何这些回报都需要进行3次比较,如果范围设置得当,确保平均情况是最优的(我现在就把随机数放在一边,不要小心)。但不确定在Haskell等功能语言上的最佳性能是什么(可读性或打字的简易性并不重要)。

例如,我可能可以直接编写if else嵌套,或者使用整个if路径的守卫:

| n > 1000 = 1
| n > 100 = 2
| n > 50 = 3
| n > 0 = 4
| etc

但我想,虽然对于n> 1000,只有一个比较,对于0等等的任何东西,它将花费超过三个。

这样的结构可能会四次评估n> 0,而不是分支:

| n > 0, n > 100, n > 1000 = 1
| n > 0, n > 100 = 2
| n > 0, n > 50 = 3
| n > 0 = 4
| etc

我也可以使用一个函数,但不确定函数是否便宜,因为比较会。

3 个答案:

答案 0 :(得分:3)

嗯,尽管Haskellers并不特别喜欢if语句,但是没有错误与它们相关。也许最好的事情就是

if n > 0
 then if n > 100
       then if n > 1000 then 1 else 2
       else if n > 50 then 3 else 4
 else ...

也就是说,您可能最好将整个比较内容委托给库中合适的数据类型。实际上,这些嵌套比较看起来有点像您尝试对Map进行硬编码。

答案 1 :(得分:2)

我可能会使用MaplookupLT操作或类似操作来实现此功能。例如:

import Data.Map.Strict as M
-- use a lambda to avoid recomputing results on each call
count = \n -> snd <$> M.lookupLT n results where
    results = M.fromAscList [(0,4),(50,3),(100,2),(1000,1)]

在ghci:

> count 1
Just 4
> count 1001
Just 1
> count (-300)
Nothing

如果您的密钥恰好是Int s,则可以通过交换IntMap来使其快一点。

这应该给你一些非常接近最佳比较次数的东西 - 在IntMap的情况下,甚至可能比你的嵌套ifs更少。我不知道这种方法必然是最快的,但它非常“有文化”:这段代码的读者不必破译深层嵌套的ifs链来发现它的作用,并在以后修改可能的结果(如果需要的话)会很简单。

答案 2 :(得分:1)

您可以定义适当的决策树:

data Tree n rv = Leaf rv | Node n (Tree n rv) (Tree n rv)

然后您的示例将被编码为

 (Node 0 ({- else nodes -}) -- depending on your other cases
         (Node 100 (Node 50 (Leaf 4) (Leaf 3))
                   (Node 1000 (Leaf 2) (Leaf 1)))

(左右子树对应<>=。如果您想进一步区分>==,可能需要进行一些调整。)

在树上行走时,将n值与每个节点中的第一个值进行比较,返回第二个值或递归遍历相应的子树。

search :: (Eq a, Ord a) => a -> Tree a b ->b
search _ (Leaf x) = x
search x (Node y left right)| x < y = search x left
                                | otherwise = search x right
相关问题