SlideShare a Scribd company logo
Hand-Toss a Stream
(Lazy List)
in C#
Dhaval Dalal
https://dhavaldalal.wordpress.com
@softwareartisan
Implementing Lazy List
IEnumerable<int> Naturals(int @from) {

for (var i = @from; true ; i++) {

yield return i;

}

}

Naturals(0)
.Take(3)
.ForEach(Console.WriteLine); // 0 1 2
Using generators
This is the idiomatic approach.
There is yet another one as well.
Implementing Lazy List
Using Lambda
Wrap the tail of the
list in a closure.
1 ?
Implementing Lazy List
Using Lambda
Wrap the tail of the
list in a closure.
When we need the result
evaluate the tail by
invoking the closure.
Evaluate
tail
1 ?
1 *
2 ?
Implementing Lazy List
Using Lambda
Wrap the tail of the
list in a closure.
When we need the result
evaluate the tail by
invoking the closure.
Additionally for
performance - Cache or
Memoize the closure
output.
Evaluate
tail
1 ?
1 *
2 ?
Implementing Lazy List
Using Lambda
Wrap the tail of the
list in a closure.
When we need the result
evaluate the tail by
invoking the closure.
Additionally for
performance - Cache or
Memoize the closure
output.
Evaluate
tail
1 ?
1 *
2 ?
Implementing Lazy List
Stream implementation shown in these slides is available on:
https://github.com/CodeJugalbandi/FunctionalProgramming/blob/master/melodies/lazy_sequences/Stream.cs
Immutable Stream with
eager Head & lazy Tail
sealed class Stream<T> {

private readonly T head;

private readonly Func<Stream<T>> tail;



public Stream(T head, Func<Stream<T>> tail) {

this.head = head;

this.tail = tail;

}

public T Head {

get => head;

}

public Stream<T> Tail {

get => tail(); // Need Memoization (for Perf)

}

public override string ToString() =>
$"Stream<{typeof(T)}>({head}, ?)";

}
Immutable Stream with
eager Head & lazy Tail
sealed class Stream<T> {

private readonly T head;

private readonly Lazy<Stream<T>> tail;



public Stream(T head, Lazy<Stream<T>> tail) {

this.head = head;

this.tail = tail;

}

public T Head {

get => head;

}

public Stream<T> Tail {

get => tail.Value; // Cached after first eval

}

public override string ToString() =>
$"Stream<{typeof(T)}>({head}, ?)";

}
Immutable Stream with
eager Head & lazy Tail
var empty = new Stream<int>(0, null);

Console.WriteLine(empty); // Stream<System.Int32>(0, ?)

Console.WriteLine(empty.Head); // 0

Console.WriteLine(empty.Tail);



var singleton = new Stream<int>(1, new Lazy<Stream<int>>(() => empty));

Console.WriteLine(singleton); // Stream<System.Int32>(1, ?)

Console.WriteLine(singleton.Head); // 1

Console.WriteLine(singleton.Tail); // Stream<System.Int32>(0, ?)



var couple = new Stream<int>(2, new Lazy<Stream<int>>(() => singleton));

Console.WriteLine(couple); // Stream<System.Int32>(2, ?)

Console.WriteLine(couple.Head); // 2

Console.WriteLine(couple.Tail); // Stream<System.Int32>(1, ?)

Console.WriteLine(couple.Tail.Tail); // Stream<System.Int32>(0, ?)

Console.WriteLine(couple.Tail.Tail.Tail);
As Streams are immutable
we can structurally share
the earlier stream.
Boom!
Boom!
Introduce Empty Stream
sealed class Stream<T> {

public static readonly Stream<T> Empty
= new Stream<T>(default(T), null);

private readonly T head;

private readonly Lazy<Stream<T>> tail;

public Stream(T head, Lazy<Stream<T>> tail) { … }

public T Head { … }

public Stream<T> Tail { … }

public bool IsEmpty {

get => tail == null;

}

public override string ToString() {

if (IsEmpty) 

return "Empty";



return $"Stream<{typeof(T)}>({head}, ?)";

}

}
Introduce Empty Stream
var empty = Stream<int>.Empty;

Console.WriteLine(empty); // Empty

Console.WriteLine(empty.IsEmpty); // True



var singleton =
new Stream<int>(1, new Lazy<Stream<int>>(() => empty));

Console.WriteLine(singleton); // Stream(1, ?)

Console.WriteLine(singleton.IsEmpty); // False



var couple =
new Stream<int>(2, new Lazy<Stream<int>>(() => singleton));

Console.WriteLine(couple); // Stream(2, ?)

Console.WriteLine(couple.IsEmpty); // False
Doing Side-effects
sealed class Stream<T> {
…
…

public void ForEach(Action<T> action) {

if (IsEmpty)

return;


action(Head);

Tail.ForEach(action);

}

}
var empty = Stream<int>.Empty;

empty.ForEach(Console.WriteLine); // Prints Nothing

var stream = new Stream<int>(2, new Lazy<Stream<int>>(new
Stream<int>(1, new Lazy<Stream<int>>(() => empty))));

stream.ForEach(Console.WriteLine); // 2 1
Consing to Stream
sealed class Stream<T> {

…
…

// Cons using +

public static Stream<T> operator + (Stream<T> s, T element) => 

new Stream<T>(element, new Lazy<Stream<T>>(() => s));

}
var stream = Stream<int>.Empty + 1 + 2;

Console.WriteLine(stream); // Stream(2, ?)

Console.WriteLine(stream.Head); // 2

Console.WriteLine(stream.Tail); // Stream(1, ?)


stream.ForEach(Console.WriteLine); // 2 1
Prepend (Cons)
Append to Stream
sealed class Stream<T> {
…

// Append using +

public static Stream<T> operator + (T element, Stream<T> s) => 

new Stream<T>(element,
new Lazy<Stream<T>>(() => s.IsEmpty ? Stream<T>.Empty : s));

}
var stream = 1 + Stream<int>.Empty;

stream.ForEach(Console.WriteLine); // 1



var stream = 1 + (2 + (3 + (4 + Stream<int>.Empty)));

stream.ForEach(Console.WriteLine); // 1 2 3 4
sealed class Stream<T> {
…
public static Stream<R> Of<R>(params R[] rs) {

var stream = Stream<R>.Empty;

var indices = rs.Length - 1;

for (var i = indices; i >= 0; i--) {

stream = stream + rs[i];

}

return stream;

}

}
Stream<int>.Of<int>()
.ForEach(Console.WriteLine); // Prints Nothing
Stream<int>.Of(1, 2, 3, 4)
.ForEach(Console.WriteLine); // 1 2 3 4
Construct a Stream
from few elements
Concat another Stream
sealed class Stream<T> {

…

public static Stream<T> operator + (Stream<T> @this,
Stream<T> other) {
if (@this.IsEmpty)

return other;



return new Stream<T>(@this.Head,
new Lazy<Stream<T>>(() => @this.Tail + other));

}
}
var concat1 = Stream<char>.Empty + Stream<char>.Of('a', 'b');

concat1.ForEach(Console.Write); // ab



var concat2 = Stream<char>.Of('a', 'b') + Stream<char>.Empty;

concat2.ForEach(Console.Write); // ab



var concat3 = Stream<char>.Of('a', 'b') + Stream<char>.Of('c', 'd', 'e');

concat3.ForEach(Console.Write); // abcde
sealed class Stream<T> {

…
public Stream<T> Take(int howMany) {

if (IsEmpty || howMany <= 0)

return Stream<T>.Empty;



return new Stream<T>(Head,
new Lazy<Stream<T>>(() => Tail.Take(howMany - 1)));

}

}
Stream<int>.Empty
.Take(2).ForEach(Console.WriteLine); // Prints Nothing

Stream<int>.Of(1, 2, 3, 4)
.Take(2).ForEach(Console.WriteLine); // 1 2

Stream<int>.Of(1, 2, 3, 4)
.Take(12).ForEach(Console.WriteLine); // 1 2 3 4

Stream<int>.Of(1, 2, 3, 4)
.Take(0).ForEach(Console.WriteLine); // Prints Nothing
Take few elements
sealed class Stream<T> {

…
public Stream<T> Drop(int howMany) {

if (IsEmpty || howMany <= 0)

return this;



return Tail.Drop(howMany - 1);

}

}
Stream<int>.Empty
.Drop(2).ForEach(Console.WriteLine); // Prints Nothing

Stream<int>.Of(1, 2, 3, 4)
.Drop(2).ForEach(Console.WriteLine); // 3 4

Stream<int>.Of(1, 2, 3, 4)
.Drop(20).ForEach(Console.WriteLine); // Prints Nothing

