嗨,我開始學習haskell,但我無法弄清楚一個主題。Haskell在第n個位置插入一個元素
假設我有一個列表:[1,2,3],並且我試圖編寫一個函數來在第n個位置插入元素。任何線索我如何做到這一點?
嗨,我開始學習haskell,但我無法弄清楚一個主題。Haskell在第n個位置插入一個元素
假設我有一個列表:[1,2,3],並且我試圖編寫一個函數來在第n個位置插入元素。任何線索我如何做到這一點?
使用lens pacakge
如果我們知道功能ID開始可以像一個鏡頭:
import Control.Lens
> [1,2,3,4] ^. id
[1,2,3,4]
然後我們就可以移動到列表如何可以修改:
> [1,2,3,4] & id %~ (99:)
[99,1,2,3,4]
以上允許插入列表的開始。要關注列表的後面部分,我們可以使用Control.Lens.Cons模塊中的_tail。
> [1,2,3,4] ^. _tail
[2,3,4]
> [1,2,3,4] & _tail %~ (99:)
[1,99,2,3,4]
我們概括本作的第n個位置
> :{
let
_drop 0 = id
_drop n = _tail . _drop (n - 1)
:}
> [1,2,3,4] ^. _drop 1
[2,3,4]
> [1,2,3,4] & _drop 0 %~ (99:)
[99,1,2,3,4]
> [1,2,3,4] & _drop 1 %~ (99:)
[1,99,2,3,4]
最後一步了所有類型的一個缺點例如,我們可以使用利弊或<概括這個|。
> [1,2,3,4] & _drop 1 %~ (99<|)
[1,99,2,3,4]
> import Data.Text
> :set -XOverloadedStrings
> ("h there"::Text) & _drop 1 %~ ('i'<|)
"hi there"
你可以建立一個insertAt
功能是這樣的:
insertAt :: a -> Int -> [a] -> [a]
insertAt newElement 0 as = newElement:as
insertAt newElement i (a:as) = a : insertAt newElement (i - 1) as
一種策略是,直到邊緣的情況下被發現對於一些邊緣情況和使用遞歸編寫代碼。
上述lens包簡化了處理數據結構,因爲代碼可能變得更短且更好編寫,但代價是要額外學習一個庫。
這兩個例子都強調了有幾種方法可以解決您的問題。我建議查看Data.List模塊,以獲得對典型列表操作的進一步瞭解。 drop
函數的source可能是您的良好開端。 也提供splitAt
功能可能是一個適合您的問題的積木。
由於Shersh提到正確上述實施insertAt
是有點瑕疵:它不檢查消極立場,以及在一個給定的只是繼續遞歸的情況下。如果列表無限,這可能特別糟糕。
我們可以很容易地通過使用警衛提高執行:
insertAt :: a -> Int -> [a] -> [a]
insertAt newElement _ [] = [newElement]
insertAt newElement i (a:as)
| i <= 0 = newElement:a:as
| otherwise = a : insertAt newElement (i - 1) as
此實現試圖通過有疑問時,請立即插入newElement
做正確的事情。 也可以寫一個版本的insertAt
,與其拋出錯誤到我們的臉:
import Data.Monoid ((<>))
import qualified Data.List as List
insertAt :: a -> Int -> [a] -> [a]
insertAt newElement i as
| null as && i != 0 = error "Cannot insert into empty list other than position 0."
| null as && i == 0 = [newElement]
| i >= 0 = let (prefix, suffix) = List.splitAt i
in prefix <> [i] <> suffix
這個版本也使得簡潔的使用List.splitAt
。
如果通過了負整數,則您的解決方案不起作用。我認爲在這種情況下不要默默地失敗,但返回有意義的東西或禁止傳遞不正確的輸入是很好的技巧。 – Shersh
是的,我同意你的意見。類型系統贏得的只有很多 - 沒有理由通過編寫不安全的代碼來交換它的優勢。 –