閱讀 Get Programming with Haskell 一書,其中一個問題是查找給定元素是否在串列的前半部分。這可以作為
isInFirstHalf x xs = elem x firstHalf
where firstHalf = take (length xs `div` 2) xs
但是,問題是這里length遍歷了整個串列。在命令式語言中,可以通過跟蹤元素索引和當前計數器來盡早縮短回圈。例如,如果串列有一百萬個元素,并且在第三個元素上有一個匹配,一旦你完成對第六個元素的回圈,你可以立即回傳 true。
我的問題是是否有辦法在 Haskell 中實作這樣的東西。
uj5u.com熱心網友回復:
當然。
halfAsLong (x:_:xs) = x:halfAsLong xs
halfAsLong _ = []
isInFirstHalf x xs = elem x (zipWith const xs (halfAsLong xs))
試試看:
> isInFirstHalf 3 (1:2:3:4:5:6:undefined)
True
讀者練習:
- 你提議的命令式解決方案的元素索引和當前計數器去哪兒了?(它們仍然在那里,只是以我認為有點微妙的方式隱藏了!)
- 當將奇數長度分成兩半時,這會向下取整,就像
length xs `div` 2這樣。代碼必須如何更改才能四舍五入,就像(length xs 1) `div` 2這樣?
uj5u.com熱心網友回復:
Daniel Wagner 發布了一個非常好的答案,表明您畢竟并不真正需要索引。
盡管如此,如果您確實想使用索引,則可以按如下方式制作解決方案。
- 我們通過將它們與它們的索引配對來列舉所有串列元素。這是通過使用
zip [0..] xs(或者zip [1..] xs如果您想從 1 開始計數)來完成的。 - 我們查找您
x是否在串列中,i如果存在則查找其索引。可以通過直接遞回進行,或者使用類似的方法dropWhile ((/= x) . fst) ...然后測驗結果。 - 一旦我們知道
i,我們需要檢查之后是否至少有i元素。這可以通過直接遞回解決,也可以通過droppingi-1元素并測驗結果是否為非空串列來解決。
當然,還有其他選擇。我們可以例如跳過列舉的元素zip [0..]通過跟蹤當前索引,并使用遞回:foo n x (y:ys) = ... foo (n 1) x ys ...。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/394427.html
標籤:哈斯克尔
