Select Haskell 列表的头部
Select head of a list in Haskell
现在,我仍在学习 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 来解析字符串列表,但我无法弄清楚获取列表头部的语法...像这样:
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:再次感谢大家的回答,他们都提供了很多信息! :)
如果你已经有一个列表,你不需要解析它——你可以简单地解构它。
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)
不过
所有这些都太复杂了。当你拆分问题时,它会变得更简单:
- 在
Query
monad 中检索 stringProperty "WM_NAME"
。
用它匹配任何提供的字符串。
doesNameBeginWithAny :: String -> Query Bool
doesNameBeginWithAny prefs = do
wmName <- stringProperty "WM_NAME"
return $ any (`isPrefixOf`wmName) prefs
这也可以用 fmap
编写,如 Freerich Raabe 所示。
如您所知,head
是获取列表第一个元素的方法,但我教给新手的解决方案是使用 pattern-matching 和递归访问列表元素。使用 doesNameBeginWith
:
的第一个定义
doesNameBeginAny :: [String] -> Query Bool
doesNameBeginAny (x:xs) = doesNameBeginWith x <||> doesNameBeginAny xs
doesNameBeginAny [] = return False
类型声明后,有两种情况。一种情况采用包含至少一个项目 (x
) 的列表,并在该项目上调用您的第一个函数,将结果与对列表其余部分的递归调用结合起来 (xs
,这可能为空)。模式匹配负责每次将列表拆开得到第一项,所以你不需要调用head
。第二种情况处理空列表。
正如您已经发现的那样,函数 foldr1
(以及其他折叠)抽象出函数的这种结构。您可以使用 map 将列表转换为 Query Bool
的列表,然后使用 fold 通过将元素组合在一起将列表减少为单个项目。
doesNameBeginAny xs = foldr (<||>) False queries
where queries = map doesNameBeginWith xs
我已经使用 where
定义了一个中间体,使代码更容易理解。我还使用了 foldr
而不是 foldr1
,因此该函数也适用于空列表。同样,您不需要调用 head
,因为 map
和 fold
负责拆分列表。
如果你正在寻找一个明确地将列表分开的答案,更像是 Python 或 Java 或其他命令式语言中的 for-each 循环,那不是你的方式写 Haskell.
您只需要稍微更改一下映射到字符串 属性 上的函数; any
函数派上用场了。而不是
doesNameBeginWith str = fmap ( str `isPrefixOf`) (stringProperty "WM_NAME")
尝试
doesNameBeginWith xs = fmap (\x -> any (`isPrefixOf` x) xs) (stringProperty "WM_NAME")
现在,我仍在学习 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 来解析字符串列表,但我无法弄清楚获取列表头部的语法...像这样:
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:再次感谢大家的回答,他们都提供了很多信息! :)
如果你已经有一个列表,你不需要解析它——你可以简单地解构它。
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)
不过
所有这些都太复杂了。当你拆分问题时,它会变得更简单:
- 在
Query
monad 中检索stringProperty "WM_NAME"
。 用它匹配任何提供的字符串。
doesNameBeginWithAny :: String -> Query Bool doesNameBeginWithAny prefs = do wmName <- stringProperty "WM_NAME" return $ any (`isPrefixOf`wmName) prefs
这也可以用 fmap
编写,如 Freerich Raabe 所示。
如您所知,head
是获取列表第一个元素的方法,但我教给新手的解决方案是使用 pattern-matching 和递归访问列表元素。使用 doesNameBeginWith
:
doesNameBeginAny :: [String] -> Query Bool
doesNameBeginAny (x:xs) = doesNameBeginWith x <||> doesNameBeginAny xs
doesNameBeginAny [] = return False
类型声明后,有两种情况。一种情况采用包含至少一个项目 (x
) 的列表,并在该项目上调用您的第一个函数,将结果与对列表其余部分的递归调用结合起来 (xs
,这可能为空)。模式匹配负责每次将列表拆开得到第一项,所以你不需要调用head
。第二种情况处理空列表。
正如您已经发现的那样,函数 foldr1
(以及其他折叠)抽象出函数的这种结构。您可以使用 map 将列表转换为 Query Bool
的列表,然后使用 fold 通过将元素组合在一起将列表减少为单个项目。
doesNameBeginAny xs = foldr (<||>) False queries
where queries = map doesNameBeginWith xs
我已经使用 where
定义了一个中间体,使代码更容易理解。我还使用了 foldr
而不是 foldr1
,因此该函数也适用于空列表。同样,您不需要调用 head
,因为 map
和 fold
负责拆分列表。
如果你正在寻找一个明确地将列表分开的答案,更像是 Python 或 Java 或其他命令式语言中的 for-each 循环,那不是你的方式写 Haskell.
您只需要稍微更改一下映射到字符串 属性 上的函数; any
函数派上用场了。而不是
doesNameBeginWith str = fmap ( str `isPrefixOf`) (stringProperty "WM_NAME")
尝试
doesNameBeginWith xs = fmap (\x -> any (`isPrefixOf` x) xs) (stringProperty "WM_NAME")