有界枚举类型的快速过滤

时间:2015-03-30 18:04:12

标签: haskell optimization enums

假设我有一个函数,它接受一些谓词f并将其应用于整个域:

someFilter :: (Enum a, Bounded a) => (a -> Bool) -> [a]
someFilter f = filter f [minBound ..]

我也知道这个谓词只在相对较少的参数中返回True,例如

isA c = (c == 'A')

是否有可能以某种方式使someFilter快速,即不依赖于类型a的可能值的数量,而不改变其签名?

2 个答案:

答案 0 :(得分:3)

一般情况下你不能真正做到这一点,因为你赋予这个功能的是aEnumBounded,这意味着你永远无法做到判断谓词是否使用==(不是说你无论如何都可以这样做)。相反,您可以编写可在执行过滤器之前检查的自定义数据类型:

import Control.Applicative

infixr 2 `Or`
infixr 3 `And`
data Pred a
    = LessThan a
    | GreaterThan a
    | EqualTo a
    | Generic (a -> Bool)
    | And (Pred a) (Pred a)
    | Or  (Pred a) (Pred a)

someFilter :: (Enum a, Bounded a, Ord a) => Pred a -> [a]
someFilter p = go p [minBound ..]
    where
        go :: (Enum a, Bounded a, Ord a) => Pred a -> [a] -> [a]
        go (LessThan x)    = takeWhile (< x)
        go (GreaterThan x) = dropWhile (<= x)
        go (EqualTo x)     = const [x]
        go (Generic f)     = filter f
        go (And p q)       = go q . go p
        go (Or  p q)       = (++) <$> go p <*> go q

naiveFilter :: (Enum a, Bounded a) => (a -> Bool) -> [a]
naiveFilter f = filter f [minBound ..]

这为您提供了大多数所需的工具(并非所有工具,这只是一个粗略的概述)来重新创建排序谓词的基本布尔组合,并支持通用谓词。对于某些情况,someFilter的效果会比naiveFilter好很多,例如someFilter (LessThan 128) :: [Word32]naiveFilter (< 128) :: [Word32]相比。在我的计算机上,前者基本上完成了0,而后者我甚至没有让完成。您还可以运行立即完成的someFilter (And (LessThan 128) (GreaterThan 256)) :: [Word32]等查询,但需要naiveFilter永久保存。我在这里使用Word32代替Int来避免负数问题。

如果你想将一堆谓词组合在一起,你可以做到

allPs, anyPs :: [Pred a] -> Pred a
allPs = foldr1 And
anyPs = foldr1 Or

或者你可以尝试用它来构建一个平衡的树,但这足以简化。它确实让你很容易表达相当复杂的查询:

> :set +s
> someFilter $ EqualTo 1 `Or` EqualTo 100 `Or` LessThan 1000 `And` GreaterThan 975 :: [Word32]
[1,100,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,991,992,993,994,995,996,997,998,999]
(0.00 secs, 0 bytes)

请注意,由于takeWhiledropWhile,操作顺序在这里很重要,并且您最终也可以使用重复项(尝试制作大于或等于且小于或等于-equals,你会明白我的意思)。这些修复程序将开始使用此算法的性能,但一般情况下,您应该看不到比naiveFilter更差的性能。

答案 1 :(得分:1)

对于固定字a,请f Word160(== w) . sha1w :: Word160,其中sha1 :: Word160 -> Word160SHA-1的某些实现}}。那么谓词只适用于极少数输入(最可能是1)。如果你有一个快速的方法来找到匹配的值,你将构建一个SHA-1的反函数,这将允许你破解数字签名和各种类似的东西。所以不,除非你以某种方式限制谓词,否则没有有效的方法。