String 如何列出子字符串?
我试图列出所有子字符串,其中每个子字符串的原始字符串中少了一个元素 e、 g“1234”将导致[“1234”、“123”、“12”、“1”] 我想实现这一点,只使用前奏(无导入),所以不能使用子序列 我是Haskell的新手,我知道代码中的一些问题,但目前不知道如何修复它们String 如何列出子字符串?,string,haskell,recursion,String,Haskell,Recursion,我试图列出所有子字符串,其中每个子字符串的原始字符串中少了一个元素 e、 g“1234”将导致[“1234”、“123”、“12”、“1”] 我想实现这一点,只使用前奏(无导入),所以不能使用子序列 我是Haskell的新手,我知道代码中的一些问题,但目前不知道如何修复它们 slist :: String -> [String] slist (x:xs) = (take (length (x:xs)) (x:xs)) ++ slist xs 如何使用 编辑:希望通过递归使用init来解决
slist :: String -> [String]
slist (x:xs) = (take (length (x:xs)) (x:xs)) ++ slist xs
如何使用
编辑:希望通过递归使用init来解决此问题,这里有一个有点复杂的版本:
slist :: String -> [String]
slist [] = []
-- slist xs = [xs] ++ (slist $ init xs)
slist xs = xs : (slist $ init xs)
main = do
print $ slist "1234"
slist xs = go (zip (repeat xs) [lenxs, lenxs - 1..1])
where lenxs = length xs
go [] = []
go (x:xs) = (take (snd x) (fst x)) : go xs
main = do
print $ slist "1234"
这里有一个非常懒惰的版本,适合处理无限列表。在第一个元素之后的每个结果列表的每个元素只需要
O(1)
摊销时间来计算它,无论我们看列表有多远
总体思路是:对于每个长度n
我们打算从末尾退出,我们将列表分成一个长度n
的项目队列和列表的其余部分。为了产生结果,我们首先检查列表中是否有另一个项目可以在队列中占有一席之地,然后产生队列中的第一个项目。当我们到达列表的末尾时,我们将丢弃队列中剩余的项目
import Data.Sequence (Seq, empty, fromList, ViewL (..), viewl, (|>))
starts :: [a] -> [[a]]
starts = map (uncurry shiftThrough) . splits
shiftThrough :: Seq a -> [a] -> [a]
shiftThrough queue [] = []
shiftThrough queue (x:xs) = q1:shiftThrough qs xs
where
(q1 :< qs) = viewl (queue |> x)
我们可以用同样的策略从列表的末尾写下删除
dropEnd :: Int -> [a] -> [a]
dropEnd n = uncurry (shiftThrough . fromList) . splitAt n
这些使用分期摊销的O(n)
从列表构建序列fromList
,O(1)
用|>
附加到序列的末尾,以及O(1)
用viewl
检查序列的开头
这足够快,可以查询(开始[1])之类的内容!!80000
非常快且(开始[1])!!8000000
在几秒钟内完成
听着,妈妈,没有进口货
队列的一个简单的纯功能实现是一对列表,一个包含要按顺序输出的next
,另一个包含最近添加的。无论何时添加内容,它都会添加到added
列表的开头。当需要某样东西时,该项目将从next
列表的开头删除。当下一个
列表中没有剩余的要删除的项目时,它将按相反顺序被添加的
列表替换,并且添加的
列表设置为[]
。这已经摊销了运行时间,因为每个项目将添加一次、删除一次和反转一次,但是许多反转将同时发生
delay
使用上述队列逻辑实现与上一节中的shift through
相同的功能xs
是最近添加的内容列表
,ys
是下一步使用的内容列表
delay :: [a] -> [a] -> [a]
delay ys = traverse step ([],ys)
where
step (xs, ys) x = step' (x:xs) ys
step' xs [] = step' [] (reverse xs)
step' xs (y:ys) = (y, (xs, ys))
遍历
几乎就是一次扫描
traverse :: (s -> a -> (b, s)) -> s -> [a] -> [b]
traverse f = go
where
go _ [] = []
go s (x:xs) = y : go s' xs
where (y, s') = f s x
我们可以根据delay
和另一个返回列表的splits
版本定义start
starts :: [a] -> [[a]]
starts = map (uncurry delay) . splits
splits :: [a] -> [([a], [a])]
splits = go []
where
go s [] = []
go s (x:xs) = (reverse s, x:xs):go (x:s) xs
这与使用更新的答案列出所有可能的子字符串(而不仅仅是从根开始)的Seq
实现的性能非常相似
所以你想要init。相反。初始化
?这需要数据。List
,但是你可以去看看源代码,看看如何自己实现它。另外,我建议将inits
作为第一个结果,相关的tails
函数就在这里。4.编写此函数的惰性版本是一个有趣的挑战。建议编辑:slist xs=xs:(slist$init xs)
++
比更昂贵:
这列出了从1开始的所有子字符串,但不是所有子字符串。OP仅使用Prelude(无导入)指定。不过回答不错。
starts :: [a] -> [[a]]
starts = map (uncurry delay) . splits
splits :: [a] -> [([a], [a])]
splits = go []
where
go s [] = []
go s (x:xs) = (reverse s, x:xs):go (x:s) xs
slist :: [t] -> [[t]]
slist [] = []
slist xs = xs : (slist $ init xs ) # Taken from Pratik Deoghare's post
all_substrings:: [t] -> [[t]]
all_substrings (x:[]) = [[x]]
all_substrings (x:xs) = slist z ++ all_substrings xs
where z = x:xs
λ> all_substrings "1234"
["1234","123","12","1","234","23","2","34","3","4"]