【问题标题】:create a new list with the difference off each value创建一个新列表,每个值都有差异
【发布时间】:2012-03-07 16:23:30
【问题描述】:

我正在尝试创建一个执行以下操作的函数:它获取一个列表,例如 [1,4,2,3],它返回一个列表 [3,2,1]

因为1 - 4 = 3(绝对值)、4 - 2 = 22 - 3 = 1

我认为除了 abs 值之外,这段代码可以做到这一点。

function :: [a] -> [a]
function [] = []
function (x:xs) = [x - head(xs)] ++ function xs

但它给了我错误,我没有找到任何解决方案。

亲切的问候,

编辑:

谢谢大家,今天学到了很多。事实上,我是一个初学者,我在大学里有一门课程,里面有 prolog、haskell、scala、python、aspectj 和元编程。所以我们每个程序有 2 节课,休息 2 小时,然后有时间做一些练习。

下周一我有考试和 zipwith 等...我们必须编写自己的函数。但是感谢您提供了很好的解释教程,学到了很多东西。这是可行的解决方案:

function :: Num a => [a] -> [a]
function (x:y:xs) = [abs(x - y)] ++ function (y:xs)
function _ = []

【问题讨论】:

标签: haskell


【解决方案1】:

如果您仍然对递归解决方案感兴趣。

No instance for (Num a)
  arising from a use of `-'
In the expression: x - head (xs)
In the first argument of `(++)', namely `[x - head (xs)]'
In the expression: [x - head (xs)] ++ function xs

所以(-) :: Num a => a -> a -> a 运算符要求它的参数应该是Num。所以我们可以用function :: Num a => [a] -> [a]修复它。

现在我们有另一个问题:

> function [1,4,2,3]
[-3,2,-1,*** Exception: Prelude.head: empty list

所以我们应该处理一些情况:

function :: Num a => [a] -> [a]
function [] = []
function [_] = []
function (x:xs) = x - head xs : function xs

但这仍然不是我们实际预期的:

> function [1,4,2,3]
[-3,2,-1]

所以我们应该添加abs函数:

function :: Num a => [a] -> [a]
function [] = []
function [_] = []
function (x:xs) = abs ( x - head xs ) : function xs

完成

> function [1,4,2,3]
[3,2,1]

除此之外,您还可以使用更易读的代码轻松地做到这一点。

如何找出两个列表之间的差异? zipWith 在这里看起来真的很有帮助。例如:

> zipWith (-) [1,2,3,4] [1,1,1]
[0,1,2]

所以想法是zipWith (-)原始列表,它是tail

> let x = [1,2,3,4]
> zipWith (-) x (tail x)
[-1,-1,-1]

而你的function 可能会喜欢这样:

function :: Num a => [a] -> [a]
function x = map abs $ zipWith (-) x (tail x)

完成

> function [1,4,2,3]
[3,2,1]

【讨论】:

  • 不需要function [x, y] 案例。
【解决方案2】:

一般来说,最好告诉我们您遇到了哪些错误,而不是期望我们输入您的代码并自行运行。但这是我一眼就能看到的:

  1. function :: [a] -> [a] 表示function 适用于 any 类型的列表。但后来你说x - head(xs)。这里我们有x :: ahead(xs) :: a。您期望减法运算符采用任意两个值,只要它们具有相同的类型。

    但是(-) :: Num a => a -> a -> a;这两个值具有相同的类型是不够的,该类型必须实现Num 类型类(它还提供加法、乘法、绝对值和一些其他功能)。

    修复:将类型签名替换为function :: Num a => [a] -> [a]

  2. 代码现在应该可以编译了,但是当您运行它时,您会收到一条关于获取空列表头部的错误消息。

    让我们逐步了解运行 function [4]* 时会发生什么:

    • 第一个等式左侧有function [],但[4][]不匹配,所以跳过它。

    • 第二个等式左侧有function (x:xs)[4] 匹配 (x:xs) 给我们 x = 4xs = [],所以我们将继续这个等式并评估 [4 - head([])] ++ function []

    • 这涉及评估[4 - head([])],这涉及评估4 - head([]),这涉及评估head([]),这是一个错误,因为空列表没有头部。

    修复:想想我们在这里想要什么。当我们对列表进行模式匹配时,仅仅知道它有一个头部 (x) 是不够的。我们还需要知道它还有第二个元素 (y)。所以用

    替换方程
    function (x:y:xs) = [x - y] ++ function (y:xs)
    function _        = []
    

    _ 完全匹配任何值。当列表包含两个或多个元素时,第一个等式匹配,因此第二个等式将清除空列表和单个元素列表的情况。

    *:我并不是说它实际上是按照这个顺序进行评估的。但这是纯代码(和有限数据),因此我们以什么顺序评估事物并不重要。

  3. 如果可以的话,你真的不想自己进行显式递归,而是想使用更高阶的函数;部分原因是这意味着您不必自己编写递归(并冒着出错的风险),部分原因是您通常也可以将模式匹配转移到高阶函数上(也避免可能出错)。所以听从 sclv 的建议:使用 zipWithdrop 1(并忽略一次 Daniel Fischer:你不想养成需要关心使用 head 是否安全的习惯当然,当drop 1 [] 产生[] 有用时)。

    使用高阶函数代替模式匹配和显式递归的其他优点:

    • 只需要一个方程
    • 更容易弄清楚代码的作用
    • 更高效的代码

【讨论】:

  • 请注意,[single element] ++ list 总是最好写成single element : list,因为 (++) 是连接,当你可以从它的头部和尾部创建一个新列表时,你不需要它(:)。它更容易阅读,效率更高。所以abs (x - y) : function (y:xs) 在这里会更好。 (当然,“函数”对于函数来说是一个非常糟糕的名字,但这是一个细节)
  • @Jedai 绝对。当你走zipWith 路线时,你不必担心这种优化。
  • 感谢 zipwith 教程,将来可能会用到
【解决方案3】:

看看zipWithdrop 1...

【讨论】:

  • 虽然这是您可以安全使用的地方之一tail
  • 我认为我们不应该期望初学者知道或发现使用tail 生成第一个输入列表还是第二个输入列表是否安全。
【解决方案4】:

Dmitri 的解决方案可以改进

function :: Num a => [a] -> [a]
function x = map abs $ zipWith (-) x (tail x)

我们真正想要的是获取(-) 的结果并立即在abs 函数中“输入”它,避免额外的map 步骤。用 lambda 表达式来做这件事是很丑陋的:

function :: Num a => [a] -> [a]
function x = zipWith (\a b -> abs(a - b)) x (tail x)

魔术酱称为“函数组合”,使用运算符(.)(f . g) x 仅表示 f(g x),但很酷的是我们可以将我们的函数粘合在一起,并在稍后提供参数。使用这个我们得到:

function :: Num a => [a] -> [a]
function x = zipWith ((abs.).(-)) x (tail x)

(在is7s备注后更正,这里要处理两个参数,有点复杂)

【讨论】:

    【解决方案5】:

    我不知道这是否满足您的所有需求,但它在 ghci 中有效:

    let func a = [ abs( fst x - snd x ) | x <- zip a (tail a)]
    

    正在做:func [1,4,2,3]

    返回:[3,2,1]

    正如预期的那样。

    我还在开始,但我也想在学习过程中提供帮助。

    【讨论】:

      猜你喜欢
      • 2018-08-10
      • 2017-01-12
      • 2016-09-13
      • 1970-01-01
      • 1970-01-01
      • 2021-08-04
      • 2020-01-15
      • 2012-12-02
      • 1970-01-01
      相关资源
      最近更新 更多