您可以构建如下insertAt函数:
insertAt :: a -> Int -> [a] -> [a]
insertAt newElement 0 as = newElement:as
insertAt newElement i (a:as) = a : insertAt newElement (i - 1) as
Run Code Online (Sandbox Code Playgroud)
解决此类问题的一种策略是针对某些边缘情况编写代码,并使用递归直到找到边缘情况。
在代码可能变得更短,更易于编写的意义上,上述镜头包简化了处理数据结构的过程,但以学习额外的库为代价。
这两个示例都强调了有几种方法可以解决您的问题。我建议看一下Data.List模块,以进一步了解典型的列表操作。该源的drop功能可能是一个良好的开端为您服务。同样,提供的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
Run Code Online (Sandbox Code Playgroud)
此实现尝试通过在有疑问时立即插入立即执行正确的操作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
Run Code Online (Sandbox Code Playgroud)
此版本也List.splitAt为了简洁起见。