Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extend a list using last element to match longer list in LINQ zip

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".

like image 554
Dave Dawkins Avatar asked Dec 24 '22 23:12

Dave Dawkins


2 Answers

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
like image 56
chi Avatar answered Dec 27 '22 17:12

chi


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.

like image 29
sloth Avatar answered Dec 27 '22 19:12

sloth