我尝试创建一个执行以下操作的函数:它获取一个列表,例如[1,4,2,3]
,它会返回一个列表[3,2,1]
。
因为1 - 4 = 3
(绝对值),4 - 2 = 2
和2 - 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 _ = []
答案 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)
一般来说,最好告诉我们您遇到的错误,而不是希望我们自己输入代码并自行运行。但这是我一眼就能看到的:
function :: [a] -> [a]
表示function
适用于任何类型的列表。但后来你说x - head(xs)
。我们在这里x :: a
和head(xs) :: a
。您期望减法运算符可以采用任意两个值,只要它们具有相同的类型。
但是(-) :: Num a => a -> a -> a
;这两个值具有相同的类型是不够的,该类型必须实现Num
类型类(它还提供加法,乘法,绝对值和一些其他函数)。
要修复:将类型签名替换为function :: Num a => [a] -> [a]
。
代码现在应该编译,但是当你运行它时,你会得到一个关于占据空列表头部的错误消息。
让我们逐步了解运行function [4]
* 时会发生什么:
第一个等式在左侧有function []
,但[4]
与[]
不匹配,因此请跳过它。
第二个等式在左侧有function (x:xs)
。 [4]
匹配(x:xs)
给我们x = 4
和xs = []
,因此我们将继续使用此等式并评估[4 - head([])] ++ function []
。
这涉及评估[4 - head([])]
,其中涉及评估4 - head([])
,其中涉及评估head([])
,这是一个错误,因为空列表没有头。< / p>
修复:想想我们想要的东西。当我们对列表进行模式匹配时,仅知道它有一个头(x
)是不够的。我们还需要知道它有第二个元素(y
)。所以用
function (x:y:xs) = [x - y] ++ function (y:xs)
function _ = []
_
匹配任何值。当列表包含两个或更多元素时,第一个等式匹配,因此第二个等式将清空空列表和单个元素列表的情况。
* :我不是说它实际按此顺序进行评估。但这是纯粹的代码(和有限的数据),因此我们评估事物的顺序并不重要。
如果你可以提供帮助,你真的不想自己做明确的递归,你想要使用更高阶的函数;部分原因是因为这意味着你不必自己编写递归(并且冒错了),部分原因是你可以经常将模式匹配移到高阶函数上(避免可能也出错)。所以请听sclv的建议:使用zipWith
和drop 1
(并忽略Daniel Fischer一次:你不想养成需要关心它是否安全的习惯当head
屈服drop 1 []
有用时,使用[]
是理所当然的。
使用更高阶函数而不是模式匹配和显式递归的其他优点:
答案 2 :(得分:4)
查看zipWith
和drop 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]
正如所料。
我还在开玩笑,但我也想帮助,因为我也学到了。