[英]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.