即inits "abc" == ["","a","ab","abc"]
Data.List
中有inits
的标准版本,但在下面我自己编写了一个版本:
myInits = f id
where
f start (l:ls) = (start []):(f (start . (l:)) ls)
f start [] = [(start [])]
虽然我的版本比标准版本简单得多,但我怀疑它因效率原因而不太好。我怀疑在完全评估myInits l
时,它需要O(n^2)
个空格。例如,myTails
,tails
的实现:
myTails a@(_:ls) = a:(myTails ls)
myTails [] = [[]]
这几乎与标准版本完全相同,我怀疑通过重用列表的尾部来实现O(n)
空间。
有人可以解释一下:
inits
版本不好。Data.List
中的标准方法或您自己的方法)。答案 0 :(得分:8)
您的myInits
使用称为difference list的技术来制作以线性时间构建列表的函数。我相信,但尚未检查,完全评估myInits
的运行时间是O(n^2)
需要O(n^2)
空间。全面评估inits
还需要O(n^2)
运行时间和空间。任何版本的inits
都需要O(n^2)
个空格;使用:
和[]
构建的列表只能共享其尾部,inits
的结果中没有共同的尾部。 inits
中Data.List
的版本使用摊销时间O(1)
队列,与simpler queue described in the second half of a related answer非常相似。 Snoc
referenced in the source code in Data.List
是Cons
上的文字游戏(:
的另一个名称)向后 - 可以将项目附加到列表的末尾。
简要地试验这些函数表明myInits
在大型列表中稀疏使用时表现令人满意。在我的计算机上,在ghci中,myInits [1..] !! 8000000
会在几秒钟内产生结果。不幸的是,我有ghc 7.8.3附带的horrifyingly inefficient implementation,因此我无法将myInits
与inits
进行比较。
myInits
和inits
以及myTails
和tails
之间存在一个很大的区别。当应用于undefined
或_|_
时,它们具有不同的含义(发音为“bottom”,我们用于undefined
的另一个符号)。
inits
具有严格性属性inits (xs ++ _|_) = inits xs ++ _|_
,当xs
为空列表时[]
表示inits
仍然会产生至少一个结果已应用于undefined
inits (xs ++ _|_) = inits xs ++ _|_
inits ([] ++ _|_) = inits [] ++ _|_
inits _|_ = [[]] ++ _|_
inits _|_ = [] : _|_
我们可以通过实验看到
> head . inits $ undefined
[]
myInits
对于空列表或更长的列表没有此属性。
> head $ myInits undefined
*** Exception: Prelude.undefined
> take 3 $ myInits ([1,2] ++ undefined)
[[],[1]*** Exception: Prelude.undefined
如果我们发现f
中的myInits
会在任一分支中产生start []
,我们就可以解决此问题。因此,我们可以延迟模式匹配,直到需要决定下一步该做什么。
myInits' = f id
where
f start list = (start []):
case list of
(l:ls) -> f (start . (l:)) ls
[] -> []
懒惰的增加使myInits'
的工作与inits
一样。
> head $ myInits' undefined
[]
> take 3 $ myInits' ([1,2] ++ undefined)
[[],[1],[1,2]]
同样,myTails
和tails
in Data.List
之间的区别在于tails
会在决定是否有剩余的列表之前将整个列表作为第一个结果。文档说它服从tails _|_ = _|_ : _|_
,但它实际上遵循一个更难以描述的更强大的规则。
答案 1 :(得分:5)
前缀函数构建可以作为实际列表与其实现分开:
diffInits = map ($ []) . scanl (\a x -> a . (x:)) id
这明显更快(在GHCi内部测试),并且比您的版本更懒惰(请参阅Cirdec's answer进行讨论):
diffInits _|_ == [] : _|_
diffInits (xs ++ _|_) == diffInits xs ++ _|_