(我目前正在做關于 Haskell 的在線課程,這是一個練習。我不是在尋找答案,而只是為了獲得一些關于如何進行的指示!)
我很難解決這個問題。在命令式語言中,我會簡單地使用回圈,但由于 Haskell 并沒有真正的回圈,我只能摸不著頭腦。
我需要撰寫一個函式 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,0,5],而它應該回傳 [0,5]。我如何將每個相鄰的數字相互比較?
uj5u.com熱心網友回復:
不錯的嘗試,但是
[x | x <- init xs, y <- tail xs, x < y]
對應于一個嵌套回圈:您x從中選擇init xs,然后對于這些選擇中的每一個,您從 中選擇所有可能y的tail xs選項。
為了使這個想法按預期作業,您需要使用{-# LANGUAGE ParallelListComp #-}或等效地壓縮源代碼:
nextIsGreater xs = [x | (x,y) <- zip (init xs) (tail xs), x<y]
但是有一種更簡單的方法來獲得兩個連續元素的所有選擇,其中tails:
nextIsGreater xs = [x | (x:y:_) <- tails xs, x<y]
uj5u.com熱心網友回復:
本練習的目的幾乎可以肯定是讓您使用模式匹配撰寫標準遞回解決方案,而不是使用串列推導或更高級別的函式或類似的東西。
如果這門課程不錯,您應該已經學習了一些遞回串列到串列的轉換,具有以下形式定義的函式:
foo :: [Int] -> [Int]
foo (x:xs) = ... something involving "x" and "foo xs" ...
foo [] = ...
或類似的,并且您應該按照相同的思路寫一些東西。
這是第一個提示,下面有更多的劇透。
撰寫對串列的相鄰元素進行操作的遞回函式的一種簡單方法是撰寫一個命名前兩個元素的模式:
foo (x:y:zs) = ...
"..." 可以對xand進行操作y,然后執行遞回呼叫來處理串列的 "rest"。遞回呼叫可能是foo zs或foo (y:zs)(或根據某些條件在它們之間切換),具體取決于函式正在執行的操作。
因為這個模式只匹配至少有兩個元素的串列,你通常還需要模式來匹配一個元素和空串列:
foo [x] = ...
foo [] = ...
如果這還不夠清楚,讓我從一個不檢查相鄰元素的示例開始,讓您重新了解基本的遞回串列到串列轉換。
劇透
.
.
.
假設我們要從串列中過濾掉所有偶數元素。遞回解決方案將考慮以下兩種情況:
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 (i.e., evens xs) or excludes x and includes only evens xs, depending on whether or not x itself even:
evens (x:xs) | even x = ...
| otherwise = ...
In particular, if x is even, the answer should include x together with evens xs:
evens (x:xs) | even x = x : evens xs
and if x is odd, the answer just should include 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.
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 (i.e., the first element of xs) is even. For example, we might call a separate function to check this:
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. Bonus points if instead of using head, it uses pattern matching. Note the special case headIsEven [] should return 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:
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. 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.
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.
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/427275.html
