说我有一个字符串列表:
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
子句中包含辅助函数?
答案 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]
显然没有错误处理,如果没有指定结束会发生什么?