Reputation: 360
I have a very large Vector a and I want to map a function over a small range. For example, if I have a Vector of size 1000, I want to map function f over values at indices 100-200 and then return the entire updated Vector. What is the best way to do this. I'm open to other data structures, but I would prefer not to use mutable vectors.
Edit: Here's an equivalent in imperative code.
for (int i = startIdx; i < endIdx ; i++){
bigVector[i] = myFunction(bigVector[i]);
}
Upvotes: 2
Views: 681
Reputation: 43310
The immutable vector implementation of the "vector" package is basically an array. This means that any modifying operation on it requires the traversal of the whole array. That limitation doesn't play well with the requirements that you've mentioned.
Since you're saying that you're open to a choice of a different data-structure, the first thing you need to consider is a data-structure driven by the Array-Mapped Trie algorithm. That algorithm allows you to project on the slices of the data-structure, concatenate, drop/take, append/prepend - all in either a constant or logarithmic time. It's efficiency is proven in production with it driving the ubiquitous immutable vector data-structures of such languages as Clojure and Scala.
Fortunately, Haskell has an implementation as well. It's presented by the "persistent-vector" package, and here's how you can efficiently solve your problem using it:
mapOverSlice :: Int -> Int -> (a -> a) -> Vector a -> Vector a
mapOverSlice startIndex length mapper input =
let
(split1Heading, split1Trail) =
splitAt startIndex input
(split2Heading, split2Trail) =
splitAt (pred length) split1Trail
in
split1Heading <>
map mapper split2Heading <>
split2Trail
Upvotes: 3
Reputation: 30775
One way to to this is
The implementation is pretty straightforward:
maprange :: (a -> a) -> [a] -> Int -> Int -> [a]
maprange f vector skip nb = map (\(idx,v) -> if idx > skip && idx <= skip+nb then f v else v) zipped_vector
where zipped_vector = zip [1..] vector
The complexity should be O(n) where n is the size of the original vector (I'm not 100% sure about the complexity of zip, though); if you need something more efficient, you should investigate the data structures mentioned in the answer by Nikita Volkov.
Upvotes: 0
Reputation: 3574
How about something like
maprange :: (a -> a) -> [a] -> Int -> Int -> [a]
maprange f vector skip nb = (take skip vector) ++ (map f (take nb (drop skip vector))) ++ (drop (skip + nb) vector)
and then use it like
*Main> maprange (\x -> 2*x) [1 .. 10] 3 4
[1,2,3,8,10,12,14,8,9,10]
to double the 4 elements after the first 3 elements.
Upvotes: 0