I have two collections:
p = [ a, b ]
v = [ 1, 2, 3, 4, 5 ]
I want to process the following tuples:
[ [a,1], [b,2], [b,3], [b,4], [b,5] ]
My current code is:
p.zip(v, (x,y) => { ... })
But of course I only end up processing:
[ [a,1], [b,2] ]
It feels like I want to create an infinite list of last(p), overlayed by p, which we then zip with v. Something like:
extend(p).zip( v, ... )
Where extend(IEnumerable list) returns
[ a, b, b, b, ..., b ] // Infinite/generated sequence
I think I can write extend() as an enumerator on list that when exhausted, keeps returning the last element, ad infinitum.
What I'm interested in knowing is if there's a functional way of creating extend, by composing existing functions. I'd be happy to see it expressed in Haskell/F#, even if those functions don't exist in LINQ. I'm encouraging myself to think functionally.
I have looked through https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-List.html for likely atoms but don't see anything that might create what I need.
Thank you!
Edit:
I've reduced the problem further:
extend(list) = list.Concat( repeat(list.Last()) )
where repeat(T item) returns [ item, item, ..., item ]
I am now searching LINQ and Haskell for existing implementations of something like "repeat".
A solution in Haskell:
> let extend = foldr (\x xs -> x : if null xs then repeat x else xs) []
A few tests:
> extend []
[]
> take 10 $ extend [1]
[1,1,1,1,1,1,1,1,1,1]
> take 10 $ extend [1,2,3]
[1,2,3,3,3,3,3,3,3,3]
The above does not use last
, so that we do not hold a reference to the whole input list to extend
-- in this way it can be garbage collected.
Of course, explicit recursion would also work:
extend :: [a] -> [a]
extend [] = []
extend [x] = repeat x
extend (x:xs) = x : extend xs
Staying in the .Net/C# world:
You could use something like:
var almostInfinite = items.Concat(Enumerable.Repeat(items.Last(), Int32.MaxValue));
but it will not yield a really infinite sequence.
Writting your own Extend
method isn't hard, either:
IEnumerable<T> Extend<T>(IEnumerable<T> source)
{
// error checking omitted
var e = source.GetEnumerator();
T last = default(T);
while(e.MoveNext())
yield return last = e.Current;
while(true)
yield return last;
}
You could also create another version of Zip
. Take a look at morelinq's Zip
, which handles source sequences of different sizes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With