按开始和结束标记过滤列表

时间:2016-02-10 00:04:46

标签: haskell

说我有一个字符串列表:

let listA = ["hello", "start", "stuff", "end", "boo"]

我想根据开始和结束标记"start""end"过滤此列表。所以我的最终结果应该是:

["start", "stuff", "end"]

我需要一个能够做到这一点的函数filterByTokens。这就是我到目前为止所做的一切,但我正在撞墙:

slice :: Int -> Int -> [a] -> [a]
slice _ _ [] = []
slice from to list = take (to - from + 1) rest
    where rest = drop from list

filterByTokens :: a -> a -> [a] -> [a]
filterByTokens start end list = 
    sIndex = elemIndex start list
    eIndex = elemIndex end list
    slice sIndex eIndex list

filterRange可以在一个块上运行,但是如果我有多个块呢?另外,elemIndex会返回Maybe类型,那么如何将其转换为Int?请帮忙。

更新

我已经递归地将我的函数更新为take,但我还在碰壁:

takeBetween :: (Eq a) => a -> a -> [a] -> [a]
takeBetween _ _ [] = []
takeBetween start end (x:xs)
    | x == start    = takeIt True (x:xs)
    | x == end      = takeIt False (x:xs)
    | otherwise     = takeIt _ (x:xs)
    where takeIt status
        | status = x : takeBetween start end xs
        | otherwise = takeBetween start end xs

但我不能传递_状态布尔值。请帮我弄清楚我做错了什么。

注意

如果有多个起始停止对,则结果列表应由它们之间的任何内容组成。例如:

["x","start","y","stop","z","start","w","stop"]应该返回["start", "y", "stop", "start", "w", "stop"]

如果停止令牌位于开始令牌之前,则会被忽略。

["stop","x","start","y","stop"]返回["start", "y", "stop"]

理想情况下,只有第一个开始和第一个停止令牌才有价值:

["start","x","start","y","stop","stop"]返回["start", "x", "start", "y", "stop"]

["start","x","start","y","stop"]返回["start", "x", "start", "y", "stop"]

更新2

我也尝试过一种简单的方法,只使用一滴,但无济于事:

takeByToken :: (Eq a) => a -> a -> [a] -> [a]
takeByToken start end (x:xs) = dropWhile (\x -> not $ x == start) xs

请帮我解决这个问题。

更新3

dfeuer的帮助下,我提出了这个解决方案:

data Status = Between | NotBetween
takeBetweens :: [String] -> [String]
takeBetweens = takeBetweens' NotBetween

takeBetweens' :: Status -> String -> String -> [String] -> [String]
takeBetweens' _ _ _ [] = []
takeBetweens' NotBetween s e (x : xs)
    | s == x = x : takeBetweens' Between s e xs
    | otherwise = takeBetweens' NotBetween s e xs
takeBetweens' Between s e (x : xs)
    | e == x = x : takeBetweens' NotBetween s e xs
    | otherwise = x : takeBetweens' Between s e xs

但是,这个解决方案仍然要求我使用2个功能。我试图在takeBetweens'子句中包含帮助器where函数,但遇到了语法错误(我知道不知道)。

data Status = Between | NotBetween

takeBetweens :: [String] -> [String]
takeBetweens = takeBetweens' NotBetween "start" "end"
    where takeBetweens' _ _ _ [] = []
    takeBetweens' NotBetween s e (x : xs)
        | s == x = x : takeBetweens' Between s e xs
        | otherwise = takeBetweens' NotBetween s e xs
    takeBetweens' Between s e (x : xs)
        | e == x = x : takeBetweens' NotBetween s e xs
        | otherwise = x : takeBetweens' Between s e xs



test.hs:5:9: parse error on input ‘takeBetweens'’

如何在where子句中包含辅助函数?

2 个答案:

答案 0 :(得分:3)

你的错误在于考虑指数。别!列表不是数组,假装它们会导致代码复杂而缓慢。

我建议你使用辅助函数:

data Status = NotBetween | Between

takeBetweens' :: Status -> [String] -> [String]

我的想法是,takeBetweens' NotBetween s应该寻找一个"start"在途中删除所有内容,然后使用Between调用自己,而takeBetweens Between s应该抓住所有内容,直到它看到"stop" 1}}然后用NotBetween调用自己。

我建议你不要使用数字来编写函数。

更大的提示

takeBetweens :: [String] -> [String]
takeBetweens = takeBetweens' NotBetween

takeBetweens' :: Status -> [String] -> [String]
takeBetweens' _ [] = ?
takeBetweens' NotBetween ("start" : xs) = ?
takeBetweens' NotBetween (x : xs) = ?
takeBetweens' Between ("stop" : xs) = ?
takeBetweens' Between (x : xs) = ?

你能看到这基本上是一台状态机吗?你能看到何时以及如何改变状态?

答案 1 :(得分:2)

这可能会给你一个想法...

Prelude> let f x = reverse . dropWhile (/=x)                                                                          
Prelude> let between s e = f e . f s
Prelude> between 3 5 [1..10]
[3,4,5]

显然没有错误处理,如果没有指定结束会发生什么?