您好我开始学习haskell,但我无法找出一个主题。
我们说我有一个列表:[1,2,3]并且我试图编写一个函数来插入第n个位置的元素。任何线索我该怎么做?
答案 0 :(得分:4)
您可以像这样构建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
。
答案 1 :(得分:2)
使用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]
使用cons实例对所有类型进行概括的最后一步我们可以使用cons或&lt; |。
> [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"
答案 2 :(得分:0)
这应该适用于正整数和负整数。
insertAt :: [a] -> a -> Int -> [a]
insertAt [] elem pos = [elem]
insertAt (x:xs) elem pos
| pos == 0 = elem : x : xs
| pos > 0 = x : insertAt xs elem (pos - 1)
| otherwise = x : insertAt xs elem ((pos) + length (x:xs) )