创建一个新的列表,每个值都有差异

时间:2012-03-07 16:23:31

标签: haskell

我尝试创建一个执行以下操作的函数:它获取一个列表,例如[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和metaprogramming。因此,我们每个课程有2个小时的课程,之后有一段时间可以进行一些练习。

下周一我有考试和拉链等...我们必须编写自己的功能。但感谢良好的解释教程,学到了很多东西。这是可行的解决方案:

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

5 个答案:

答案 0 :(得分:5)

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

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]

答案 1 :(得分:5)

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

  1. function :: [a] -> [a]表示function适用于任何类型的列表。但后来你说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([]),这是一个错误,因为空列表没有头。< / p>

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

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

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

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

  3. 如果你可以提供帮助,你真的不想自己做明确的递归,你想要使用更高阶的函数;部分原因是因为这意味着你不必自己编写递归(并且冒错了),部分原因是你可以经常将模式匹配移到高阶函数上(避免可能也出错)。所以请听sclv的建议:使用zipWithdrop 1(并忽略Daniel Fischer一次:你不想养成需要关心它是否安全的习惯当head屈服drop 1 []有用时,使用[]是理所当然的。

    使用更高阶函数而不是模式匹配和显式递归的其他优点:

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

答案 2 :(得分:4)

查看zipWithdrop 1 ...

答案 3 :(得分:0)

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评论之后更正了,因为我们必须在这里处理两个参数,所以它有点复杂)

答案 4 :(得分:0)

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

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

执行:func [1,4,2,3]

返回:[3,2,1]

正如所料。

我还在开玩笑,但我也想帮助,因为我也学到了。