【问题标题】:Merging Two Lists of Tuples合并两个元组列表
【发布时间】:2020-02-19 02:06:55
【问题描述】:

我需要创建一个递归函数,将两个元组列表合并在一起,并从一个已知元组开始。该函数接收三个参数:

  • 两个元组列表
  • 一个元组

我试过了:

mergePoints l1 l2 o = o ++ [f | (a, b) <- zip l1 l2, 
                                let f if a < b then f = a, b else f = b, a]

我知道我做得不对,但我真的不知道该怎么做。

练习的示例输入和输出是:

ghci> mergePoints [(1, 3), (1, 4), (5, 5)] [(1, 1), (4, 4)] (0, 0)
[(0.0, 0.0), (1.0, 1.0), (1.0, 3.0), (1.0, 4.0), (4.0, 4.0), (5.0, 5.0)]

【问题讨论】:

  • 如果它“以一个已知的元组开头”,那么简单的mergePoints l1 l2 o = o : mergePoints2 l1 l2,不是吗?
  • why the dv? OP 说明了问题,显示了对解决方案进行编码的尝试、示例输入和所需的输出......这是一个几乎完美的问题, 我想。事实上,我应该马上对它进行紫外线处理。如果回答足够好,那么投票就足够了。

标签: list haskell recursion merge


【解决方案1】:

根据你的例子,

ghci> mergePoints [(1, 3), (1, 4), (5, 5)] [(1, 1), (4, 4)] 
      (0, 0)
[    (0.0, 0.0),                          (1.0, 1.0), 
               (1.0, 3.0), (1.0, 4.0),            (4.0, 4.0), 
                                  (5.0, 5.0)]

这两个列表中的每一个都以自己的速度消耗,因此zip 不适合这里,因为它以相同的速度消耗其参数列表。

对此进行编码的最简单方法是使用带有模式匹配的直接递归定义,该定义将比较两个头部元素并根据比较结果使用一个或另一个。

也许您还需要将它们与提供的第三点进行比较,以防它不是最小的。


那么,关于您的代码。

从示例输入输出来看,点应该是按顺序出来的。我们可以在元组定义字典顺序时比较它们。你已经在这样做了。通过一个小的语法修复,您的代码是:

mergePoints1 l1 l2 o  =  o ++ [f | (a, b) <- zip l1 l2, 
                                   let f if a < b then f = [a, b] 
                                                  else f = [b, a]]

这不是一个有效的 Haskell。 Haskell 中没有赋值,只有定义:

mergePoints2 l1 l2 o  =  o ++ [f | (a, b) <- zip l1 l2, 
                                   let f = if a < b then [a, b] else [b, a]]

但是这个列表推导会创建一个元组列表列表,因为我们将它们创建为f = [a,b]f = [b,a]。逐一使用这些点,我们需要从这些短列表中逐一“绘制”它们,使用

mergePoints3 l1 l2 o  =  o ++ [r | (a, b) <- zip l1 l2, 
                                   r <- if a < b then [a, b] else [b, a]]

(使用r 表示“结果”,而不是f)。最后一个错误是++ 连接了两个列表,但o 不是一个。那么它一定是

mergePoints4 l1 l2 o  =  [o] ++ merge1 l1 l2

merge1 l1 l2  =  [r | (a, b) <- zip l1 l2, 
                      r <- if a < b then [a, b] else [b, a]]

现在它可以工作了,但没有做它应该做的事情。事实上,由于它的输出仍然不能保证是有序的,我们甚至可以把它写成

merge2 l1 l2  =  l1 ++ l2
  where                     -- the definition of the built-in `++`:
  []     ++ l2  =  l2
  l1     ++ []  =  l1
  (x:t1) ++ l2  =  x : (t1 ++ l2)

或者我们可以使用

both 列表中提取元素
merge3 l1 l2  =  l1 ++ l2
  where
  []     ++ l2      =  l2
  l1     ++ []      =  l1
  (x:t1) ++ (y:t2)  =  x : y : (t1 ++ t2)   -- or:
               --   =  x : ((y:t2) ++ t1)

当然,按位置逐个取出元素可能会扰乱它们的顺序。您尝试使用merge1 纠正此问题可以重写为

merge1b l1 l2  =  l1 ++ l2
  where
  []     ++ l2  =  l2
  l1     ++ []  =  l1
  (x:t1) ++ (y:t2)  =  if  x < y  then  x : y : (t1 ++ t2)
                       else             y : x : (t1 ++ t2)

但它仍然可以打乱订单。我们假设我们的两个输入是有序的,增加列表,其中每个元素都小于列表中跟随它的元素。所以我们只需要使用这些知识,小心只提取其中一个元素:

merge4 l1 l2  =  l1 ++ l2
  where
  []     ++ l2      =  l2
  l1     ++ []      =  l1
  (x:t1) ++ (y:t2)  =  if   x < y  then  x : (_ ++ (y:t2))
                       else              _ : _

您需要通过将_s 替换为实际代码来完成此定义。

额外问题:在两个元素相等的情况下,Haskell 中merge 的通常定义更喜欢它的“左”参数而不是“右”参数。对代码进行单字符编辑,以实现此目的。

【讨论】:

  • 我明白了。新问题是我不太确定如何进行递归。
  • 这是一个例子:foo x | x &gt; 10 = [] | otherwise = x : foo (x+1).
  • 这是另一个:bar [] = [] ; bar (x:xs) | x &gt; 10 = [] | otherwise = x : bar xs.
  • 我最终得到了这个:mergePoints [] [] o = [] ; mergePoints (x:xs) (y:ys) o | x &lt;= y = p ++ [x] ++ [y] ++ mergePoints xs ys o | x &gt; y = p ++ [y] ++ [x] ++ mergePoints xs ys o ; where p = o : [] ,我知道我的错误在哪里,我只是不知道如何处理它
  • 我让它工作,另一种方式。我首先想做的是你在 mergePoints2 中所做的,但是我在 let 子句中遇到了错误,所以你所做的帮助我理解了我的错误,现在我知道如何处理它了。其余所有内容对我了解更多 Haskell 及其工作原理非常有帮助。非常感谢,这真是一个很好的帮助。
猜你喜欢
  • 2012-10-15
  • 2013-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-07
  • 2017-09-26
  • 2021-11-11
  • 1970-01-01
相关资源
最近更新 更多