Stream<int>.Of(1, 2, 3, 4)
.Drop(0).ForEach(Console.WriteLine); // 1 2 3 4
Drop few elements
sealed class Stream<T> {

…
…
public static Stream<R> Generate<R>(Func<R> fn) => 

new Stream<R>(fn(), new Lazy<Stream<R>>(() => Generate(fn)));
}
var random = new Random();

Stream<int>.Generate(() => random.Next(100, 150))
.Take(4)
.ForEach(Console.WriteLine);
// Prints 4 random numbers bet [100, 150)
Construct a Stream
using lambda - 1
sealed class Stream<T> {

…
…
public static Stream<R> Iterate<R>(R initial, Func<R, R> fn) => 

new Stream<R>(initial,
new Lazy<Stream<R>>(() => Iterate(fn(initial), fn)));

}
Stream<int>.Iterate(9, x => x + 2)
.Take(4)
.ForEach(Console.WriteLine); // 9 11 13 15
Construct a Stream
using lambda - 2
sealed class Stream<T> {

…
…
public void Deconstruct(out T first, out Stream<T> rest) {

if (IsEmpty) 

throw new ArgumentException("Collection is Empty!");



first = Head;

rest = Drop(1);

}

}
var (head, rest) = Stream<int>.Of(1, 2, 3, 4);

Console.WriteLine("Head = " + head); // 1

Console.WriteLine("Rest = " + rest); // Stream(2, ?)
Deconstruct a Stream
sealed class Stream<T> {

…
public void Deconstruct(out T first, out Stream<T> rest) {

if (IsEmpty) 

throw new ArgumentException("Collection is Empty!");



first = Head;

rest = Drop(1);

}

public void Deconstruct(out T first, out T second,
out Stream<T> rest) =>
(first, (second, rest)) = this; 

}
var (head, second, rest) = Stream<int>.Of(1, 2, 3, 4);
Console.WriteLine("Head = " + head); // 1
Console.WriteLine("Second = " + second); // 2
Console.WriteLine("Rest = " + rest); // Stream(2, ?)
Deconstruct a Stream
sealed class Stream<T> {

…
public void Deconstruct(out T first, out T second,
out Stream<T> rest) =>

(first, (second, rest)) = this;



public void Deconstruct(out T first, out T second, out T third,
out Stream<T> rest) =>

(first, second, (third, rest)) = this;

}
var (head, second, third, rest) = Stream<int>.Of(1, 2, 3, 4);

Console.WriteLine("Head = " + head); // 1

Console.WriteLine("Second = " + second); // 2

Console.WriteLine("Third = " + third); // 3

Console.WriteLine("Rest = " + rest); // Stream(4, ?)
Deconstruct a Stream
Transform each element
sealed class Stream<T> {

…
…

public Stream<R> Select<R>(Func<T, R> fn) {

if (IsEmpty)

return Stream<R>.Empty;



return new Stream<R>(fn(Head),
new Lazy<Stream<R>>(() => Tail.Select(fn)));

}

}
var empty = Stream<int>.Empty;
Console.WriteLine(empty.Select(x => x * x)); // Prints Nothing
var stream = Stream<int>.Of(1, 2);

Console.WriteLine(stream.Select(x => x * x)); // 1 4
Filtering the Stream
sealed class Stream<T> {

…

public Stream<T> Where(Predicate<T> pred) {

if (IsEmpty)

return Stream<T>.Empty;



if (pred(Head)) 

return new Stream<T>(Head,
new Lazy<Stream<T>>(() => Tail.Where(pred)));



return Tail.Where(pred);

}

}
var empty = Stream<int>.Empty;
Console.WriteLine(empty.Where(x => x < 2)); // Prints Nothing
var stream = Stream<int>.Of(1, 2);

Console.WriteLine(stream.Where(x => x < 2)); // 1
Flatmap the Stream
sealed class Stream<T> {
…

public Stream<T> SelectMany(Func<T, Stream<R>> fn) {

if (IsEmpty)

return Stream<R>.Empty;



return fn(Head) + Tail.SelectMany(fn);

}

}
Stream<char>.Of('a', 'b')

.SelectMany(c => Stream<int>.Of(1, 2).Select(n => (c, n)))

.ForEach(t => Console.Write(t)); // (a, 1)(a, 2)(b, 1)(b, 2)



Stream<int>.Empty.SelectMany(c => Stream<int>.Of(1, 2).Select(n => (c, n)))

