List 哈斯凯尔:返回;列表“;函数的结果作为一个;名单;不使用空列表“;[]:foo“;
返回列表列表([[a]])但不使用空列表([]:[a])的语法是什么(如果可能的话)? (与下面的第二个注释保护(2)类似,这是不正确的) 这是一个正常工作的功能:List 哈斯凯尔:返回;列表“;函数的结果作为一个;名单;不使用空列表“;[]:foo“;,list,haskell,syntax,List,Haskell,Syntax,返回列表列表([[a]])但不使用空列表([]:[a])的语法是什么(如果可能的话)? (与下面的第二个注释保护(2)类似,这是不正确的) 这是一个正常工作的功能: -- Split string on every (shouldSplit == true) splitWith :: (Char -> Bool) -> [Char] -> [[Char]] splitWith shouldSplit list = filter (not.null) -- would like
-- Split string on every (shouldSplit == true)
splitWith :: (Char -> Bool) -> [Char] -> [[Char]]
splitWith shouldSplit list = filter (not.null) -- would like to get rid of filter
(imp' shouldSplit list)
where
imp' _ [] = [[]]
imp' shouldSplit (x:xs)
| shouldSplit x = []:imp' shouldSplit xs -- (1) this line is adding empty lists
-- | shouldSplit x = [imp' shouldSplit xs] -- (2) if this would be correct, no filter needed
| otherwise = let (z:zs) = imp' shouldSplit xs in (x:z):zs
这是正确的结果
Prelude> splitWith (== 'a') "miraaaakojajeja234"
["mir","koj","jej","234"]
然而,它必须使用“filter”来清理它的结果,所以我想去掉函数“filter”。
这是不使用过滤器的结果:
["mir","","","","koj","jej","234"]
如果使用“| shouldspilt x=imp”shouldspilt xs”代替第一个保护,则结果不正确:
["mirkojjej234"]
第一个保护(1)添加了空列表,因此(我假设)编译器可以将结果视为列表列表([[a]])
(我对函数的其他/不同的解决方案不感兴趣,只对语法澄清感兴趣。)
回答:
来自Dave4420的答案让我找到了答案,但这是一个评论,不是答案,所以我不能接受它作为答案。问题的解决办法是我问错了问题。这不是语法的问题,而是我的算法的问题
有几种解决空列表问题的解决方案,但它们不是我问题的答案。然而,他们扩展了我对如何使用基本Haskell语法完成事情的看法,我为此感谢他们
编辑:
splitWith :: (Char -> Bool) -> String -> [String]
splitWith p = go False
where
go _ [] = [[]]
go lastEmpty (x:xs)
| p x = if lastEmpty then go True xs else []:go True xs
| otherwise = let (z:zs) = go False xs in (x:z):zs
问题很自然地表现为要拆分的列表的折叠。您需要跟踪两种状态—结果列表和要附加到结果列表的当前单词 我可能会写一个天真的版本如下:
splitWith p xs = word:result
where
(result, word) = foldr func ([], []) xs
func x (result, word) = if p x
then (word:result,[])
else (result, x:word)
请注意,这也会保留在空列表中,因为它在检测到满足谓词p
的新元素时会将当前单词追加到结果中
要解决这个问题,只需将list cons操作符(:)
替换为新操作符即可
(~:) :: [a] -> [[a]] -> [[a]]
如果原始列表为非空,则仅将一个列表转换为另一个列表。算法的其余部分保持不变
splitWith p xs = word ~: result
where
(result, word) = foldr func ([], []) xs
func x (result, word) = if p x
then (word ~: result, [])
else (result, x:word)
x ~: xs = if null x then xs else x:xs
这就是你想要的。问题很自然地表现为你要拆分的列表的折叠。您需要跟踪两种状态—结果列表和要附加到结果列表的当前单词 我可能会写一个天真的版本如下:
splitWith p xs = word:result
where
(result, word) = foldr func ([], []) xs
func x (result, word) = if p x
then (word:result,[])
else (result, x:word)
请注意,这也会保留在空列表中,因为它在检测到满足谓词p
的新元素时会将当前单词追加到结果中
要解决这个问题,只需将list cons操作符(:)
替换为新操作符即可
(~:) :: [a] -> [[a]] -> [[a]]
如果原始列表为非空,则仅将一个列表转换为另一个列表。算法的其余部分保持不变
splitWith p xs = word ~: result
where
(result, word) = foldr func ([], []) xs
func x (result, word) = if p x
then (word ~: result, [])
else (result, x:word)
x ~: xs = if null x then xs else x:xs
这一个利用模式匹配来完成在一次遍历中不生成空交错列表的任务:
splitWith :: Eq a => (a -> Bool) -> [a] -> [[a]]
splitWith f list = case splitWith' f list of
[]:result -> result
result -> result
where
splitWith' _ [] = []
splitWith' f (a:[]) = if f a then [] else [[a]]
splitWith' f (a:b:tail) =
let next = splitWith' f (b : tail)
in if f a
then if a == b
then next
else [] : next
else case next of
[] -> [[a]]
nextHead:nextTail -> (a : nextHead) : nextTail
运行它:
main = do
print $ splitWith (== 'a') "miraaaakojajeja234"
print $ splitWith (== 'a') "mirrraaaakkkojjjajeja234"
print $ splitWith (== 'a') "aaabbbaaa"
产生:
["mir","koj","jej","234"]
["mirrr","kkkojjj","jej","234"]
["bbb"]
该方法利用模式匹配来完成在一次遍历中不生成空交错列表的任务:
splitWith :: Eq a => (a -> Bool) -> [a] -> [[a]]
splitWith f list = case splitWith' f list of
[]:result -> result
result -> result
where
splitWith' _ [] = []
splitWith' f (a:[]) = if f a then [] else [[a]]
splitWith' f (a:b:tail) =
let next = splitWith' f (b : tail)
in if f a
then if a == b
then next
else [] : next
else case next of
[] -> [[a]]
nextHead:nextTail -> (a : nextHead) : nextTail
运行它:
main = do
print $ splitWith (== 'a') "miraaaakojajeja234"
print $ splitWith (== 'a') "mirrraaaakkkojjjajeja234"
print $ splitWith (== 'a') "aaabbbaaa"
产生:
["mir","koj","jej","234"]
["mirrr","kkkojjj","jej","234"]
["bbb"]
我想我和克里斯有着相似的想法,即使不是那么优雅:
splitWith shouldSplit list = imp' list [] []
where
imp' [] accum result = result ++ if null accum then [] else [accum]
imp' (x:xs) accum result
| shouldSplit x =
imp' xs [] (result ++ if null accum
then []
else [accum])
| otherwise = imp' xs (accum ++ [x]) result
我想我和克里斯有着相似的想法,即使不是那么优雅:
splitWith shouldSplit list = imp' list [] []
where
imp' [] accum result = result ++ if null accum then [] else [accum]
imp' (x:xs) accum result
| shouldSplit x =
imp' xs [] (result ++ if null accum
then []
else [accum])
| otherwise = imp' xs (accum ++ [x]) result
这基本上只是
dropWhile
和break
的交替应用,不是吗:
splitWith p xs=g xs
哪里
gxs=let(a,b)=中断p(dropwhilepxs)
如果为空a,则[]否则a:g b
你说除了你的解决方案之外,你对其他解决方案不感兴趣,但其他读者可能会感兴趣。它确实很短,而且看起来很清楚。随着您的学习,使用基本的Prelude
函数成为您的第二天性。:)
至于您的代码,以非必要的方式进行了一点修改(使用简短的提示性函数名,如对“predicate”使用p
,对主辅助函数使用g
),它是
splitWith::(Char->Bool)->[Char]->[[Char]]
splitWith p list=过滤器(非.null)(g list)
哪里
g[]=[[]]
g(x:xs)
|px=[]:gxs
|否则=let(z:zs)=gxs
in(x:z):zs
此外,不需要将谓词作为参数传递给worker(注释中也提到了这一点)。现在可以说它更具可读性
接下来,只要稍作改变,它就变成了
splitWith::(Char->Bool)->[Char]->[[Char]]
splitWith p list=案例g列表的([]:r)->r;x->x
哪里
g[]=[[]]
g(x:xs)
|px=情况z为[]->r;——如果还没有开始一个新单词
_->[]:r
|否则=(x:z):zs
其中——现在z,z是可访问的
r@(z:zs)=gxs——在这两种情况下
这是你想要的。顶级的大小写
在这里最多删除一个空单词,它在内部函数工作期间的某个点用作分隔符标记。您的过滤器(not.null)
基本上融合到这里的worker函数g
,有条件地打开一个新词(即空列表的添加)
将let
替换为where
允许变量(z
等)在g
定义的第二个子句的两个分支中都可以访问
最后,您的算法已经足够接近了,代码毕竟是可以修复的
1当思考“从右向左”时。实际上,列表是以保护递归的方式从左到右构造的。这基本上只是
dropWhile
和break
的交替应用,不是吗:
splitWith p xs=g xs
哪里
gxs=let(a,b)=中断p(dropwhilepxs)
如果为空a,则[]否则a:g b