[英]How to check if next element in a list is greater than the previous in Haskell?
(我目前正在 Haskell 上做一个在线课程,这是一个练习。我不是在寻找答案,而只是寻求一些关于如何进行的指导!)
我很难解决这个问题。 在命令式语言中,我会简单地使用一个循环,但由于 Haskell 并没有真正让我摸不着头脑。
我需要编写一个 function nextIsGreater:: [Int] -> [Int] ,给定一个数字列表,生成一个包含输入列表所有元素的列表,使得该元素后跟输入列表中的更大数字(下一个数字更大)。
到目前为止,这是我设法想出的。
nextIsGreater :: [Int] -> [Int]
nextIsGreater xs = [x | x <- init xs, y <- tail xs, x < y]
到目前为止,如果我在列表中只有两个数字,它就可以工作。 说 [0,5],它按预期返回 [0]。 如果我有,比如 [0,5,6],那么我的代码似乎会根据列表中的两个下一个数字检查 0 并在它应该返回 [0,5] 时返回 [0,0,5]。 我怎样才能将每个相邻的数字相互比较?
不错的尝试,但是
[x | x <- init xs, y <- tail xs, x < y]
对应于一个嵌套循环:您从init xs
中选择x
,然后对于这些选择中的每一个,您从tail xs
中选择所有可能的y
。
为了使这个想法按预期工作,您需要使用{-# LANGUAGE ParallelListComp #-}
或等效的zip来源:
nextIsGreater xs = [x | (x,y) <- zip (init xs) (tail xs), x<y]
但是有一个更简单的方法来获得两个连续元素的所有选择, tails
:
nextIsGreater xs = [x | (x:y:_) <- tails xs, x<y]
这个练习的目的几乎肯定是让你使用模式匹配编写一个标准的递归解决方案,而不是使用列表理解或更高级别的函数或类似的东西。
如果课程不错,您应该已经涵盖了一些递归的列表到列表转换,以及具有以下形式定义的函数:
foo :: [Int] -> [Int]
foo (x:xs) = ... something involving "x" and "foo xs" ...
foo [] = ...
或类似的,你应该按照同样的思路写一些东西。
这是第一个提示,下面还有更多剧透。
编写对列表的相邻元素进行操作的递归 function 的一种简单方法是编写一个命名前两个元素的模式:
foo (x:y:zs) = ...
“...”可以对x
和y
进行操作,然后执行递归调用以处理列表的“其余部分”。 递归调用可能是foo zs
或foo (y:zs)
(或根据某些条件在它们之间切换),具体取决于 function 在做什么。
因为这个模式只会匹配至少有两个元素的列表,你通常还需要模式来匹配一个元素和空列表:
foo [x] = ...
foo [] = ...
如果这还不够清楚,让我从一个不检查相邻元素的示例开始,刷新您的 memory 基本递归列表到列表转换。
剧透
.
.
.
假设我们要从列表中过滤掉所有偶数元素。 递归解决方案将考虑两种情况:
evens (x:xs) = ...
evens [] = ...
对于第一种情况,从x:xs
中提取所有偶数要么包括x
加上xs
中的所有偶数(即evens xs
) ,要么排除x
并仅包括偶数evens xs
,具体取决于x
本身是否偶数:
evens (x:xs) | even x = ...
| otherwise = ...
特别是,如果x
是偶数,答案应该包括x
和evens xs
:
evens (x:xs) | even x = x : evens xs
如果x
是奇数,答案就应该包括偶数evens xs
:
| otherwise = evens xs
最后一种情况是空列表中偶数的子集,它只是空列表:
evens [] = []
给出完整的定义:
evens :: [Int] -> [Int]
evens (x:xs) | even x = x : evens xs
| otherwise = evens xs
evens [] = []
您的示例中的主要区别在于包含x
的决定不仅取决于x
还取决于出现在x
之后的元素,因此让我们考虑一个稍微不同的问题:获取一个列表和 output后跟偶数的所有元素。
我们可能会考虑从类似的结构开始:
beforeEvens (x:xs) | ... = x : beforeEvens xs -- include x
| otherwise = beforeEvens xs -- exclude x
beforeEvens [] = []
其中“...”检查x
之后的元素(即xs
的第一个元素)是否为偶数。 例如,我们可能会调用一个单独的 function 来检查:
beforeEvens (x:xs) | headIsEven xs = x : beforeEvens xs
| otherwise = beforeEvens xs
beforeEvens [] = []
您应该能够编写headIsEven
的体面定义来完成此操作。 如果不使用head
而是使用模式匹配,则可以加分。 注意特殊情况headIsEven []
应该返回False
。
不过,更直接的方法是利用模式可用于检查列表开头的多个元素这一事实。 在这里,我们匹配一个命名前两个元素x
和y
的模式,加上列表zs
的 rest:
beforeEvens (x:y:zs) | even y = x : beforeEvens (y:zs)
| otherwise = beforeEvens (y:zs)
beforeEvens [x] = []
beforeEvens [] = []
请注意这里的几个棘手点。 如果我们匹配模式(x:y:zs)
,那么我们必须小心我们是否单独递归y:zs
或zs
。 这取决于y
是否应该被考虑包含在 output 中。此外,模式(x:y:zs)
不会匹配 singleton 列表,因此我们需要一个额外的模式匹配。
因为最后两个案例是一样的,我们可以将它们合并为一个案例:
beforeEvens (x:y:zs) | even y = x : beforeEvens (y:zs)
| otherwise = beforeEvens (y:zs)
beforeEvens _ = []
您应该会发现修改beforeEvens
以编写nextIsGreater
function 相对简单。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.