2017-04-08 61 views

回答

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] 

最后一步了所有类型的一个缺点例如,我们可以使用利弊或<概括这个|。

> [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" 
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

+0

如果通过了负整数,则您的解决方案不起作用。我认为在这种情况下不要默默地失败,但返回有意义的东西或禁止传递不正确的输入是很好的技巧。 – Shersh

+0

是的,我同意你的意见。类型系统赢得的只有很多 - 没有理由通过编写不安全的代码来交换它的优势。 –