使用列表推导式代替递归

4

我很好奇是否可以在以下示例中使用列表推导式代替递归。

replaceFirst 函数接受一个元素和一个列表,并替换列表中第一次出现的该元素。

可以使用递归来实现:

replaceFirst _ [] = []
replaceFirst elem y (x:xs) | x==y = (elem:xs)
                           | otherwise = x:replaceFirst elem y xs

我的问题是,这个递归函数或类似于操作列表中第一次出现的元素的递归函数是否可以用列表推导式函数替换?为什么可以或不可以?(我更关心推理过程而不是实际代码)。


这不是 https://dev59.com/7G7Xa4cB1Zd3GeqPlQur 的重复吗?删除和替换之间的区别并不大,也不影响是否可以使用列表递归来完成。 - Joachim Breitner
第一个方程式缺少一种模式。 - Ingo
将列表推导式视为“foreach”循环可能会有所帮助,您可以在其中对每个元素应用过滤器和计算。请参阅此Wiki中列表单子的“do”语法http://www.haskell.org/haskellwiki/List_comprehension - wizzup
2个回答

8
列表推导式是各种形式的map、filter和concatMap的语法糖。如果您的递归函数可以用这些来描述,那么您可以将其重写为一个列表推导式。它们无法像您上面所做的那样进行短路计算,也无法传递累积状态。
您的replaceFirst似乎需要一个累加器来“告诉”列表中后面的元素有关先前元素的出现。我认为这使得只使用列表推导式语法编写它变得困难或不可能。

+1,感谢您的回复。我一直在努力确定是否可以仅使用列表推导来完成此操作,但是您的答案刚好澄清并确认了我的第一直觉。谢谢! - AnchovyLegend
@MiGusta:当然,这并不意味着该函数不能使用其他标准高阶函数进行重写。replaceFirst elem y xs | (l,_:r)<-break(==y)xs = l++[elem]++r | otherwise = xs 可以工作。 - leftaroundabout

0

列表推导式,灵感来自leftaroundabout:

replaceFirst elem y xs = [a | let b = break (==y) xs
                                  c = if not (null $ snd b) 
                                         then fst b ++ [elem] ++ tail (snd b) 
                                         else fst b
                              , a <- c]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接