考虑一下:
ruleset = [rule0, rule1, rule2, rule3, rule4, rule5]
其中rule0
,rule1
等是带有一个参数的布尔函数。找到特定列表的所有元素是否满足ruleset
中的所有规则的最简洁方法是什么?
显然,一个循环可以工作,但Haskell人似乎总是对这些类型的问题有聪明的单行。
all
函数似乎合适(例如。all (== check_one_element) ruleset
)或嵌套map
。此外,map ($ anElement) ruleset
大致是我想要的,但对于所有元素。
我是Haskell的新手,人们可以解决这个问题的方法很多。
答案 0 :(得分:12)
如果您需要所有 每个参数的函数,那么它只是
and (ruleset <*> list)
(您需要import Control.Applicative
才能使用<*>
。)
说明:
当<*>
给出一对列表时,它会将左侧列表中的每个函数应用到右侧列表中的每个参数,并返回包含所有结果的列表。
答案 1 :(得分:3)
单行:
import Control.Monad.Reader
-- sample data
rulesetL = [ (== 1), (>= 2), (<= 3) ]
list = [1..10]
result = and $ concatMap (sequence rulesetL) list
(我们在这里工作的类型是Integer
,但它可能是其他任何内容。)
让我解释一下发生了什么:rulesetL
的类型为[Integer -> Bool]
。通过认识(->) e
是monad,我们可以使用
sequence :: Monad m => [m a] -> m [a]
在我们的案例中将专门输入[Integer -> Bool] -> (Integer -> [Bool])
。所以
sequence rulesetL :: Integer -> [Bool]
会将值传递给列表中的所有规则。接下来,我们使用concatMap
将此函数应用于list
,并将所有结果收集到一个列表中。最后,致电
and :: [Bool] -> Bool
将检查所有组合是否返回True
。
编辑:查看 dave4420 的答案,它更好,更简洁。如果您需要组合规则并在以后的某些列表中应用它们,我的答案可能有所帮助。特别是
liftM and . sequence :: [a -> Bool] -> (a -> Bool)
将多个规则合并为一个。您还可以将其扩展到其他类似的组合器,例如使用or
等。实现规则是(->) a
monad的值可以为您提供其他有用的组合器,例如:
andRules = liftM2 (&&) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)
orRules = liftM2 (||) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)
notRule = liftM not :: (a -> Bool) -> (a -> Bool)
-- or just (not .)
等。 (不要忘记导入Control.Monad.Reader
)。
答案 2 :(得分:1)
易于理解的版本(不使用Control.Applicative
):
satisfyAll elems ruleset = and $ map (\x -> all ($ x) ruleset) elems
答案 3 :(得分:1)
就个人而言,我喜欢这种编写函数的方式,因为它明确使用的唯一组合符是and
:
allOkay ruleset items = and [rule item | rule <- ruleset, item <- items]