[英]How to check if next element in a list is greater than the previous in Haskell?
(I am currently doing an online course on Haskell and this is an exercise. I am not looking for answers, but simply for some pointers on how to proceed!) (我目前正在 Haskell 上做一个在线课程,这是一个练习。我不是在寻找答案,而只是寻求一些关于如何进行的指导!)
I have trouble wrapping my head around this.我很难解决这个问题。 In imperative languages I would simply use a loop, but since Haskell doesn't really have those I am left scratching my head.
在命令式语言中,我会简单地使用一个循环,但由于 Haskell 并没有真正让我摸不着头脑。
I need to write a function nextIsGreater:: [Int] -> [Int] that, given a list of numbers, produces a list with all elements of the input list such that the element is followed by a greater number in the input list (the next number is greater).我需要编写一个 function nextIsGreater:: [Int] -> [Int] ,给定一个数字列表,生成一个包含输入列表所有元素的列表,使得该元素后跟输入列表中的更大数字(下一个数字更大)。
Here is what I've managed to come up with so far.到目前为止,这是我设法想出的。
nextIsGreater :: [Int] -> [Int]
nextIsGreater xs = [x | x <- init xs, y <- tail xs, x < y]
So far it works if I have only two numbers in the list.到目前为止,如果我在列表中只有两个数字,它就可以工作。 Say [0,5], it returns [0] as it is supposed to.
说 [0,5],它按预期返回 [0]。 If I have, say [0,5,6] then my code seems to check the 0 against both of the next numbers in the list and returns [0,0,5], when it should return [0,5].
如果我有,比如 [0,5,6],那么我的代码似乎会根据列表中的两个下一个数字检查 0 并在它应该返回 [0,5] 时返回 [0,0,5]。 How could I compare each adjacent number to eachother?
我怎样才能将每个相邻的数字相互比较?
Not a bad attempt, but不错的尝试,但是
[x | x <- init xs, y <- tail xs, x < y]
corresponds to a nested loop: you choose x
from init xs
, and then for each of these choices you pick all possible y
from tail xs
.对应于一个嵌套循环:您从
init xs
中选择x
,然后对于这些选择中的每一个,您从tail xs
中选择所有可能的y
。
To make the idea work as intended, you'd need to use {-# LANGUAGE ParallelListComp #-}
or equivalently zip the sources:为了使这个想法按预期工作,您需要使用
{-# LANGUAGE ParallelListComp #-}
或等效的zip来源:
nextIsGreater xs = [x | (x,y) <- zip (init xs) (tail xs), x<y]
But there's a simpler way to obtain all choices of two consecutive elements, with tails
:但是有一个更简单的方法来获得两个连续元素的所有选择,
tails
:
nextIsGreater xs = [x | (x:y:_) <- tails xs, x<y]
The intention of this exercise is almost certainly to have you write a standard recursive solution using pattern matching, not use list comprehensions or higher level functions or anything like that.这个练习的目的几乎肯定是让你使用模式匹配编写一个标准的递归解决方案,而不是使用列表理解或更高级别的函数或类似的东西。
If the course is any good, you should already have covered some recursive list-to-list transformations, functions with definitions of the form:如果课程不错,您应该已经涵盖了一些递归的列表到列表转换,以及具有以下形式定义的函数:
foo :: [Int] -> [Int]
foo (x:xs) = ... something involving "x" and "foo xs" ...
foo [] = ...
or similar, and you're expected to write something along the same lines.或类似的,你应该按照同样的思路写一些东西。
Here's a first hint, with further spoilers below.这是第一个提示,下面还有更多剧透。
A simple way of writing a recursive function that operates on adjacent elements of lists is to write a pattern that names the first two elements:编写对列表的相邻元素进行操作的递归 function 的一种简单方法是编写一个命名前两个元素的模式:
foo (x:y:zs) = ...
The "..." can operate on x
and y
, and then perform a recursive call to process the "rest" of the list. “...”可以对
x
和y
进行操作,然后执行递归调用以处理列表的“其余部分”。 The recursive call might be either foo zs
or foo (y:zs)
(or switch between those based on some condition), depending on what the function is doing.递归调用可能是
foo zs
或foo (y:zs)
(或根据某些条件在它们之间切换),具体取决于 function 在做什么。
Because this pattern will only match lists with at least two elements, you will usually also need patterns to match both one-element and empty lists:因为这个模式只会匹配至少有两个元素的列表,你通常还需要模式来匹配一个元素和空列表:
foo [x] = ...
foo [] = ...
If that's not clear enough, let me refresh your memory on basic recursive list-to-list transformations starting with an example that doesn't inspect adjacent elements.如果这还不够清楚,让我从一个不检查相邻元素的示例开始,刷新您的 memory 基本递归列表到列表转换。
SPOILERS剧透
. .
. .
. .
Suppose we want to filter out all even elements from a list.假设我们要从列表中过滤掉所有偶数元素。 A recursive solution would consider the two cases:
递归解决方案将考虑两种情况:
evens (x:xs) = ...
evens [] = ...
For the first case, the extraction of all evens from x:xs
either includes x
plus all the evens from xs
(ie, evens xs
) or excludes x
and includes only evens xs
, depending on whether or not x
itself even:对于第一种情况,从
x:xs
中提取所有偶数要么包括x
加上xs
中的所有偶数(即evens xs
) ,要么排除x
并仅包括偶数evens xs
,具体取决于x
本身是否偶数:
evens (x:xs) | even x = ...
| otherwise = ...
In particular, if x
is even, the answer should include x
together with evens xs
:特别是,如果
x
是偶数,答案应该包括x
和evens xs
:
evens (x:xs) | even x = x : evens xs
and if x
is odd, the answer just should include evens xs
:如果
x
是奇数,答案就应该包括偶数evens xs
:
| otherwise = evens xs
The final case is the subset of even numbers from the empty list, which is just the empty list:最后一种情况是空列表中偶数的子集,它只是空列表:
evens [] = []
giving the complete definition:给出完整的定义:
evens :: [Int] -> [Int]
evens (x:xs) | even x = x : evens xs
| otherwise = evens xs
evens [] = []
The main difference in your example is that the decision to include x
depends not only on x
but on the element appearing after x
, so let's consider a slightly different problem: take a list and output all elements that are followed by an even number.您的示例中的主要区别在于包含
x
的决定不仅取决于x
还取决于出现在x
之后的元素,因此让我们考虑一个稍微不同的问题:获取一个列表和 output后跟偶数的所有元素。
We might consider starting with a similar structure:我们可能会考虑从类似的结构开始:
beforeEvens (x:xs) | ... = x : beforeEvens xs -- include x
| otherwise = beforeEvens xs -- exclude x
beforeEvens [] = []
where "..." checks to see if the element after x
(ie, the first element of xs
) is even.其中“...”检查
x
之后的元素(即xs
的第一个元素)是否为偶数。 For example, we might call a separate function to check this:例如,我们可能会调用一个单独的 function 来检查:
beforeEvens (x:xs) | headIsEven xs = x : beforeEvens xs
| otherwise = beforeEvens xs
beforeEvens [] = []
You ought to be able to write a decent definition of headIsEven
to complete this.您应该能够编写
headIsEven
的体面定义来完成此操作。 Bonus points if instead of using head
, it uses pattern matching.如果不使用
head
而是使用模式匹配,则可以加分。 Note the special case headIsEven []
should return False
.注意特殊情况
headIsEven []
应该返回False
。
A more direct approach, though, is to take advantage of the fact that patterns can be used to examine multiple elements at the start of the list.不过,更直接的方法是利用模式可用于检查列表开头的多个元素这一事实。 Here, we match a pattern that names the first two elements
x
and y
, plus the rest of the list zs
:在这里,我们匹配一个命名前两个元素
x
和y
的模式,加上列表zs
的 rest:
beforeEvens (x:y:zs) | even y = x : beforeEvens (y:zs)
| otherwise = beforeEvens (y:zs)
beforeEvens [x] = []
beforeEvens [] = []
Note a couple of tricky points here.请注意这里的几个棘手点。 If we match against the pattern
(x:y:zs)
, then we have to be careful about whether we recurse on y:zs
or zs
alone.如果我们匹配模式
(x:y:zs)
,那么我们必须小心我们是否单独递归y:zs
或zs
。 It depends on whether y
should or shouldn't be considered for inclusion in the output. Also, the pattern (x:y:zs)
won't match a singleton list, so we need an extra pattern match on that.这取决于
y
是否应该被考虑包含在 output 中。此外,模式(x:y:zs)
不会匹配 singleton 列表,因此我们需要一个额外的模式匹配。
Because the last two cases are the same, we can combine them into a single case:因为最后两个案例是一样的,我们可以将它们合并为一个案例:
beforeEvens (x:y:zs) | even y = x : beforeEvens (y:zs)
| otherwise = beforeEvens (y:zs)
beforeEvens _ = []
You should find it relatively straightforward to modify beforeEvens
to write your nextIsGreater
function.您应该会发现修改
beforeEvens
以编写nextIsGreater
function 相对简单。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.