【问题标题】:Filter a list of tuples and return the first item based on a condition that applies to the second item过滤元组列表并根据适用于第二项的条件返回第一项
【发布时间】:2021-02-23 01:09:36
【问题描述】:

任务如下:我有一个包含国家名称和官方语言的元组列表。我有另一个列表,其中包含 (名字、姓氏、语言) 格式的翻译者的“3 项元组”。我需要获得一份国家/地区列表,其中任何翻译人员都不使用哪种官方语言。

它必须是对这种形式的函数的一次调用:

foo :: [(String, String)] -> [(String, String, String)] -> [String]

我尝试了各种mapfilter 组合都无济于事。我最接近的是使用集合。

third :: (a, b, c) -> c  
third (_, _, z) = z  

translators x = Set.fromList (map third x)
languages x = Set.fromList (map snd x)

diff x y = Set.toList (Set.difference (languages x) (translators y))

这为我提供了第一个列表中但翻译人员未使用的所有语言。然而,当然,任务是列出国家名称,而不是语言。所以我也尝试了这个,但它不起作用:

foo x y = filter ((Set.notMember (translators y)).snd) x

我是初学者,非常感谢一些帮助。

【问题讨论】:

  • 为了推动你朝着正确的方向前进,而不是吐出答案,你能解决一个更简单的问题吗?给定一个国家和一个口译员,你能不能写一个函数来告诉你那个口译员是否可以翻译那个国家的语言?我将从那里开始,然后弄清楚如何将该谓词函数用作过滤器。
  • 每种语言只能关联一个国家

标签: list haskell filter tuples


【解决方案1】:

您的第一个函数会为您提供正确的答案 - 除了它会为您提供语言列表。使用关联列表如何查找与该语言相关的国家/地区?

为此,您只需 swap (Country, Language) 元组中的国家和语言

import Data.Tuple ( swap )

langMap :: [(String, String)] -> [(String, String)]
langMap = map swap

现在,一旦您有了自己的 diff 函数的结果,您只需要将 lookup 函数映射到结果列表上

diff :: [(String, String)] -> [(String, String, String)] -> [String]
diff x y = map (fromJust . (`lookup` langMap')) diffList
    where
        diffList = Set.toList $ Set.difference (languages x) (translators y)
        langMap' = langMap x

如果您担心性能,可以将关联列表替换为Data.Map

import qualified Data.Map as Map

langMap :: [(String, String)] -> Map.Map String String
langMap = Map.fromList . map swap

并且还将diff中的lookup更改为Map.lookup

diff :: [(String, String)] -> [(String, String, String)] -> [String]
diff x y = map (fromJust . (`Map.lookup` langMap')) diffList
    where
        diffList = Set.toList $ Set.difference (languages x) (translators y)
        langMap' = langMap x

但是,这里有一个警告 - lookup 方法仅适用于每种语言只有一个国家/地区。

如果您想支持有多个国家与同一语言相关联的情况。 langMap-

你需要一些额外的电池
langMap :: [(String, String)] -> Map.Map String [String]
langMap x = Map.fromListWith (++) [(v, [k]) | (k, v) <- x]

这是对this answer 的轻微修改。它创建String[String] 的映射。键字符串是语言,值是国家列表

现在,由于 map 的值是一个字符串列表,而不仅仅是一个字符串 - 我们还需要稍微更改一下 diff

diff :: [(String, String)] -> [(String, String)] -> [String]
diff x y = concatMap (fromJust . (`Map.lookup` langMap')) diffList
    where
        diffList = Set.toList $ Set.difference (languages x) (translators y)
        langMap' = langMap x

嗯,这很简单。只需将 map 更改为 concatMap 就足够了 - 它会将字符串列表扁平化为字符串列表。

让我们看看它的实际效果-

λ> xs = [("US", "English"), ("Mexico", "Spanish"), ("France", "French"), ("Spain", "Spanish"), ("UK", "English"), ("Italy", "Italian")]
λ> ys = [("Foo", "Spanish"), ("Bar", "French")]
λ> diff xs ys
["UK","US","Italy"]

【讨论】:

  • 感谢您的详细解答。这是一个语法问题,但是“map (fromJust . (lookup langMap')) diffList”中的查找如何知道要查找什么?我理解 fromJust 接收到 langMap 上查找的输出,并且我们将整个内容映射到 difflist 的每个元素。
【解决方案2】:

让我们专注于正确性,解决问题,而不是解决方案是否有效。我们以后可以照顾。先正确,后效率!

最简单、最直观的编码方式是使用列表理解。

foo :: [(String, String)] -> [(String, String, String)] -> [String]
--       Country Language      FirstN  LastN   Language

-- return: list of Contries with no translator
foo cls nnls = [ c | (c, ___) <- cls,  -- for each country/language pair,
                     -- test the {list of ()s for each translator that ...}
                     ___ [ () | (_, _, ___) <- nnls, ___ == ___] ]

填写___ 空白(您不必填写_ 空白,这些是Haskell 的匿名、一次性变量)。

现在您可以提高效率,将上述内容视为可执行规范。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-22
    • 1970-01-01
    • 2012-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多