现在,我还在学习haskell(配置我的xmonad wm),所以请耐心等待。
我写过这个函数:
doesNameBeginWith :: String -> Query Bool
doesNameBeginWith str = fmap ( str `isPrefixOf`) (stringProperty "WM_NAME")
这会检查我的str的WM_NAME X属性的开头。我可以这样写:
isMusic = doesNameBeginWith "MPD:" <||> doesNameBeginWith "ncmpcpp"
有效。现在我想用这个签名编写一个函数:
[String] -> Query Bool
所以我可以这样称呼它:
isMusic = doesNameBeginWith ["MPD:", "ncmpcpp"]
我的想法是使用foldr1来解析字符串列表,但是我无法弄清楚sintax来获得列表的头部......就像这样:
doesNameBeginWith :: [String] -> Query Bool
doesNameBeginWith str = foldr1 (<||>) . fmap ( (head str) `isPrefixOf`) (stringProperty "WM_NAME")
编辑1:如leftaroundabout建议,我可以结合两个函数来获得我想要的东西:
doesNameBeginWithL :: [String] -> Query Bool
doesNameBeginWithL = foldr1 (<||>) . map doesNameBeginWith
但我仍然希望有一种更直接的方式,并了解如何在这种情况下使用列表头!
编辑2:再次感谢大家的回答,所有这些都非常有用! :)
答案 0 :(得分:2)
你只需要改变你在字符串属性上映射的功能; any
功能派上用场。而不是
doesNameBeginWith str = fmap ( str `isPrefixOf`) (stringProperty "WM_NAME")
试
doesNameBeginWith xs = fmap (\x -> any (`isPrefixOf` x) xs) (stringProperty "WM_NAME")
答案 1 :(得分:1)
如果您已有列表,则无需解析它 - 您只需解构即可。
isThisStuff :: [String] -> Query Bool
isThisStuff [c,d] = doesNameBeginWith c <||> doesNameBeginWith d
然后
isMusic = isThisStuff ["MPD:", "ncmpcpp"]
请注意,如果提供的列表没有两个元素,则会失败。
可以说,基于折叠的解决方案更好。正如您所指出的,这可以写成
foldr1 (<||>) . map doesNameBeginWith
现在,您需要做的就是内联来摆脱doesNameBeginWith
。注意
doesNameBeginWith = \str -> fmap ( str `isPrefixOf`) (stringProperty "WM_NAME")
所以你可以使用
foldr1 (<||>) . map (\str -> fmap ( str `isPrefixOf`) (stringProperty "WM_NAME"))
如果你不喜欢lambda,你可以将其表达为列表理解:
doesNameBeginWithAny strs = foldr1 (<||>)
[fmap ( str `isPrefixOf`) (stringProperty "WM_NAME") | str <- strs]
或者,您可以doesNameBeginWith
无点
doesNameBeginWith = \str -> fmap (isPrefixOf str) (stringProperty "WM_NAME")
= \str -> flip fmap (stringProperty "WM_NAME") (isPrefixOf str)
= \str -> flip fmap (stringProperty "WM_NAME") . isPrefixOf $ str
= flip fmap (stringProperty "WM_NAME") . isPrefixOf
= (stringProperty "WM_NAME" `fmap`) . isPrefixOf
所以
doesNameBeginWithAny = foldr1 (<||>)
. map ((stringProperty "WM_NAME" `fmap`) . isPrefixOf)
所有这一切都过于复杂。当你解决问题时,它变得更加简单:
stringProperty "WM_NAME"
monad。Query
将任何提供的字符串与其匹配。
doesNameBeginWithAny :: String -> Query Bool
doesNameBeginWithAny prefs = do
wmName <- stringProperty "WM_NAME"
return $ any (`isPrefixOf`wmName) prefs
这也可以用fmap
写成,正如Freerich Raabe所示。
答案 2 :(得分:1)
正如您所知,head
是您如何获得列表的第一个元素,但我教给新手的解决方案是使用模式匹配和递归访问列表元素。使用doesNameBeginWith
的第一个定义:
doesNameBeginAny :: [String] -> Query Bool
doesNameBeginAny (x:xs) = doesNameBeginWith x <||> doesNameBeginAny xs
doesNameBeginAny [] = return False
在类型声明之后,有两种情况。一个案例采用包含至少一个项目(x
)的列表,并在该项目上调用您的第一个函数,将结果与列表其余部分(xs
)的递归调用相结合,是空的。模式匹配负责拆分列表以便每次都获得第一项,因此您不需要致电head
。第二种情况处理空列表。
正如您已经发现的那样,函数foldr1
(以及其他折叠)抽象出函数的这个结构。您可以使用地图将列表转换为Query Bool
列表,然后使用折叠将列表缩减为单个项目或将元素组合在一起。
doesNameBeginAny xs = foldr (<||>) False queries
where queries = map doesNameBeginWith xs
我已使用where
定义中间件以使代码更易于遵循。我还使用了foldr
而不是foldr1
,因此该功能也适用于空列表。同样,您不需要致电head
,因为map
和fold
负责拆分列表。
如果您正在寻找明确区分列表的答案,更像是Python或Java中的for-each循环或其他一些命令式语言,那就不是您编写Haskell的方式。< / p>