我正在尝试列出所有子字符串,其中每个子字符串只有一个原始字符串元素。
例如" 1234"会导致[" 1234"," 123"," 12"," 1"]
我想只使用前奏(没有导入)来实现这一点,所以不能使用子序列。
我是Haskell的新手,我知道我的代码存在一些问题,但目前还不知道如何修复它们。
slist :: String -> [String]
slist (x:xs) = (take (length (x:xs)) (x:xs)) ++ slist xs
如何使用
递归执行此操作编辑:想通过递归使用init来实现这个目标
答案 0 :(得分:3)
slist :: String -> [String]
slist [] = []
-- slist xs = [xs] ++ (slist $ init xs)
slist xs = xs : (slist $ init xs)
main = do
print $ slist "1234"
答案 1 :(得分:1)
这是一个非常懒惰的版本,适合处理无限列表。每个结果列表在第一个之后的每个元素只需要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)
splits
查找列表的所有初始序列以及尾随列表。
splits :: [a] -> [(Seq a, [a])]
splits = go empty
where
go s [] = []
go s (x:xs) = (s,x:xs):go (s |> x) xs
我们可以根据相同的策略从列表末尾写下来。
dropEnd :: Int -> [a] -> [a]
dropEnd n = uncurry (shiftThrough . fromList) . splitAt n
这些使用Data.Sequence的O(n)
的序列fromList
构建O(1)
,|>
附加到O(1)
和{{1}的序列末尾用viewl
检查序列的开始。
这足够快,可以在几秒钟内快速查询(starts [1..]) !! 80000
之类的内容和(starts [1..]) !! 8000000
。
队列的一个简单的纯功能实现是一对列表,一个包含按顺序输出next
的内容,另一个包含最新的内容added
。每当添加某些内容时,它都会添加到added
列表的开头。当需要某些内容时,该项目将从next
列表的开头删除。如果没有其他项目可以从next
列表中删除,则会以相反的顺序替换为added
列表,added
列表将设置为[]
。这已经摊销O(1)
运行时间,因为每个项目将被添加一次,删除一次,然后反转一次,但是许多反转将同时发生。
delay
使用上述队列逻辑实现与上一节shiftThrough
相同的操作。 xs
是最近added
项内容的列表,ys
是要使用的内容列表next
。
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
几乎是一次扫描
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
我们可以使用starts
和另一个返回列表的delay
版本来定义splits
。
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
的实现非常相似。
答案 2 :(得分:0)
这是一个有点复杂的版本:
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"
答案 3 :(得分:0)
更新了答案,列出了所有可能的子字符串(不仅仅是从根目录开始)。
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"]