假设我有一个List
元素,其中包含不同的数字:[1, 2, 3, 4, 5]
,我想在列表中将值2
更改为9
。
我可以使用一个简单的函数来实现这一点:
map (\x -> if x == 2 then 9 else x) [1, 2, 3, 4, 5]
但是表现明智,我认为没有必要遍历整个列表,因为元素是不同的。如果不进行整个列表遍历,是否还有其他更高阶的函数来执行相同的操作?
我知道这可以使用显式递归来解决,但我只是想知道是否可以使用任何现有的高阶函数以有效的方式解决这个问题。
答案 0 :(得分:3)
如果您想避免显式递归,可以执行以下操作:
modifyFirst :: (a -> Bool) -> (a -> a) -> [a] -> [a]
modifyFirst p f xs = concat [a, map f (take 1 b), drop 1 b]
where (a, b) = break p xs
modifyFirst (==2) (const 9) [1, 2, 3, 4, 5] === [1, 9, 3, 4, 5]
答案 1 :(得分:0)
例如,我们可以做下一步:
modifyFirst _ _ [] = []
modifyFirst f y (x:xs) = if f x
then (y:xs)
else x : modifyFirst f y xs
但是我们可以用库函数来做到这一点:
import Data.List(span)
modifyFirst f y xs = nonF ++ newTail
where (nonF,withF) = span f xs
newTail = if null withF then [] else y : tail withF
答案 2 :(得分:0)
我知道这不完全是你所问的,但我觉得无论如何我都必须提一下......
如果您有一组不同的元素,最好将它们存储在Data.Set中。然后,您可以像这样修改集合中的一个元素
modify x x' theSet = if member x theSet then (insert x' $ delete x set) else theSet
这将在log n时间内发生(与列表中的线性时间相反)。
你甚至可以将一个列表转换为一个集合(使用fromList),然后使用这个函数,虽然这样做有一个n * log n惩罚,所以只有你必须重复运行转换例程才有意义( ie-如果集合像数据库一样使用,不断更新......这种转换就像索引数据库中的列一样,只有在计划经常查找该值时才会这样做。 / p>
如果您只进行一次更改,请不要费心转换为套装....就此而言,为什么在这种情况下您会担心性能? :)