[英]Haskell get a filtered List of integers
場景:如果有一個整數數組,我想獲得整數數組作為回報,它們的總數不應超過10。
我是Haskell的初學者並在下面嘗試過。 如果有人能糾正我,我將不勝感激。
numbers :: [Int]
numbers = [1,2,3,4,5,6,7,8,9,10, 11, 12]
getUpTo :: [Int] -> Int -> [Int]
getUpTo (x:xs) max =
if max <= 10
then
max = max + x
getUpTo xs max
else
x
輸入
getUpTo numbers 0
預期產出
[1,2,3,4]
請注意:這不是背包問題的解決方案:)
我想出的一個非常快速的解決方案是以下一個。 當然解決完整的背包問題會更難,但如果你只需要一個快速的解決方案,這應該工作:
import Data.List (sort)
getUpTo :: Int -> [Int] -> [Int]
getUpTo max xs = go (sort xs) 0 []
where
go [] sum acc = acc
go (x:xs) sum acc
| x + sum <= max = go xs (x + sum) (x:acc)
| otherwise = acc
通過在其他所有內容之前對數組進行排序,我可以從頂部逐個獲取項目,直到超過最大值; 然后返回構建到該點的列表。
編輯:作為附注,我交換了前兩個參數的順序,因為這種方式應該對部分應用程序更有用。
出於教育目的(因為我想解釋一些東西:-),這里有一個不同的版本,它使用更多的標准功能。 由於它寫的速度較慢,因為它計算了一些總和,並且沒有保持總計。 另一方面,我認為它很好地表達了如何解決問題。
getUpTo :: [Int] -> [Int]
getUpTo = last . filter (\xs -> sum xs <= 10) . Data.List.inits
我把解決方案寫成了一個功能的“管道”; 如果將getUpTo
應用於數字列表, Data.List.inits
首先應用於列表,然后filter (\\xs -> sum xs <= 10)
應用於結果,最后last
應用於結果那個 。
那么,讓我們看看這三個函數的作用。 首先, Data.List.inits
以遞增的長度順序返回列表的初始段。 例如, Data.List.inits [2,3,4,5,6]
返回[[],[2],[2,3],[2,3,4],[2,3,4,5],[2,3,4,5,6]]
。 如您所見,這是一個整數列表的列表。
接下來, filter (\\xs -> sum xs <= 10)
按順序遍歷這些整數列表,如果它們的總和小於10則保留它們,否則丟棄它們。 的第一個參數filter
是其中給定的列表的謂詞xs
返回True
,如果總和xs
小於10,這可能是有點混亂在第一,所以一個例子以更簡單的謂詞是為了,我想。 filter even [1,2,3,4,5,6,7]
返回[2,4,6]
因為這是原始列表中的偶數值。 在前面的例子中,列表[]
, [2]
, [2,3]
和[2,3,4]
都有一個小於10的總和,但是[2,3,4,5]
和[2,3,4,5,6]
不要,所以filter (\\xs -> sum xs <= 10) . Data.List.inits
的結果filter (\\xs -> sum xs <= 10) . Data.List.inits
應用於[2,3,4,5,6]
filter (\\xs -> sum xs <= 10) . Data.List.inits
是[[],[2],[2,3],[2,3,4]]
,也是整數列表的列表。
最后一步是最簡單的:我們只返回整數列表列表的最后一個元素。 這原則上是不安全的,因為空列表的最后一個元素應該是什么? 在我們的例子中,我們很高興,因為inits
總是首先返回空列表[]
,其總和0,小於10 - 所以在列表列表中總是至少有一個元素我們正在最后的元素。 我們應用last
一個列表,其中包含原始列表的初始段,總和小於10,按長度排序。 換句話說:我們返回最長的初始段,總和小於10 - 這就是你想要的!
如果你的numbers
列表中有負數,這種做法可以返回你不期望的東西: getUpTo [10,4,-5,20]
返回[10,4,-5]
因為這是最長的初始值[10,4,-5,20]
,總和低於10; 即使[10,4]
高於10.如果這不是你想要的行為,並期望[10]
,那么你必須用takeWhile
替換filter
- 這基本上會在謂詞返回的第一個元素后立即停止過濾遇到False
。 例如takeWhile [2,4,1,3,6,8,5,7]
評估為[2,4]
。 所以在我們的例子中,使用takeWhile
停止總和超過10的時刻,而不是嘗試更長的段。
通過將getUpTo
編寫為函數組合,可以輕松更改算法的各個部分:如果您希望最長的初始段精確到10,則可以使用last . filter (\\xs -> sum xs == 10) . Data.List.inits
last . filter (\\xs -> sum xs == 10) . Data.List.inits
last . filter (\\xs -> sum xs == 10) . Data.List.inits
。 或者,如果您想要查看尾部,請使用head . filter (\\xs -> sum xs <= 10) . Data.List.tails
head . filter (\\xs -> sum xs <= 10) . Data.List.tails
head . filter (\\xs -> sum xs <= 10) . Data.List.tails
; 或考慮所有可能的子列表(即低效的背包解決方案!): last . filter (\\xs -> sum xs <= 10) . Data.List.sortBy (\\xs ys -> length xs
last . filter (\\xs -> sum xs <= 10) . Data.List.sortBy (\\xs ys -> length xs
last . filter (\\xs -> sum xs <= 10) . Data.List.sortBy (\\xs ys -> length xs
比較length ys) . Control.Monad.filterM (const [False,True])
length ys) . Control.Monad.filterM (const [False,True])
- 但我不打算在這里解釋一下,我已經漫游了很長時間!
有一個快速版本的答案; 但是,我認為查看代碼所需的最小更改以使其按預期方式工作也可能是有益的。
numbers :: [Int]
numbers = [1,2,3,4,5,6,7,8,9,10, 11, 12]
getUpTo :: [Int] -> Int -> [Int]
getUpTo (x:xs) max =
if max < 10 -- (<), not (<=)
then
-- return a list that still contains x;
-- can't reassign to max, but can send a
-- different value on to the next
-- iteration of getUpTo
x : getUpTo xs (max + x)
else
[] -- don't want to return any more values here
我是Haskell的新手。 幾個小時前我剛開始使用它,因此我在每個問題中都看到了一個挑戰,它幫助我擺脫了必要的思維方式,並有機會練習我的遞歸思考:)
我對這個問題進行了一些思考,我想出了這個,也許是天真的解決方案:
upToBound :: (Integral a) => [a] -> a -> [a]
upToBound (x:xs) bound =
let
summation _ [] = []
summation n (m:ms)
| n + m <= bound = m:summation (n + m) ms
| otherwise = []
in
summation 0 (x:xs)
我知道已經有了更好的答案,我只是為了它的樂趣而做到了。
我的印象是我改變了原始調用的簽名,因為我認為向外部函數調用提供初始零是沒有意義的,因為我只能假設它一開始只能為零。 因此,在我的實現中,我隱藏了來自調用者的種子,而是提供了最大限度,更有可能發生變化。
upToBound [1,2,3,4,5,6,7,8,9,0] 10
哪些產出: [1,2,3,4]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.