【问题标题】:as-pattern and list pattern matching questionas-pattern 和 list 模式匹配问题
【发布时间】:2020-12-14 20:03:21
【问题描述】:

this pdf 的练习 2 内容如下:

一旦我们有正确顺序的数字,我们需要将每个数字加倍 另一个。定义一个函数 doubleEveryOther :: [Integer] -> [Integer] 请记住 doubleEveryOther 应该每隔一个加倍 从右开始的数字,即倒数第二个, 倒数第四个,...数字翻了一番。

我创建了一个实现,但它没有达到我的预期。这是我的代码:

doubleEveryOther'' :: [Integer] -> [Integer]
doubleEveryOther'' [] = []
doubleEveryOther'' [x] = [x]
doubleEveryOther'' s@(_:_:_) = 
    let x:y:xs = reverse s
    in reverse (x : 2 * y : doubleEveryOther'' xs)

以及它运行的一些示例:

*Main> doubleEveryOther'' [1,2,3,4,5,6,7,8,9]
[1,4,3,8,5,12,7,16,9]
*Main> doubleEveryOther'' [1,2,3,4,5,6,7,8]
[1,4,3,8,10,6,14,8]

不过,我期待

*Main> doubleEveryOther'' [1,2,3,4,5,6,7,8,9]
[1,4,3,8,5,12,7,16,9]
*Main> doubleEveryOther'' [1,2,3,4,5,6,7,8]
[2,2,6,4,10,6,14,8]

您会看到,在条目数量为偶数的情况下,它会部分地通过序列发生故障。我的猜测是我没有正确处理列表项 [] 的结尾,或者我没有正确使用 as-pattern。

【问题讨论】:

  • 在整个计算过程中你应该只需要两次reverse。现在,您在每次递归调用时都在反转列表,这看起来很奇怪。错误的结果有可能是在错误的时间倒车太多造成的。
  • 这里有一个可以帮助你的观察。假设doubleEveryOther'' 做了它想要做的事情:它将从其参数末尾开始的所有其他元素加倍。现在请注意,xs 与参数相比顺序相反;所以doubleEveryOther'' xs 是从xs 末尾开始的所有其他元素的两倍——但这是从原始参数的 start 开始的所有其他元素!
  • 这些是有用的提示。在我目前的水平上,很难确定事情发生在哪里。这就说得通了。此外,由于 reverse 有 2 个状态,并且我们正在对每个其他元素做一些事情,所以周期为 4 是有道理的。

标签: haskell as-pattern


【解决方案1】:

正如评论中所建议的,由于从左侧工作更容易,您可以使用以下过程:

  • 反转列表
  • 从左侧工作
  • 反转结果

这就像从右边开始工作一样。

这是一个使用递归函数来完成这项工作的解决方案

doubleEveryOther'' :: [Integer] -> [Integer]
doubleEveryOther'' = reverse . work . reverse
  where
    work [] = []
    work (x:y:z) = x:(2*y):work z
    work x = x

这是一个使用递归的解决方案

-- same as above except for work:
    work = zipWith (*) (concat $ repeat [1,2])

这可能不如前一个好,因为我们将一半的数字乘以 1 是在浪费时间;但我们没有递归......好吧,老实说,我的水平足够低,以至于我不知道哪种解决方案更好;我也不知道为什么[1..1000000] 上的第二个解决方案heap overflows,而第一个解决方案仍然需要在几秒钟后给我一个结果。我也试过在这两种情况下做take 10 $ doubleEveryOther'' [1..1000000],但这不起作用。可能这两种解决方案都不是懒惰的。

【讨论】:

  • 这是一个基于你的更新版本doubleEveryOther :: [Integer] -> [Integer] doubleEveryOther = reverse . zipWith ($) (cycle [id, (*2)]) . reverse 这并不是说堆溢出,但它确实在我的笔记本电脑上运行了一段时间后停止评估。我的猜测是你的问题来自重复的连接?这只是一个猜测。
  • 哦,是的,cycle 在概念上与concat . repeat 相同。尽管如此,它们的行为不同(对我来说,在[1..1000000] 上,它开始计算打印到屏幕,但无论如何它都会溢出。这是输出的尾随部分:...,92334,46168,92338,46170,923*** Exception: heap overflow。我认为这种差异完全与懒惰有关,它本身就值得提出一个问题,尽管我认为已经存在一个问题。
  • 关于使用$/[id, (*2)]而不是*/[1,2],我认为这是一个非常好的修改,因为它为每两个节省了一个乘法。
猜你喜欢
  • 1970-01-01
  • 2015-08-03
  • 2015-12-17
  • 2017-01-26
  • 1970-01-01
  • 2018-12-15
  • 2011-06-17
  • 2020-07-05
  • 1970-01-01
相关资源
最近更新 更多