嗨,我开始学习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
是的,我同意你的意见。类型系统赢得的只有很多 - 没有理由通过编写不安全的代码来交换它的优势。 –