3

我有一个列表 [String],任务是删除列表中具有“q”或“p”的元素,然后使用 toUpper 将列表中的所有字母大写。

我尝试过的如下:

delAndUpper :: [String] -> [String]
delAndUpper myList = filter (\x -> not('p' `elem` x || 'q' `elem` x)) myList

它会正确地从列表中删除不需要的元素,但是我无法在此列表上应用 toUpper,因为 toUpper 的类型是 Char。

我用地图尝试过,它不起作用。

delAndUpper myList = map toUpper (filter (\x -> not('p' `elem` x || 'q' `elem` x)) myList)

我知道,这行代码中的 toUpper 将一个列表作为值,因此它无法工作,但知道如何将级别降低到列表和应用映射 toUpper。

请你帮助我好吗。

提前致谢!

问候

4

3 回答 3

9

映射更深一层

你需要使用map (map toUpper).

这是因为你有[String]而不是String.

toUpper           :: Char -> Char
map toUpper       :: [Char] -> [Char]

IE

map toUpper       :: String -> String
map (map toUpper) :: [String] -> [String]

map toUpper通过使每个字母大写来将字符串大写,因此map (map toUpper)将字符串列表中的每个字符串大写。

你的功能变成

delAndUpper myList = map (map toUpper) (filter (\x -> not('p' `elem` x || 'q' `elem` x)) myList)

dave4420 提出了一个很好的建议,这(map.map) toUpper是一种简洁的写作方式map (map toUpper),可以帮助您非常简单自然地思考两个列表级别 - 也请查看他的答案。

我们可以取消对 p 和 q 的硬连线吗?

您问是否有更短的方法来编写条件,并且不喜欢硬编码`q`and `p`。我同意那些多位`elem`并不漂亮。让我们传入不允许的字母列表并整理一下:

delAndUpper omit strings = map (map toUpper) (filter ok strings) where
            ok xs = not (any (`elem` omit) xs)

这里(`elem` omit)检查一个字符是否在会导致我们省略该单词的字符列表中,因此(any (`elem` omit) xs)检查是否有任何字符xs被禁止。当然,如果没有禁止,那就是ok.

您的原件delAndUpper现在是delAndUpper "pq",或者如果您还想禁止大写 P 和 Q,delAndUpper "pqPQ"。(稍后会详细介绍。)

我们可以让它更简洁吗?

让我们看看我们能不能写得ok更短一点。我的第一个想法是使用pointfree它(有关如何使其在 ghci 中运行的详细信息,请参阅我对另一个问题的回答not),但它似乎挂起,因此使用一些标准的转换技巧,我们可以组合一个带有两个参数的函数在给我们一个 Bool 之前,(not.).f而不是not.f像我们在第一次输入后给我们一个 Bool 的函数那样做。any(`elem` omit)作为它的第一个论点。这给了我们

ok xs = ((not.).any) (`elem` omit) xs

我们可以从中删除尾随的 xs:

ok = ((not.).any) (`elem` omit) 

和内联:

delAndUpper omit strings = map (map toUpper) (filter (((not.).any) (`elem` omit)) strings)

我也不热衷于尾随strings

delAndUpper omit = map (map toUpper).filter (((not.).any) (`elem` omit))

(我们也可以摆脱omit争论,完全没有意义,但这在我看来很难阅读的道路上走得太远了。)

Q去哪儿?

> delAndUpper "pq" $ words "The Queen has probably never had a parking ticket."
["THE","QUEEN","HAS","NEVER","HAD","A","TICKET."]

这是必需的行为吗?小心地排除小写变体然后将所有内容变为大写似乎很奇怪。我们可以反过来做:

upperAndDel omit = filter (((not.).any) (`elem` omit)).map (map toUpper)

给予

> upperAndDel "PQ" $ words "The Queen has probably never had a parking ticket."
["THE","HAS","NEVER","HAD","A","TICKET."]
于 2013-05-06T18:00:58.990 回答
3

我知道,这行代码中的 toUpper 将一个列表作为值,因此它无法工作,但知道如何将级别降低到列表和应用映射 toUpper。

使用(map . map)而不是map.

nb与其他答案所建议(map . map) toUpper的相同。map (map toUpper)我提到它是因为,就我个人而言,我觉得它更清晰:它看起来更像是下降了两个级别来 apply toUpper。(您可能对函数组合运算符不熟悉,(.)如果需要,请查看或询问。)

具有相似类型 ( (a -> b) -> something a -> something b) 的其他函数,例如fmap、和Data.Map.map,可以相互组合并以类似的方式组合。firstsecondmap

也许你找不到(map . map) toUppermap (map toUpper); 很公平。

于 2013-05-06T18:12:13.730 回答
1

你快到了,你只需要一秒钟map

map (map toUpper) (filter (\x -> not('p' `elem` x || 'q' `elem` x)) myList)

这是因为在 vanilla haskellString中完全是同义词。[Char]由于地图的类型是

(a->b) -> [a] -> b

我们有

toUpper :: Char -> Char
String  :: [Char]

我们将返回另一个String,除了大写。

顺便说一句,可以通过使用箭头来替换那个丑陋的过滤器,让它变得更漂亮:)(把这些想象成更结构化的函数)

map (map toUpper) . filter $ elem 'p' &&& elem 'q' >>> arr (not . uncurry (||))

无偿?也许吧,但有点酷。

于 2013-05-06T18:00:58.747 回答