【问题标题】:Searching a tuple for a matching pair在元组中搜索匹配对
【发布时间】:2018-11-28 19:18:56
【问题描述】:

[(3,9),(4,16),(5,25)] 4 = 16

我使用 map (\(x,y ) -> x) [(3,9),(4,16),(5,25)] 来获取每个元素的 fst,然后将其过滤以匹配 - 但我在制作第一件作品时遇到了错误:

findTup :: Rel -> Int -> Int
findTup rs a = map (\(x,y ) -> x) rs
  • 无法将预期类型“Int”与实际类型“[Int]”匹配
  • 在表达式中:map (\ (x, y) -> x) rs

有什么方法可以使 map 在 Int -> Int 的元组上工作?

【问题讨论】:

  • 你可能想要lookup :: Eq a => a -> [(a, b)] -> Maybe b
  • 目前,您只得到map 返回的[Int] 值,但您已经声明findTup 将返回Int,而不是[Int]
  • \(x,y) -> x 已定义为 fst
  • 您可能会发现删除findTup 的类型签名并看看会发生什么很有趣。如果它编译,推断什么类型?如果它不编译,错误如何变化?您对结果感到惊讶吗?
  • 首先,在检查元组之前,如果找不到匹配项,您是否决定了答案?

标签: haskell


【解决方案1】:

让我们从 findTup 的内容开始。如果我们将你的函数放入 ghci 并询问我们得到的类型

ghci> findTup rs a = map (\(x,y) -> x) rs
ghci> :t findTup
findTup :: [(b1, b2)] -> p -> [b1]

这告诉我们,您的函数返回一个具有某种类型 b1 的项目列表,考虑到 map 的类型签名是 map :: (a -> b) -> [a] -> [b],这是有道理的。另一个将给定函数应用于列表中每个元素的函数,这里对我们更有用的是filter。过滤器的类型签名为filter :: (a -> Bool) -> [a] -> [a]

所以让我们尝试用filter 替换代码中的map,得到findTup rs a = filter (\(x, y) -> x) rs。现在,如果我们检查类型,我们发现它是findTup :: [(Bool, b)] -> p -> [(Bool, b)]。这个功能还有一些问题。对于初学者,(\(x, y) -> x) 将我们的输入限制为在元组的第一项中包含一个布尔值,但这不是我们想要的。我们需要重写过滤函数,以便它实际执行我们的搜索。 (\(x, y) -> x == a) 是我们真正想要的。这将导致过滤器仅返回与我们正在搜索的元素匹配的元素。现在我们可以看到findTup :: Eq a => [(a, b)] -> a -> [(a, b)]

不幸的是,这仍然返回一个列表,我们只需要一个项目。我们可以使用head 函数只获取第一个匹配的项目,所以findTup rs a = head $ filter (\(x, y) -> x == a) rs。 Ghci 告诉我们这个函数的签名是findTup :: Eq a => [(a, b)] -> a -> (a, b),这与我们想要的非常接近。唯一剩下的就是从元组中获取第二个值,我们可以使用函数snd

所以我们的最终代码将是findTup rs a = snd . head $ filter (\(x, y) -> x == a) rs。快速检查 ghci 显示 findTup :: Eq a => [(a, b)] -> a -> b。将Int 替换为我们找到的类型变量findTup :: [(Int, Int)] -> Int -> Int

【讨论】:

    【解决方案2】:

    问题是您声明的类型与您的部分实现不匹配。您可以简单地更改类型,直到您进行过滤:

    findTup :: Rel -> Int -> [Int]
    findTup rs a = map (\(x,y) -> x) rs
    

    或添加过滤。为此,显式递归和模式匹配会比使用map 更简单:

    findTup :: Rel -> Int -> Int
    -- findTup [] a = ...   -- Omitted to reflect the assumption of success
    findTup ((k, v):rs) a = if k == a then v else findTup rs a
    

    但是,没有必要重新发明轮子:

    import Data.Maybe
    
    findTup :: Rel -> Int -> Int
    findTup rs a = fromJust (lookup a rs)
    

    或者,因为您可能不应该假设查找会成功,

    findTup :: Rel -> Int -> Maybe Int
    findTup = flip lookup
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多