Haskell:将列表中的所有索引更改为某个值
问题描述:
如果给出了列表中的对象列表和列表中的某些索引的另一个列表,是否有一种简单方法可以通过列表中的索引更改此列表中的每个对象的指数到不同的价值?Haskell:将列表中的所有索引更改为某个值
E.g.我希望存在一些函数f,使得
f 0 [4,2,5] [6,5,8,4,3,6,2,7]
将输出
[6,5,0,4,0,0,2,7]
答
这可以用一个列表理解来完成:
f :: (Eq i, Num i, Enum i) => a -> [i] -> [a] -> [a]
f repl indices values = [if i `elem` indices then repl else v | (i, v) <- zip [0..] values]
这不是最有效的方法不过,特别是如果指数清单很长的话。你可以尝试使用数组。
答
您可以定义一个辅助函数来替换单个值,然后使用它来折叠列表。
replaceAll :: a -> [Int] -> [a] -> [a]
replaceAll repVal indices values = foldl (replaceValue repVal) values indices
where replaceValue val vals index = (take index vals) ++ [val] ++ (drop (index + 1) vals)
答
下面是一个使用lens
一个美丽的版本:
import Control.Lens
f :: a -> [Int] -> [a] -> [a]
f x is = elements (`elem` is) .~ x
这里是没有比其他base
任何依赖一个有效的版本。基本上,我们从排序(并从索引列表中删除重复项)开始。这样,我们无需为每个替代品扫描整个列表。
import Data.List
f :: a -> [Int] -> [a] -> [a]
f x is xs = snd $ mapAccumR go is' (zip xs [1..])
where
is' = map head . group . sort $ is
go [] (y,_) = ([],y)
go (i:is) (y,j) = if i == j then (is,x) else (i:is,y)
答
排序指数的第一。然后你可以一前一后地遍历这两个列表。
{-# LANGUAGE ScopedTypeVariables #-}
import Prelude (Eq, Enum, Num, Ord, snd, (==), (<$>))
import Data.List (head, group, sort, zip)
f :: forall a. (Eq a, Enum a, Num a, Ord a) => a -> [a] -> [a] -> [a]
f replacement indices values =
go (head <$> group (sort indices)) (zip [0..] values)
where
go :: [a] -> [(a, a)] -> [a]
go [] vs = snd <$> vs
go _ [] = []
go (i:is) ((i', v):vs) | i == i' = replacement : go is vs
go is (v:vs) = snd v : go is vs
分选招致在索引列表的长度额外日志因子,但其余的是线性的。
我投票结束这个问题作为题外话,因为没有尝试或事先的努力。 –