.ForEach(t => Console.Write(t)); // Prints Nothing
Reverse
sealed class Stream<T> {

…
…

public Stream<T> Reverse() {

Stream<T> Reverse0(Stream<T> acc, Stream<T> source) {

if (source.IsEmpty)

return acc;



return Reverse0(acc + source.Head, source.Tail);

}

return Reverse0(Stream<T>.Empty, this);

}

}
Stream<char>.Of('a', 'b', ‘c')
.Reverse().ForEach(Console.Write); // cba


Stream<int>.Empty
.Reverse().ForEach(Console.WriteLine); // Prints Nothing
Take while
predicate holds
sealed class Stream<T> {

…

public Stream<T> TakeWhile(Predicate<T> pred) {

if (IsEmpty)

return Stream<T>.Empty;



if (pred(Head))

return Head + Tail.TakeWhile(pred);



return Stream<T>.Empty;

}

}
Stream<char>.Of('a', 'a', 'b', 'c').TakeWhile(c => c == 'a')

.ForEach(Console.Write); // aa



Stream<char>.Of('a', 'a', 'b', 'c').TakeWhile(c => c == 'b')

.ForEach(Console.Write); // Prints Nothing
sealed class Stream<T> {

…

public Stream<T> DropWhile(Predicate<T> pred) {

if (IsEmpty)

return Stream<T>.Empty;



if (pred(Head))

return Tail.DropWhile(pred);



return this;

}

}
Stream<char>.Of('a', 'a', 'b', 'c').DropWhile(c => c == 'a')

.ForEach(Console.Write); // bc



Stream<char>.Of('a', 'a', 'b', 'c').DropWhile(c => c == 'b')

.ForEach(Console.Write); // aabc
Drop while
predicate holds
sealed class Stream<T> {

…

public U Aggregate<U>(U identity, Func<U, T, U> func) {

if (IsEmpty)

return identity;



return Tail.Aggregate(func(identity, Head), func);

}

}
var sum1 = Stream<int>.Of(1, 2, 3, 4)
.Aggregate(0, (acc, elem) => acc + elem);

Console.WriteLine($"sum = {sum1}"); // 10

var sum2 = Stream<int>.Of<int>()
.Aggregate(0, (acc, elem) => acc + elem); 

Console.WriteLine($"sum = {sum2}"); // 0
Aggregate or Reduce
sealed class Stream<T> {

…

public bool All(Predicate<T> pred) {

bool All0(bool accumulator, Stream<T> stream) {

if (stream.IsEmpty || accumulator == false)

return accumulator;



return All0(accumulator && pred(stream.Head), stream.Tail);

}

return All0(true, this);

}

}
Console.WriteLine(Stream<int>.Of<int>().All(x => x % 2 == 0));
// True

Console.WriteLine(Stream<int>.Of<int>(2, 4).All(x => x % 2 == 0));
// True

Console.WriteLine(Stream<int>.Of<int>(1, 2, 4).All(x => x % 2 == 0));
// False
All Don’t evaluate the entire
Stream, short-circuit if we
already determined
negation.
sealed class Stream<T> {

…

public bool Any(Predicate<T> pred) {

bool Any0(bool accumulator, Stream<T> stream) {

if (stream.IsEmpty || accumulator == true)

return accumulator;



return Any0(accumulator || pred(stream.Head), stream.Tail);

}

return Any0(false, this);

}

}
Console.WriteLine(Stream<int>.Of<int>().Any(x => x % 2 == 0));
// False

Console.WriteLine(Stream<int>.Of<int>(2, 4).Any(x => x % 2 == 0));
// True

Console.WriteLine(Stream<int>.Of<int>(1, 2, 4).Any(x => x % 2 == 0));
// True

Console.WriteLine(Stream<int>.Of<int>(1, 3).Any(x => x % 2 == 0));
// False
Any Don’t evaluate the entire
Stream, short-circuit if we
already determined
affirmation.
sealed class Stream<T> {

…

public Stream<T> Scan(U identity, Func<U, T, U> func) {

if (IsEmpty)

return Stream<T>.Empty;



U newHead = func(identity, Head);

return newHead + Tail.Scan(newHead, func);

}

}
// Prints running sum

Stream<int>.Of(1, 2, 3, 4)
.Scan(0, (acc, elem) => acc + elem)
.ForEach(Console.WriteLine); // 1 3 6 10

Stream<int>.Of<int>()
.Scan(0, (acc, elem) => acc + elem)
.ForEach(Console.WriteLine); // Prints Nothing
Scan
Zip two Streams
sealed class Stream<T> {

…

public Stream<(T,U)> Zip<U>(Stream<U> that) {

if (this.IsEmpty || that.IsEmpty)

return Stream<(T,U)>.Empty;



return (this.Head, that.Head) + this.Tail.Zip(that.Tail);

}

}
Stream<char>.Of('a', 'b').Zip(Stream<int>.Of(1, 2))

.ForEach(t => Console.Write(t)); // (a, 1)(b, 2)



Stream<char>.Of('a', 'b').Zip(Stream<int>.Empty)

.ForEach(t => Console.Write(t)); // Prints Nothing



Stream<int>.Empty.Zip(Stream<char>.Of('a', 'b'))

.ForEach(t => Console.Write(t)); // Prints Nothing
Zip with function
sealed class Stream<T> {

…

public Stream<R> ZipWith<U, R>(Stream<U> that, Func<T, U, R> fn) {

if (this.IsEmpty || that.IsEmpty)

return Stream<R>.Empty;



return fn(this.Head, that.Head) +
this.Tail.ZipWith(that.Tail, fn);

}

}
var numbers = Stream<int>.Of(1, 2, 3);

numbers.ZipWith(numbers, (n1, n2) => n1 * n2)

.ForEach(Console.WriteLine); // 1 4 9



numbers.ZipWith(Stream<int>.Empty, (n1, n2) => n1 * n2)

.ForEach(Console.WriteLine); // Prints Nothing



Stream<int>.Empty.ZipWith(numbers, (n1, n2) => n1 * n2)

.ForEach(Console.WriteLine); // Prints Nothing
Splitsealed class Stream<T> {

…

public (Stream<T>, Stream<T>) Split(Predicate<T> pred) {

(Stream<T>, Stream<T>) Split0(Stream<T> yesAcc,
Stream<T> noAcc,
Stream<T> source) {

if (source.IsEmpty) 

return (yesAcc.Reverse(), noAcc.Reverse());



var elem = source.Head;

if (pred(elem))

return Split0(yesAcc + elem, noAcc, source.Tail);

else

return Split0(yesAcc, noAcc + elem, source.Tail);

} 

return Split0(Stream<T>.Empty, Stream<T>.Empty, this);

}

}
var (evens, odds) = Stream<int>.Iterate(0, x => x + 1).Take(10)
.Split(x => x % 2 == 0);

evens.ForEach(Console.Write); // 02468

odds.ForEach(Console.Write); // 13579
To List
sealed class Stream<T> {

…
public List<T> ToList() {

var result = new List<T>();

ForEach(result.Add);

return result;

}

}
var list = Stream<int>.Iterate(1, x => x + 1)
.Take(4)
.ToList();

foreach (var item in list) {

Console.WriteLine(item);

}
Find first 6 primes using the Sieve of
Eratosthenes
Hint: Use Stream created earlier
http://world.mathigon.org/Prime_Numbers
Sieve
Stream<int> From(int start) =>
Stream<int>.Iterate(start, x => x + 1);

Stream<int> Sieve(Stream<int> s) {

var first = s.Head;

var rest = s.Tail.Where(n => n % first != 0);

return new Stream<int>(first,
new Lazy<Stream<int>>(() => rest));

}



var primes = Sieve(From(2)).Take(6)
primes.ToList() // [2, 3, 5, 7, 11, 13]
First 10 Fibonacci Nos.
Write a function fibonacci which consumes an
integer and produces that many numbers in the
fibonacci series.
For Example: fibonacci(10) produces
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Provide Solutions
Using Generator
Using IEnumerable
Using Stream that we developed
IEnumerable<int> Fibonacci(int howMany) {
var (first, second) = (0, 1);

for (var i = 0; i < howMany; i++) {

yield return first;

(first, second) = (second, first + second);

}

}



Fibonacci(10).ToList();

// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
First 10 Fibonacci Nos.
Using Generator
IEnumerable<int> Fibonacci(int howMany) {
return IEnumerableExtensions.Iterate<(int, int)>((0, 1), tuple => {

var (first, second) = tuple;

var next = first + second;

return (second, next);

})

.Select(tuple => {

var (first, second) = tuple;

return first;

})

.Take(howMany);

}
Fibonacci(10).ToList();
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
First 10 Fibonacci Nos.
Using IEnumerable
Definition as suggested by Christopher Grande
First 10 Fibonacci Nos.
Using Stream
https://www.haskell.org/tutorial/functions.html
var seed = From(0).Take(2);

// Start with seed elements 0 and 1

Fibonacci(seed)

.Take(10)

.ForEach(Console.WriteLine);
// 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
Stream<int> Fibonacci(Stream<int> s) {

var next = s.Zip(s.Tail).Select(tuple => {

var (first, second) = tuple;

return first + second;

});

return new Stream<int>(s.Head,
new Lazy<Stream<int>>(() => Fibonacci(s + next.Head)));

}
Prime Counts
Using Lazy Lists, write a
program that counts number of
primes up to a given number.
At every power of 10, it should
emit the count of primes
obtained thus far. The table
shows the output until 10
10
Implement this using:
Using Stream we developed earlier.
Using IEnumerable
Hint: Write an extension method Scan
on IEnumerable
i/p count
10 4
10
2 25
10
3 168
10
4 1,229
10
5 9,592
10
6 78,498
10
7 6,64,579
10
8 57,61,455
10
9 5,08,47,534
10
10 45,50,52,511
class Streams {

public static Stream<int> Range(int start, int count) {

if (count < 0)

throw new ArgumentOutOfRangeException($"{count}");



return Stream<int>.Iterate(start, x => x + 1).Take(count);

} 

}

Streams.Range(1, 5).ForEach(Console.WriteLine);
// 1 2 3 4 5
Prime Counts using IEnumerable
First, write our own Range
Prime Counts Using Stream
Stream<(int, int)> PrimeCount(int howManyPowerOf10) {

bool IsPrime(int x) => Streams.Range(2, x)
.Where(n => n < x)
.All(n => x % n != 0);


return Stream<int>.Iterate(2, x => x + 1)

.TakeWhile(x => x <= Math.Pow(10, howManyPowerOf10))

.Select(x => (x, IsPrime(x)))

.Scan((0, 0), (acc, tuple) => {

var (x, isPrime) = tuple;

var (_, count) = acc;

return isPrime ? (x, count + 1): (x, count);

})

.Where(tuple => {

var (x, _) = tuple;

return Streams.Range(1, howManyPowerOf10)

.Any(n => Math.Pow(10, n) == x);

});

}
PrimeCount(3).ForEach(t => Console.WriteLine(t));
(10, 4), (100, 25), (1000, 168)
Prime Counts using IEnumerable
First, write our own Scan
static class IEnumerableExtensions {
public static IEnumerable<U> Scan<T, U>(this IEnumerable<T> @this,
U initial, Func<U, T, U> fn) {

IEnumerable<U> ScannedEnumerable() {

var acc = seed;

foreach (var item in @this) {

acc = fn(acc, item);

yield return acc;

} 

if (@this == null)

throw new ArgumentNullException("Require non-null list!");

if (fn == null)

throw new ArgumentNullException("Require non-null function!”);

return ScannedEnumerable();

}
}
Prime Counts using IEnumerable
IEnumerable<(int, int)> PrimeCount(int howManyPowerOf10) {

bool IsPrime(int x) => Enumerable.Range(2, x)
.Where(n => n < x)
.All(n => x % n != 0);


return IEnumerableExtensions.Iterate(2, x => x + 1)

.TakeWhile(x => x <= Math.Pow(10, howManyPowerOf10))

.Select(x => (x, IsPrime(x)))

.Scan((0, 0), (acc, tuple) => {

var (x, isPrime) = tuple;

var (_, count) = acc;

return isPrime ? (x, count + 1): (x, count);

})

.Where(tuple => {

var (x, _) = tuple;

return Enumerable.Range(1, howManyPowerOf10)

.Any(n => Math.Pow(10, n) == x);

});

}
PrimeCount(3).ForEach(t => Console.WriteLine(t));
(10, 4), (100, 25), (1000, 168)
Drawbacks of this
Stream Implementation
As C# compiler does not support Tail-
Recursion, all the APIs that are
implemented recursively will cause a
stack blow-up at some point for large
values of stream.
Even-though it uses caching of tail
using Lazy<T>, this Stream is not
performant like IEnumerable! This is
because the recursive APIs don’t run in
constant stack space.
But still its a good
mental exercise to create
streams from first
principles!
Thank-you!

More Related Content

What's hot (20)

Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Mario Fusco
 
JavaScript Functions
JavaScript Functions
Colin DeCarlo
 
Lambda and Stream Master class - part 1
Lambda and Stream Master class - part 1
José Paumard
 
If You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are Wrong
Mario Fusco
 
Jumping-with-java8
Jumping-with-java8
Dhaval Dalal
 
Java 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forward
Mario Fusco
 
LinkedIn TBC JavaScript 100: Functions
LinkedIn TBC JavaScript 100: Functions
Adam Crabtree
 
Swiss army knife Spring
Swiss army knife Spring
Mario Fusco
 
Java 8: the good parts!
Java 8: the good parts!
Andrzej Grzesik
 
Refactoring to Java 8 (Devoxx BE)
Refactoring to Java 8 (Devoxx BE)
Trisha Gee
 
Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8
RichardWarburton
 
Leveraging Completable Futures to handle your query results Asynchrhonously
Leveraging Completable Futures to handle your query results Asynchrhonously
David Gómez García
 
JavaScript Functions
JavaScript Functions
Brian Moschel
 
Sneaking inside Kotlin features
Sneaking inside Kotlin features
Chandra Sekhar Nayak
 
Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...
Mario Fusco
 
Java9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidad
David Gómez García
 
Booting into functional programming
Booting into functional programming
Dhaval Dalal
 
Core Java - Quiz Questions - Bug Hunt
Core Java - Quiz Questions - Bug Hunt
CodeOps Technologies LLP
 
Python Programming Essentials - M8 - String Methods
Python Programming Essentials - M8 - String Methods
P3 InfoTech Solutions Pvt. Ltd.
 
Java script – basic auroskills (2)
Java script – basic auroskills (2)
BoneyGawande
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Mario Fusco
 
JavaScript Functions
JavaScript Functions
Colin DeCarlo
 
Lambda and Stream Master class - part 1
Lambda and Stream Master class - part 1
José Paumard
 
If You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are Wrong
Mario Fusco
 
Jumping-with-java8
Jumping-with-java8
Dhaval Dalal
 
Java 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forward
Mario Fusco
 
LinkedIn TBC JavaScript 100: Functions
LinkedIn TBC JavaScript 100: Functions
Adam Crabtree
 
Swiss army knife Spring
Swiss army knife Spring
Mario Fusco
 
Refactoring to Java 8 (Devoxx BE)
Refactoring to Java 8 (Devoxx BE)
Trisha Gee
 
Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8
RichardWarburton
 
Leveraging Completable Futures to handle your query results Asynchrhonously
Leveraging Completable Futures to handle your query results Asynchrhonously
David Gómez García
 
JavaScript Functions
JavaScript Functions
Brian Moschel
 
Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...
Mario Fusco
 
Java9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidad
David Gómez García
 
Booting into functional programming
Booting into functional programming
Dhaval Dalal
 
Java script – basic auroskills (2)
Java script – basic auroskills (2)
BoneyGawande
 

Similar to Creating Lazy stream in CSharp (20)

Lowering in C#: What really happens with your code?, from NDC Oslo 2019
Lowering in C#: What really happens with your code?, from NDC Oslo 2019
David Wengier
 
NetPonto - The Future Of C# - NetConf Edition
NetPonto - The Future Of C# - NetConf Edition
Paulo Morgado
 
15. Streams Files and Directories
15. Streams Files and Directories
Intro C# Book
 
Tuga IT 2018 Summer Edition - The Future of C#
Tuga IT 2018 Summer Edition - The Future of C#
Paulo Morgado
 
Concurrent Collections Object In Dot Net 4
Concurrent Collections Object In Dot Net 4
Neeraj Kaushik
 
F# in the enterprise
F# in the enterprise
7sharp9
 
csharp dot net codes for practical examination
csharp dot net codes for practical examination
SanikaPatil68377
 
Collection
Collection
Gayathri Ganesh
 
final project for C#
final project for C#
Benjamin Fulker
 
Multi core programming 2
Multi core programming 2
Robin Aggarwal
 
Oops pramming with examples
Oops pramming with examples
Syed Khaleel
 
Csharp_Chap13
Csharp_Chap13
Mohamed Krar
 
C# Starter L04-Collections
C# Starter L04-Collections
Mohammad Shaker
 
C# labprograms
C# labprograms
Jafar Nesargi
 
How to capture a variable in C# and not to shoot yourself in the foot
How to capture a variable in C# and not to shoot yourself in the foot
Sofia Fateeva
 
How to capture a variable in C# and not to shoot yourself in the foot
How to capture a variable in C# and not to shoot yourself in the foot
PVS-Studio
 
What's new in c#7
What's new in c#7
Kyrylo Bezpalyi
 
using System.docx
using System.docx
KunalkishorSingh4
 
Whats new in_csharp4
Whats new in_csharp4
Abed Bukhari
 
Mixing functional and object oriented approaches to programming in C#
Mixing functional and object oriented approaches to programming in C#
Mark Needham
 
Lowering in C#: What really happens with your code?, from NDC Oslo 2019
Lowering in C#: What really happens with your code?, from NDC Oslo 2019
David Wengier
 
NetPonto - The Future Of C# - NetConf Edition
NetPonto - The Future Of C# - NetConf Edition
Paulo Morgado
 
15. Streams Files and Directories
15. Streams Files and Directories
Intro C# Book
 
Tuga IT 2018 Summer Edition - The Future of C#
Tuga IT 2018 Summer Edition - The Future of C#
Paulo Morgado
 
Concurrent Collections Object In Dot Net 4
Concurrent Collections Object In Dot Net 4
Neeraj Kaushik
 
F# in the enterprise
F# in the enterprise
7sharp9
 
csharp dot net codes for practical examination
csharp dot net codes for practical examination
SanikaPatil68377
 
Multi core programming 2
Multi core programming 2
Robin Aggarwal
 
Oops pramming with examples
Oops pramming with examples
Syed Khaleel
 
C# Starter L04-Collections
C# Starter L04-Collections
Mohammad Shaker
 
How to capture a variable in C# and not to shoot yourself in the foot
How to capture a variable in C# and not to shoot yourself in the foot
Sofia Fateeva
 
How to capture a variable in C# and not to shoot yourself in the foot
How to capture a variable in C# and not to shoot yourself in the foot
PVS-Studio
 
Whats new in_csharp4
Whats new in_csharp4
Abed Bukhari
 
Mixing functional and object oriented approaches to programming in C#
Mixing functional and object oriented approaches to programming in C#
Mark Needham
 
Ad

More from Dhaval Dalal (20)

Sri-Aurobindos-Integral-Education-Principles.pdf
Sri-Aurobindos-Integral-Education-Principles.pdf
Dhaval Dalal
 
Test Pyramid in Microservices Context
Test Pyramid in Microservices Context
Dhaval Dalal
 
Code Retreat
Code Retreat
Dhaval Dalal
 
Json Viewer Stories
Json Viewer Stories
Dhaval Dalal
 
Value Objects
Value Objects
Dhaval Dalal
 
Mars rover-extension
Mars rover-extension
Dhaval Dalal
 
How Is Homeopathy Near To Yoga?
How Is Homeopathy Near To Yoga?
Dhaval Dalal
 
Approaching ATDD/BDD
Approaching ATDD/BDD
Dhaval Dalal
 
Paradigms Code jugalbandi
Paradigms Code jugalbandi
Dhaval Dalal
 
Data Reconciliation
Data Reconciliation
Dhaval Dalal
 
CodeRetreat
CodeRetreat
Dhaval Dalal
 
4-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr2015
4-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr2015
Dhaval Dalal
 
3-CodeJugalbandi-currying-pfa-healthycodemagazine#mar2015
3-CodeJugalbandi-currying-pfa-healthycodemagazine#mar2015
Dhaval Dalal
 
CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015
CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015
Dhaval Dalal
 
CodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-Issue
CodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-Issue
Dhaval Dalal
 
The tao-of-transformation-workshop
The tao-of-transformation-workshop
Dhaval Dalal
 
Grooming with Groovy
Grooming with Groovy
Dhaval Dalal
 
Language portfolio
Language portfolio
Dhaval Dalal
 
Code jugalbandi
Code jugalbandi
Dhaval Dalal
 
A case-for-graph-db
A case-for-graph-db
Dhaval Dalal
 
Sri-Aurobindos-Integral-Education-Principles.pdf
Sri-Aurobindos-Integral-Education-Principles.pdf
Dhaval Dalal
 
Test Pyramid in Microservices Context
Test Pyramid in Microservices Context
Dhaval Dalal
 
Json Viewer Stories
Json Viewer Stories
Dhaval Dalal
 
Mars rover-extension
Mars rover-extension
Dhaval Dalal
 
How Is Homeopathy Near To Yoga?
How Is Homeopathy Near To Yoga?
Dhaval Dalal
 
Approaching ATDD/BDD
Approaching ATDD/BDD
Dhaval Dalal
 
Paradigms Code jugalbandi
Paradigms Code jugalbandi
Dhaval Dalal
 
Data Reconciliation
Data Reconciliation
Dhaval Dalal
 
4-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr2015
4-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr2015
Dhaval Dalal
 
3-CodeJugalbandi-currying-pfa-healthycodemagazine#mar2015
3-CodeJugalbandi-currying-pfa-healthycodemagazine#mar2015
Dhaval Dalal
 
CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015
CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015
Dhaval Dalal
 
CodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-Issue
CodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-Issue
Dhaval Dalal
 
The tao-of-transformation-workshop
The tao-of-transformation-workshop
Dhaval Dalal
 
Grooming with Groovy
Grooming with Groovy
Dhaval Dalal
 
Language portfolio
Language portfolio
Dhaval Dalal
 
A case-for-graph-db
A case-for-graph-db
Dhaval Dalal
 
Ad

Recently uploaded (20)

Oracle Cloud and AI Specialization Program
Oracle Cloud and AI Specialization Program
VICTOR MAESTRE RAMIREZ
 
Murdledescargadarkweb.pdfvolumen1 100 elementary
Murdledescargadarkweb.pdfvolumen1 100 elementary
JorgeSemperteguiMont
 
Bridging the divide: A conversation on tariffs today in the book industry - T...
Bridging the divide: A conversation on tariffs today in the book industry - T...
BookNet Canada
 
FIDO Seminar: Evolving Landscape of Post-Quantum Cryptography.pptx
FIDO Seminar: Evolving Landscape of Post-Quantum Cryptography.pptx
FIDO Alliance
 
Edge-banding-machines-edgeteq-s-200-en-.pdf
Edge-banding-machines-edgeteq-s-200-en-.pdf
AmirStern2
 
cnc-drilling-dowel-inserting-machine-drillteq-d-510-english.pdf
cnc-drilling-dowel-inserting-machine-drillteq-d-510-english.pdf
AmirStern2
 
Viral>Wondershare Filmora 14.5.18.12900 Crack Free Download
Viral>Wondershare Filmora 14.5.18.12900 Crack Free Download
Puppy jhon
 
Data Validation and System Interoperability
Data Validation and System Interoperability
Safe Software
 
FIDO Seminar: Targeting Trust: The Future of Identity in the Workforce.pptx
FIDO Seminar: Targeting Trust: The Future of Identity in the Workforce.pptx
FIDO Alliance
 
Analysis of the changes in the attitude of the news comments caused by knowin...
Analysis of the changes in the attitude of the news comments caused by knowin...
Matsushita Laboratory
 
FME for Distribution & Transmission Integrity Management Program (DIMP & TIMP)
FME for Distribution & Transmission Integrity Management Program (DIMP & TIMP)
Safe Software
 
“Why It’s Critical to Have an Integrated Development Methodology for Edge AI,...
“Why It’s Critical to Have an Integrated Development Methodology for Edge AI,...
Edge AI and Vision Alliance
 
Artificial Intelligence in the Nonprofit Boardroom.pdf
Artificial Intelligence in the Nonprofit Boardroom.pdf
OnBoard
 
Mastering AI Workflows with FME - Peak of Data & AI 2025
Mastering AI Workflows with FME - Peak of Data & AI 2025
Safe Software
 
MuleSoft for AgentForce : Topic Center and API Catalog
MuleSoft for AgentForce : Topic Center and API Catalog
shyamraj55
 
FIDO Seminar: New Data: Passkey Adoption in the Workforce.pptx
FIDO Seminar: New Data: Passkey Adoption in the Workforce.pptx
FIDO Alliance
 
Supporting the NextGen 911 Digital Transformation with FME
Supporting the NextGen 911 Digital Transformation with FME
Safe Software
 
FME for Good: Integrating Multiple Data Sources with APIs to Support Local Ch...
FME for Good: Integrating Multiple Data Sources with APIs to Support Local Ch...
Safe Software
 
Can We Use Rust to Develop Extensions for PostgreSQL? (POSETTE: An Event for ...
Can We Use Rust to Develop Extensions for PostgreSQL? (POSETTE: An Event for ...
NTT DATA Technology & Innovation
 
vertical-cnc-processing-centers-drillteq-v-200-en.pdf
vertical-cnc-processing-centers-drillteq-v-200-en.pdf
AmirStern2
 
Oracle Cloud and AI Specialization Program
Oracle Cloud and AI Specialization Program
VICTOR MAESTRE RAMIREZ
 
Murdledescargadarkweb.pdfvolumen1 100 elementary
Murdledescargadarkweb.pdfvolumen1 100 elementary
JorgeSemperteguiMont
 
Bridging the divide: A conversation on tariffs today in the book industry - T...
Bridging the divide: A conversation on tariffs today in the book industry - T...
BookNet Canada
 
FIDO Seminar: Evolving Landscape of Post-Quantum Cryptography.pptx
FIDO Seminar: Evolving Landscape of Post-Quantum Cryptography.pptx
FIDO Alliance
 
Edge-banding-machines-edgeteq-s-200-en-.pdf
Edge-banding-machines-edgeteq-s-200-en-.pdf
AmirStern2
 
cnc-drilling-dowel-inserting-machine-drillteq-d-510-english.pdf
cnc-drilling-dowel-inserting-machine-drillteq-d-510-english.pdf
AmirStern2
 
Viral>Wondershare Filmora 14.5.18.12900 Crack Free Download
Viral>Wondershare Filmora 14.5.18.12900 Crack Free Download
Puppy jhon
 
Data Validation and System Interoperability
Data Validation and System Interoperability
Safe Software
 
FIDO Seminar: Targeting Trust: The Future of Identity in the Workforce.pptx
FIDO Seminar: Targeting Trust: The Future of Identity in the Workforce.pptx
FIDO Alliance
 
Analysis of the changes in the attitude of the news comments caused by knowin...
Analysis of the changes in the attitude of the news comments caused by knowin...
Matsushita Laboratory
 
FME for Distribution & Transmission Integrity Management Program (DIMP & TIMP)
FME for Distribution & Transmission Integrity Management Program (DIMP & TIMP)
Safe Software
 
“Why It’s Critical to Have an Integrated Development Methodology for Edge AI,...
“Why It’s Critical to Have an Integrated Development Methodology for Edge AI,...
Edge AI and Vision Alliance
 
Artificial Intelligence in the Nonprofit Boardroom.pdf
Artificial Intelligence in the Nonprofit Boardroom.pdf
OnBoard
 
Mastering AI Workflows with FME - Peak of Data & AI 2025
Mastering AI Workflows with FME - Peak of Data & AI 2025
Safe Software
 
MuleSoft for AgentForce : Topic Center and API Catalog
MuleSoft for AgentForce : Topic Center and API Catalog
shyamraj55
 
FIDO Seminar: New Data: Passkey Adoption in the Workforce.pptx
FIDO Seminar: New Data: Passkey Adoption in the Workforce.pptx
FIDO Alliance
 
Supporting the NextGen 911 Digital Transformation with FME
Supporting the NextGen 911 Digital Transformation with FME
Safe Software
 
FME for Good: Integrating Multiple Data Sources with APIs to Support Local Ch...
FME for Good: Integrating Multiple Data Sources with APIs to Support Local Ch...
Safe Software
 
Can We Use Rust to Develop Extensions for PostgreSQL? (POSETTE: An Event for ...
Can We Use Rust to Develop Extensions for PostgreSQL? (POSETTE: An Event for ...
NTT DATA Technology & Innovation
 
vertical-cnc-processing-centers-drillteq-v-200-en.pdf
vertical-cnc-processing-centers-drillteq-v-200-en.pdf
AmirStern2
 

Creating Lazy stream in CSharp

  • 1. Hand-Toss a Stream (Lazy List) in C# Dhaval Dalal https://dhavaldalal.wordpress.com @softwareartisan
  • 2. Implementing Lazy List IEnumerable<int> Naturals(int @from) {
 for (var i = @from; true ; i++) {
 yield return i;
 }
 }
 Naturals(0) .Take(3) .ForEach(Console.WriteLine); // 0 1 2 Using generators This is the idiomatic approach. There is yet another one as well.
  • 4. Using Lambda Wrap the tail of the list in a closure. 1 ? Implementing Lazy List
  • 5. Using Lambda Wrap the tail of the list in a closure. When we need the result evaluate the tail by invoking the closure. Evaluate tail 1 ? 1 * 2 ? Implementing Lazy List
  • 6. Using Lambda Wrap the tail of the list in a closure. When we need the result evaluate the tail by invoking the closure. Additionally for performance - Cache or Memoize the closure output. Evaluate tail 1 ? 1 * 2 ? Implementing Lazy List
  • 7. Using Lambda Wrap the tail of the list in a closure. When we need the result evaluate the tail by invoking the closure. Additionally for performance - Cache or Memoize the closure output. Evaluate tail 1 ? 1 * 2 ? Implementing Lazy List Stream implementation shown in these slides is available on: https://github.com/CodeJugalbandi/FunctionalProgramming/blob/master/melodies/lazy_sequences/Stream.cs
  • 8. Immutable Stream with eager Head & lazy Tail sealed class Stream<T> {
 private readonly T head;
 private readonly Func<Stream<T>> tail;
 
 public Stream(T head, Func<Stream<T>> tail) {
 this.head = head;
 this.tail = tail;
 }
 public T Head {
 get => head;
 }
 public Stream<T> Tail {
 get => tail(); // Need Memoization (for Perf)
 }
 public override string ToString() => $"Stream<{typeof(T)}>({head}, ?)";
 }
  • 9. Immutable Stream with eager Head & lazy Tail sealed class Stream<T> {
 private readonly T head;
 private readonly Lazy<Stream<T>> tail;
 
 public Stream(T head, Lazy<Stream<T>> tail) {
 this.head = head;
 this.tail = tail;
 }
 public T Head {
 get => head;
 }
 public Stream<T> Tail {
 get => tail.Value; // Cached after first eval
 }
 public override string ToString() => $"Stream<{typeof(T)}>({head}, ?)";
 }
  • 10. Immutable Stream with eager Head & lazy Tail var empty = new Stream<int>(0, null);
 Console.WriteLine(empty); // Stream<System.Int32>(0, ?)
 Console.WriteLine(empty.Head); // 0
 Console.WriteLine(empty.Tail);
 
 var singleton = new Stream<int>(1, new Lazy<Stream<int>>(() => empty));
 Console.WriteLine(singleton); // Stream<System.Int32>(1, ?)
 Console.WriteLine(singleton.Head); // 1
 Console.WriteLine(singleton.Tail); // Stream<System.Int32>(0, ?)
 
 var couple = new Stream<int>(2, new Lazy<Stream<int>>(() => singleton));
 Console.WriteLine(couple); // Stream<System.Int32>(2, ?)
 Console.WriteLine(couple.Head); // 2
 Console.WriteLine(couple.Tail); // Stream<System.Int32>(1, ?)
 Console.WriteLine(couple.Tail.Tail); // Stream<System.Int32>(0, ?)
 Console.WriteLine(couple.Tail.Tail.Tail); As Streams are immutable we can structurally share the earlier stream. Boom! Boom!
  • 11. Introduce Empty Stream sealed class Stream<T> {
 public static readonly Stream<T> Empty = new Stream<T>(default(T), null);
 private readonly T head;
 private readonly Lazy<Stream<T>> tail;
 public Stream(T head, Lazy<Stream<T>> tail) { … }
 public T Head { … }
 public Stream<T> Tail { … }
 public bool IsEmpty {
 get => tail == null;
 }
 public override string ToString() {
 if (IsEmpty) 
 return "Empty";
 
 return $"Stream<{typeof(T)}>({head}, ?)";
 }
 }
  • 12. Introduce Empty Stream var empty = Stream<int>.Empty;
 Console.WriteLine(empty); // Empty
 Console.WriteLine(empty.IsEmpty); // True
 
 var singleton = new Stream<int>(1, new Lazy<Stream<int>>(() => empty));
 Console.WriteLine(singleton); // Stream(1, ?)
 Console.WriteLine(singleton.IsEmpty); // False
 
 var couple = new Stream<int>(2, new Lazy<Stream<int>>(() => singleton));
 Console.WriteLine(couple); // Stream(2, ?)
 Console.WriteLine(couple.IsEmpty); // False
  • 13. Doing Side-effects sealed class Stream<T> { … …
 public void ForEach(Action<T> action) {
 if (IsEmpty)
 return; 
 action(Head);
 Tail.ForEach(action);
 }
 } var empty = Stream<int>.Empty;
 empty.ForEach(Console.WriteLine); // Prints Nothing
 var stream = new Stream<int>(2, new Lazy<Stream<int>>(new Stream<int>(1, new Lazy<Stream<int>>(() => empty))));
 stream.ForEach(Console.WriteLine); // 2 1
  • 14. Consing to Stream sealed class Stream<T> {
 … …
 // Cons using +
 public static Stream<T> operator + (Stream<T> s, T element) => 
 new Stream<T>(element, new Lazy<Stream<T>>(() => s));
 } var stream = Stream<int>.Empty + 1 + 2;
 Console.WriteLine(stream); // Stream(2, ?)
 Console.WriteLine(stream.Head); // 2
 Console.WriteLine(stream.Tail); // Stream(1, ?) 
 stream.ForEach(Console.WriteLine); // 2 1 Prepend (Cons)
  • 15. Append to Stream sealed class Stream<T> { …
 // Append using +
 public static Stream<T> operator + (T element, Stream<T> s) => 
 new Stream<T>(element, new Lazy<Stream<T>>(() => s.IsEmpty ? Stream<T>.Empty : s));
 } var stream = 1 + Stream<int>.Empty;
 stream.ForEach(Console.WriteLine); // 1
 
 var stream = 1 + (2 + (3 + (4 + Stream<int>.Empty)));
 stream.ForEach(Console.WriteLine); // 1 2 3 4
  • 16. sealed class Stream<T> { … public static Stream<R> Of<R>(params R[] rs) {
 var stream = Stream<R>.Empty;
 var indices = rs.Length - 1;
 for (var i = indices; i >= 0; i--) {
 stream = stream + rs[i];
 }
 return stream;
 }
 } Stream<int>.Of<int>() .ForEach(Console.WriteLine); // Prints Nothing Stream<int>.Of(1, 2, 3, 4) .ForEach(Console.WriteLine); // 1 2 3 4 Construct a Stream from few elements
  • 17. Concat another Stream sealed class Stream<T> {
 …
 public static Stream<T> operator + (Stream<T> @this, Stream<T> other) { if (@this.IsEmpty)
 return other;
 
 return new Stream<T>(@this.Head, new Lazy<Stream<T>>(() => @this.Tail + other));
 } } var concat1 = Stream<char>.Empty + Stream<char>.Of('a', 'b');
 concat1.ForEach(Console.Write); // ab
 
 var concat2 = Stream<char>.Of('a', 'b') + Stream<char>.Empty;
 concat2.ForEach(Console.Write); // ab
 
 var concat3 = Stream<char>.Of('a', 'b') + Stream<char>.Of('c', 'd', 'e');
 concat3.ForEach(Console.Write); // abcde
  • 18. sealed class Stream<T> {
 … public Stream<T> Take(int howMany) {
 if (IsEmpty || howMany <= 0)
 return Stream<T>.Empty;
 
 return new Stream<T>(Head, new Lazy<Stream<T>>(() => Tail.Take(howMany - 1)));
 }
 } Stream<int>.Empty .Take(2).ForEach(Console.WriteLine); // Prints Nothing
 Stream<int>.Of(1, 2, 3, 4) .Take(2).ForEach(Console.WriteLine); // 1 2
 Stream<int>.Of(1, 2, 3, 4) .Take(12).ForEach(Console.WriteLine); // 1 2 3 4
 Stream<int>.Of(1, 2, 3, 4) .Take(0).ForEach(Console.WriteLine); // Prints Nothing Take few elements
  • 19. sealed class Stream<T> {
 … public Stream<T> Drop(int howMany) {
 if (IsEmpty || howMany <= 0)
 return this;
 
 return Tail.Drop(howMany - 1);
 }
 } Stream<int>.Empty .Drop(2).ForEach(Console.WriteLine); // Prints Nothing
 Stream<int>.Of(1, 2, 3, 4) .Drop(2).ForEach(Console.WriteLine); // 3 4
 Stream<int>.Of(1, 2, 3, 4) .Drop(20).ForEach(Console.WriteLine); // Prints Nothing
 Stream<int>.Of(1, 2, 3, 4) .Drop(0).ForEach(Console.WriteLine); // 1 2 3 4 Drop few elements
  • 20. sealed class Stream<T> {
 … … public static Stream<R> Generate<R>(Func<R> fn) => 
 new Stream<R>(fn(), new Lazy<Stream<R>>(() => Generate(fn))); } var random = new Random();
 Stream<int>.Generate(() => random.Next(100, 150)) .Take(4) .ForEach(Console.WriteLine); // Prints 4 random numbers bet [100, 150) Construct a Stream using lambda - 1
  • 21. sealed class Stream<T> {
 … … public static Stream<R> Iterate<R>(R initial, Func<R, R> fn) => 
 new Stream<R>(initial, new Lazy<Stream<R>>(() => Iterate(fn(initial), fn)));
 } Stream<int>.Iterate(9, x => x + 2) .Take(4) .ForEach(Console.WriteLine); // 9 11 13 15 Construct a Stream using lambda - 2
  • 22. sealed class Stream<T> {
 … … public void Deconstruct(out T first, out Stream<T> rest) {
 if (IsEmpty) 
 throw new ArgumentException("Collection is Empty!");
 
 first = Head;
 rest = Drop(1);
 }
 } var (head, rest) = Stream<int>.Of(1, 2, 3, 4);
 Console.WriteLine("Head = " + head); // 1
 Console.WriteLine("Rest = " + rest); // Stream(2, ?) Deconstruct a Stream
  • 23. sealed class Stream<T> {
 … public void Deconstruct(out T first, out Stream<T> rest) {
 if (IsEmpty) 
 throw new ArgumentException("Collection is Empty!");
 
 first = Head;
 rest = Drop(1);
 }
 public void Deconstruct(out T first, out T second, out Stream<T> rest) => (first, (second, rest)) = this; 
 } var (head, second, rest) = Stream<int>.Of(1, 2, 3, 4); Console.WriteLine("Head = " + head); // 1 Console.WriteLine("Second = " + second); // 2 Console.WriteLine("Rest = " + rest); // Stream(2, ?) Deconstruct a Stream
  • 24. sealed class Stream<T> {
 … public void Deconstruct(out T first, out T second, out Stream<T> rest) =>
 (first, (second, rest)) = this;
 
 public void Deconstruct(out T first, out T second, out T third, out Stream<T> rest) =>
 (first, second, (third, rest)) = this;
 } var (head, second, third, rest) = Stream<int>.Of(1, 2, 3, 4);
 Console.WriteLine("Head = " + head); // 1
 Console.WriteLine("Second = " + second); // 2
 Console.WriteLine("Third = " + third); // 3
 Console.WriteLine("Rest = " + rest); // Stream(4, ?) Deconstruct a Stream
  • 25. Transform each element sealed class Stream<T> {
 … …
 public Stream<R> Select<R>(Func<T, R> fn) {
 if (IsEmpty)
 return Stream<R>.Empty;
 
 return new Stream<R>(fn(Head), new Lazy<Stream<R>>(() => Tail.Select(fn)));
 }
 } var empty = Stream<int>.Empty; Console.WriteLine(empty.Select(x => x * x)); // Prints Nothing var stream = Stream<int>.Of(1, 2);
 Console.WriteLine(stream.Select(x => x * x)); // 1 4
  • 26. Filtering the Stream sealed class Stream<T> {
 …
 public Stream<T> Where(Predicate<T> pred) {
 if (IsEmpty)
 return Stream<T>.Empty;
 
 if (pred(Head)) 
 return new Stream<T>(Head, new Lazy<Stream<T>>(() => Tail.Where(pred)));
 
 return Tail.Where(pred);
 }
 } var empty = Stream<int>.Empty; Console.WriteLine(empty.Where(x => x < 2)); // Prints Nothing var stream = Stream<int>.Of(1, 2);
 Console.WriteLine(stream.Where(x => x < 2)); // 1
  • 27. Flatmap the Stream sealed class Stream<T> { …
 public Stream<T> SelectMany(Func<T, Stream<R>> fn) {
 if (IsEmpty)
 return Stream<R>.Empty;
 
 return fn(Head) + Tail.SelectMany(fn);
 }
 } Stream<char>.Of('a', 'b')
 .SelectMany(c => Stream<int>.Of(1, 2).Select(n => (c, n)))
 .ForEach(t => Console.Write(t)); // (a, 1)(a, 2)(b, 1)(b, 2)
 
 Stream<int>.Empty.SelectMany(c => Stream<int>.Of(1, 2).Select(n => (c, n)))
 .ForEach(t => Console.Write(t)); // Prints Nothing
  • 28. Reverse sealed class Stream<T> {
 … …
 public Stream<T> Reverse() {
 Stream<T> Reverse0(Stream<T> acc, Stream<T> source) {
 if (source.IsEmpty)
 return acc;
 
 return Reverse0(acc + source.Head, source.Tail);
 }
 return Reverse0(Stream<T>.Empty, this);
 }
 } Stream<char>.Of('a', 'b', ‘c') .Reverse().ForEach(Console.Write); // cba 
 Stream<int>.Empty .Reverse().ForEach(Console.WriteLine); // Prints Nothing
  • 29. Take while predicate holds sealed class Stream<T> {
 …
 public Stream<T> TakeWhile(Predicate<T> pred) {
 if (IsEmpty)
 return Stream<T>.Empty;
 
 if (pred(Head))
 return Head + Tail.TakeWhile(pred);
 
 return Stream<T>.Empty;
 }
 } Stream<char>.Of('a', 'a', 'b', 'c').TakeWhile(c => c == 'a')
 .ForEach(Console.Write); // aa
 
 Stream<char>.Of('a', 'a', 'b', 'c').TakeWhile(c => c == 'b')
 .ForEach(Console.Write); // Prints Nothing
  • 30. sealed class Stream<T> {
 …
 public Stream<T> DropWhile(Predicate<T> pred) {
 if (IsEmpty)
 return Stream<T>.Empty;
 
 if (pred(Head))
 return Tail.DropWhile(pred);
 
 return this;
 }
 } Stream<char>.Of('a', 'a', 'b', 'c').DropWhile(c => c == 'a')
 .ForEach(Console.Write); // bc
 
 Stream<char>.Of('a', 'a', 'b', 'c').DropWhile(c => c == 'b')
 .ForEach(Console.Write); // aabc Drop while predicate holds
  • 31. sealed class Stream<T> {
 …
 public U Aggregate<U>(U identity, Func<U, T, U> func) {
 if (IsEmpty)
 return identity;
 
 return Tail.Aggregate(func(identity, Head), func);
 }
 } var sum1 = Stream<int>.Of(1, 2, 3, 4) .Aggregate(0, (acc, elem) => acc + elem);
 Console.WriteLine($"sum = {sum1}"); // 10
 var sum2 = Stream<int>.Of<int>() .Aggregate(0, (acc, elem) => acc + elem); 
 Console.WriteLine($"sum = {sum2}"); // 0 Aggregate or Reduce
  • 32. sealed class Stream<T> {
 …
 public bool All(Predicate<T> pred) {
 bool All0(bool accumulator, Stream<T> stream) {
 if (stream.IsEmpty || accumulator == false)
 return accumulator;
 
 return All0(accumulator && pred(stream.Head), stream.Tail);
 }
 return All0(true, this);
 }
 } Console.WriteLine(Stream<int>.Of<int>().All(x => x % 2 == 0)); // True
 Console.WriteLine(Stream<int>.Of<int>(2, 4).All(x => x % 2 == 0)); // True
 Console.WriteLine(Stream<int>.Of<int>(1, 2, 4).All(x => x % 2 == 0)); // False All Don’t evaluate the entire Stream, short-circuit if we already determined negation.
  • 33. sealed class Stream<T> {
 …
 public bool Any(Predicate<T> pred) {
 bool Any0(bool accumulator, Stream<T> stream) {
 if (stream.IsEmpty || accumulator == true)
 return accumulator;
 
 return Any0(accumulator || pred(stream.Head), stream.Tail);
 }
 return Any0(false, this);
 }
 } Console.WriteLine(Stream<int>.Of<int>().Any(x => x % 2 == 0)); // False
 Console.WriteLine(Stream<int>.Of<int>(2, 4).Any(x => x % 2 == 0)); // True
 Console.WriteLine(Stream<int>.Of<int>(1, 2, 4).Any(x => x % 2 == 0)); // True
 Console.WriteLine(Stream<int>.Of<int>(1, 3).Any(x => x % 2 == 0)); // False Any Don’t evaluate the entire Stream, short-circuit if we already determined affirmation.
  • 34. sealed class Stream<T> {
 …
 public Stream<T> Scan(U identity, Func<U, T, U> func) {
 if (IsEmpty)
 return Stream<T>.Empty;
 
 U newHead = func(identity, Head);
 return newHead + Tail.Scan(newHead, func);
 }
 } // Prints running sum
 Stream<int>.Of(1, 2, 3, 4) .Scan(0, (acc, elem) => acc + elem) .ForEach(Console.WriteLine); // 1 3 6 10
 Stream<int>.Of<int>() .Scan(0, (acc, elem) => acc + elem) .ForEach(Console.WriteLine); // Prints Nothing Scan
  • 35. Zip two Streams sealed class Stream<T> {
 …
 public Stream<(T,U)> Zip<U>(Stream<U> that) {
 if (this.IsEmpty || that.IsEmpty)
 return Stream<(T,U)>.Empty;
 
 return (this.Head, that.Head) + this.Tail.Zip(that.Tail);
 }
 } Stream<char>.Of('a', 'b').Zip(Stream<int>.Of(1, 2))
 .ForEach(t => Console.Write(t)); // (a, 1)(b, 2)
 
 Stream<char>.Of('a', 'b').Zip(Stream<int>.Empty)
 .ForEach(t => Console.Write(t)); // Prints Nothing
 
 Stream<int>.Empty.Zip(Stream<char>.Of('a', 'b'))
 .ForEach(t => Console.Write(t)); // Prints Nothing
  • 36. Zip with function sealed class Stream<T> {
 …
 public Stream<R> ZipWith<U, R>(Stream<U> that, Func<T, U, R> fn) {
 if (this.IsEmpty || that.IsEmpty)
 return Stream<R>.Empty;
 
 return fn(this.Head, that.Head) + this.Tail.ZipWith(that.Tail, fn);
 }
 } var numbers = Stream<int>.Of(1, 2, 3);
 numbers.ZipWith(numbers, (n1, n2) => n1 * n2)
 .ForEach(Console.WriteLine); // 1 4 9
 
 numbers.ZipWith(Stream<int>.Empty, (n1, n2) => n1 * n2)
 .ForEach(Console.WriteLine); // Prints Nothing
 
 Stream<int>.Empty.ZipWith(numbers, (n1, n2) => n1 * n2)
 .ForEach(Console.WriteLine); // Prints Nothing
  • 37. Splitsealed class Stream<T> {
 …
 public (Stream<T>, Stream<T>) Split(Predicate<T> pred) {
 (Stream<T>, Stream<T>) Split0(Stream<T> yesAcc, Stream<T> noAcc, Stream<T> source) {
 if (source.IsEmpty) 
 return (yesAcc.Reverse(), noAcc.Reverse());
 
 var elem = source.Head;
 if (pred(elem))
 return Split0(yesAcc + elem, noAcc, source.Tail);
 else
 return Split0(yesAcc, noAcc + elem, source.Tail);
 } 
 return Split0(Stream<T>.Empty, Stream<T>.Empty, this);
 }
 } var (evens, odds) = Stream<int>.Iterate(0, x => x + 1).Take(10) .Split(x => x % 2 == 0);
 evens.ForEach(Console.Write); // 02468
 odds.ForEach(Console.Write); // 13579
  • 38. To List sealed class Stream<T> {
 … public List<T> ToList() {
 var result = new List<T>();
 ForEach(result.Add);
 return result;
 }
 } var list = Stream<int>.Iterate(1, x => x + 1) .Take(4) .ToList();
 foreach (var item in list) {
 Console.WriteLine(item);
 }
  • 39. Find first 6 primes using the Sieve of Eratosthenes Hint: Use Stream created earlier http://world.mathigon.org/Prime_Numbers
  • 40. Sieve Stream<int> From(int start) => Stream<int>.Iterate(start, x => x + 1);
 Stream<int> Sieve(Stream<int> s) {
 var first = s.Head;
 var rest = s.Tail.Where(n => n % first != 0);
 return new Stream<int>(first, new Lazy<Stream<int>>(() => rest));
 }
 
 var primes = Sieve(From(2)).Take(6) primes.ToList() // [2, 3, 5, 7, 11, 13]
  • 41. First 10 Fibonacci Nos. Write a function fibonacci which consumes an integer and produces that many numbers in the fibonacci series. For Example: fibonacci(10) produces [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] Provide Solutions Using Generator Using IEnumerable Using Stream that we developed
  • 42. IEnumerable<int> Fibonacci(int howMany) { var (first, second) = (0, 1);
 for (var i = 0; i < howMany; i++) {
 yield return first;
 (first, second) = (second, first + second);
 }
 }
 
 Fibonacci(10).ToList();
 // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] First 10 Fibonacci Nos. Using Generator
  • 43. IEnumerable<int> Fibonacci(int howMany) { return IEnumerableExtensions.Iterate<(int, int)>((0, 1), tuple => {
 var (first, second) = tuple;
 var next = first + second;
 return (second, next);
 })
 .Select(tuple => {
 var (first, second) = tuple;
 return first;
 })
 .Take(howMany);
 } Fibonacci(10).ToList(); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] First 10 Fibonacci Nos. Using IEnumerable Definition as suggested by Christopher Grande
  • 44. First 10 Fibonacci Nos. Using Stream https://www.haskell.org/tutorial/functions.html var seed = From(0).Take(2);
 // Start with seed elements 0 and 1
 Fibonacci(seed)
 .Take(10)
 .ForEach(Console.WriteLine); // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 Stream<int> Fibonacci(Stream<int> s) {
 var next = s.Zip(s.Tail).Select(tuple => {
 var (first, second) = tuple;
 return first + second;
 });
 return new Stream<int>(s.Head, new Lazy<Stream<int>>(() => Fibonacci(s + next.Head)));
 }
  • 45. Prime Counts Using Lazy Lists, write a program that counts number of primes up to a given number. At every power of 10, it should emit the count of primes obtained thus far. The table shows the output until 10 10 Implement this using: Using Stream we developed earlier. Using IEnumerable Hint: Write an extension method Scan on IEnumerable i/p count 10 4 10 2 25 10 3 168 10 4 1,229 10 5 9,592 10 6 78,498 10 7 6,64,579 10 8 57,61,455 10 9 5,08,47,534 10 10 45,50,52,511
  • 46. class Streams {
 public static Stream<int> Range(int start, int count) {
 if (count < 0)
 throw new ArgumentOutOfRangeException($"{count}");
 
 return Stream<int>.Iterate(start, x => x + 1).Take(count);
 } 
 }
 Streams.Range(1, 5).ForEach(Console.WriteLine); // 1 2 3 4 5 Prime Counts using IEnumerable First, write our own Range
  • 47. Prime Counts Using Stream Stream<(int, int)> PrimeCount(int howManyPowerOf10) {
 bool IsPrime(int x) => Streams.Range(2, x) .Where(n => n < x) .All(n => x % n != 0); 
 return Stream<int>.Iterate(2, x => x + 1)
 .TakeWhile(x => x <= Math.Pow(10, howManyPowerOf10))
 .Select(x => (x, IsPrime(x)))
 .Scan((0, 0), (acc, tuple) => {
 var (x, isPrime) = tuple;
 var (_, count) = acc;
 return isPrime ? (x, count + 1): (x, count);
 })
 .Where(tuple => {
 var (x, _) = tuple;
 return Streams.Range(1, howManyPowerOf10)
 .Any(n => Math.Pow(10, n) == x);
 });
 } PrimeCount(3).ForEach(t => Console.WriteLine(t)); (10, 4), (100, 25), (1000, 168)
  • 48. Prime Counts using IEnumerable First, write our own Scan static class IEnumerableExtensions { public static IEnumerable<U> Scan<T, U>(this IEnumerable<T> @this, U initial, Func<U, T, U> fn) {
 IEnumerable<U> ScannedEnumerable() {
 var acc = seed;
 foreach (var item in @this) {
 acc = fn(acc, item);
 yield return acc;
 } 
 if (@this == null)
 throw new ArgumentNullException("Require non-null list!");
 if (fn == null)
 throw new ArgumentNullException("Require non-null function!”);
 return ScannedEnumerable();
 } }
  • 49. Prime Counts using IEnumerable IEnumerable<(int, int)> PrimeCount(int howManyPowerOf10) {
 bool IsPrime(int x) => Enumerable.Range(2, x) .Where(n => n < x) .All(n => x % n != 0); 
 return IEnumerableExtensions.Iterate(2, x => x + 1)
 .TakeWhile(x => x <= Math.Pow(10, howManyPowerOf10))
 .Select(x => (x, IsPrime(x)))
 .Scan((0, 0), (acc, tuple) => {
 var (x, isPrime) = tuple;
 var (_, count) = acc;
 return isPrime ? (x, count + 1): (x, count);
 })
 .Where(tuple => {
 var (x, _) = tuple;
 return Enumerable.Range(1, howManyPowerOf10)
 .Any(n => Math.Pow(10, n) == x);
 });
 } PrimeCount(3).ForEach(t => Console.WriteLine(t)); (10, 4), (100, 25), (1000, 168)
  • 50. Drawbacks of this Stream Implementation As C# compiler does not support Tail- Recursion, all the APIs that are implemented recursively will cause a stack blow-up at some point for large values of stream. Even-though it uses caching of tail using Lazy<T>, this Stream is not performant like IEnumerable! This is because the recursive APIs don’t run in constant stack space.
  • 51. But still its a good mental exercise to create streams from first principles!