我正在尝试创建一个执行以下操作的函数:它获取一个列表,例如[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
Run Code Online (Sandbox Code Playgroud)
但它给了我错误,我找不到任何解决方案.
亲切的问候,
编辑:
谢谢你们,今天学到了很多东西.确实我是初学者,我正在上大学课程,给我prolog,haskell,scala,python,aspectj和metaprogramming.所以我们每个课程有2个小时的课程,之后有一段时间可以做一些练习.
下周一我有考试和拉链等...我们必须编写自己的功能.但感谢良好的解释教程,学到了很多东西.这是工作解决方案:
function :: Num a => [a] -> [a]
function (x:y:xs) = [abs(x - y)] ++ function (y:xs)
function _ = []
Run Code Online (Sandbox Code Playgroud)
如果您仍然对递归解决方案感兴趣.
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
Run Code Online (Sandbox Code Playgroud)
所以(-) :: Num a => a -> a -> a运营商要求,它的论点应该是Num.所以我们可以解决它function :: Num a => [a] -> [a].
现在我们还有另一个问题:
> function [1,4,2,3]
[-3,2,-1,*** Exception: Prelude.head: empty list
Run Code Online (Sandbox Code Playgroud)
所以我们应该处理一些情况:
function :: Num a => [a] -> [a]
function [] = []
function [_] = []
function (x:xs) = x - head xs : function xs
Run Code Online (Sandbox Code Playgroud)
但它仍然不是我们实际预期的:
> function [1,4,2,3]
[-3,2,-1]
Run Code Online (Sandbox Code Playgroud)
所以我们应该添加abs功能:
function :: Num a => [a] -> [a]
function [] = []
function [_] = []
function (x:xs) = abs ( x - head xs ) : function xs
Run Code Online (Sandbox Code Playgroud)
完成
> function [1,4,2,3]
[3,2,1]
Run Code Online (Sandbox Code Playgroud)
除此之外,您可以非常轻松地使用更易读的代码.
你怎么能找到两个列表之间的差异?zipWith看起来真有帮助.例如:
> zipWith (-) [1,2,3,4] [1,1,1]
[0,1,2]
Run Code Online (Sandbox Code Playgroud)
所以想法是zipWith (-)原始列表,它是tail.
> let x = [1,2,3,4]
> zipWith (-) x (tail x)
[-1,-1,-1]
Run Code Online (Sandbox Code Playgroud)
而你function可能喜欢这样:
function :: Num a => [a] -> [a]
function x = map abs $ zipWith (-) x (tail x)
Run Code Online (Sandbox Code Playgroud)
完成
> function [1,4,2,3]
[3,2,1]
Run Code Online (Sandbox Code Playgroud)
一般来说,告诉我们您遇到的错误是一个好主意,而不是期望我们自己输入代码并自行运行.但这是我一眼就能看到的:
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([]),这是一个错误,因为空列表没有头部.
修复:想想我们想要的东西.当我们对列表进行模式匹配时,知道它有一个head(x)是不够的.我们还需要知道它有第二个元素(y).所以用.替换方程
function (x:y:xs) = [x - y] ++ function (y:xs)
function _ = []
Run Code Online (Sandbox Code Playgroud)
该_场比赛任何价值可言.当列表包含两个或更多元素时,第一个等式匹配,因此第二个等式将清空空列表和单个元素列表情况.
*:我不是说它实际按此顺序进行评估.但这是纯代码(和有限数据),因此我们评估事物的顺序并不重要.
如果你可以帮助它,你真的不想自己做明确的递归,你想要使用更高阶的函数; 部分原因是因为这意味着你不必自己编写递归(并且冒错了),部分原因是你可以经常将模式匹配移到高阶函数上(避免可能也出错).因此,采取sclv的建议是:使用zipWith和drop 1(而忽略丹尼尔·费舍尔这一次,你不要想进入的习惯需要去关心它是否安全使用head是理所当然的事,当drop 1 []收益[]是非常有用的).
使用更高阶函数而不是模式匹配和显式递归的其他优点: