Extensions that support the C# functional paradigm.
The library is written in C# and released with an MIT license, so feel free to fork or use commercially.
Any feedback is appreciated, please visit the issues page or send me an e-mail.
Binaries of the last build can be downloaded on the AppVeyor CI page of the project.
The library is also published on NuGet.org (prerelease), install using:
PM> Install-Package Endless -Pre
Endless is built for .NET v4.0 and .NET Standard 1.1.
- Generate
- Reduce
- Custom IEnumerable extensions
- Existing IEnumerable extensions variations
- Existing IEnumerable extensions overloads
- Stream extensions
- String extensions
- Random extensions
- Functional features
- Samples
The basic function for creating infinite sequences. Can be widely used in algorithms that are based on any kind of iteration.
Creates an infinite list where the first item is calculated by applying the function on the second argument, the second item by applying the function on the previous result and so on.
Sample usages:
DateTime NextFridayThe13th()
{
var future = DateTime.Today.Iterate(x => x.AddDays(1)); // future in a variable
return future.First(x => x.Day == 13 && x.DayOfWeek == DayOfWeek.Friday);
}
IEnumerable<int> enumerable = 2.Iterate(x => x * x).Take(5); // yields 2, 4, 16, 256, 65536
There is also overload for Iterate
, which uses binary function taking two seeds and generating new value using two previous values.
Sample usage:
Generate.Iterate(0, 1, (a, b) => a + b); // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... (Fibonacci sequence)
Creates an infinite list where all items are the first argument.
Sample usage:
IEnumerable<int> ones = 1.Repeat(); // yields 1, 1, 1, ...
Instead of constant value, the repeated value can be also generated by function:
var random = new Random();
IEnumerable<int> enumerable = new Func<int>(random.Next).Repeat().Take(10); // yields 10 random numbers
Creates an infinite list by repeating the given sequence.
Sample usage:
IEnumerable<int> enumerable = new[] { 1, 2, 3 }.Cycle(); // yields 1, 2, 3, 1, 2, 3, 1, 2, ...
Creates IEnumerable containing one item. Mostly used as syntactic sugar or to write more readable one-liners.
Simple usage:
IEnumerable<int> five = 5.Yield();
Can be also used in functions that are returning IEnumerable and we cannot use yield keyword:
IEnumerable<int> GetNumbers()
{
if (some condition)
return 0.Yield();
return someOtherSequence;
}
This code would have to be written without the Yield() function in two separate ways:
IEnumerable<int> GetNumbers()
{
if (some condition)
return new [] { 0 };
return someOtherSequence;
}
IEnumerable<int> GetNumbers()
{
if (some condition)
yield return 0;
foreach(var item in someOtherSequence)
{
yield return item;
}
}
Enumerate extensions allow more functionality than the standard library function Enumerable.Range
.
Simple sequences to generate infinite sequence of natural numbers:
IEnumerable<int> numbers1 = Natural.Numbers; // yields 1, 2, 3, ...
IEnumerable<int> numbers2 = Natural.NumbersWithZero; // yields 0, 1, 2, 3, ...
IEnumerable<BigInteger> numbers3 = BigNatural.Numbers; // yields 1, 2, 3, ...
IEnumerable<BigInteger> numbers4 = BigNatural.NumbersWithZero; // yields 0, 1, 2, 3, ...
Sample usage:
// Find the sum of all the multiples of 3 or 5 below 1000.
int sum = Natural.Numbers.TakeUntil(1000).Where(x => x % 3 == 0 || x % 5 == 0).Sum();
Set of generic enumerators to create useful finite or infinite sequences. The argument is implemented dynamically, it is tested with BigInteger
, byte
, char
, decimal
, double
, float
, int
, long
, but should work with other types too (even custom ones that can be added/subtracted and compared). Also there is a support for DateTime
and DateTimeOffset
.
Enumerate.From(1); // yields 1, 2, 3, ...
Enumerate.From('a'); // yields 'a', 'b', 'c', ...
Enumerate.From(new BigInteger(10)); // yields 10, 11, 12, ...
Enumerate.From((byte) 250); // yields 250, 251, 252, 253, 254, 255, 0, 1, ...
Enumerate.From(new DateTime(1990, 7, 5)); // yields 1990-07-05, 1990-07-06, 1990-07-07, 1990-07-08, ...
Next number is generated by adding the distance of the given two values.
Enumerate.From('a').Then('c'); // yields 'a', 'c', 'e', 'g', 'i', ...
Enumerate.From(1d).Then(0.9); // yields 1, 0.9, 0.8, 0.7, 0.6, ...
Enumerate.From(new BigInteger(1)).Then(3); // yields 1, 3, 5, 7, 9, ...
Enumerate.From(new DateTime(1990, 7, 5, 12, 0, 0)).Then(new DateTime(1990, 7, 5, 13, 0, 0)); // yields 1990-07-05 12:00, 1990-07-05 13:00, 1990-07-05 14:00, ...
The sequence is bounded by the high value.
Enumerate.From('a').To('e'); // yields 'a', 'b', 'c', 'd', 'e'
Enumerate.From('e').To('a'); // yields nothing
Enumerate.From(1M).To(5.5M); // yields 1, 2, 3, 4, 5
Enumerate.From(new DateTime(2014, 1, 1)).To(new DateTime(2014, 31, 1)); // yields 2014-01-01, 2014-01-02, ..., 2014-01-31
Combines both principles above. The sequence is bounded by the highest/lowest value depending on the increasing/decreasing sequence.
Enumerate.From(1d).Then(0.5).To(-1); // yields 1, 0.5, 0, -0.5, -1
Enumerate.From(1).Then(10).To(40); // yields 1, 10, 19, 28, 37
Enumerate.From(1M).Then(1.1M).To(2); // yields 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2
Using this syntax, it is easier to write Haskell-like list comprehensions in C#.
Haskell sample:
take 10 [ (i,j) | i <- [1..], let k = i*i, j <- [1..k]]
C# equivalent:
(from i in Enumerate.From(1)
let k = i * i
from j in Enumerate.From(1).To(k)
select new { i, j }).Take(10);
The library provides implementation of AggregateRight
funciton with both overloads (with or without seed). The difference to original Aggregate
function is that it is evaluated from the right-hand side.
The reduce function must be binary. When using aggregations without seed value, the reduce function requires input and output of the same type.
new[] { 5, 6, 7, 8 }.Aggregate(2, (x, y) => x * y); // evaluates as: (((2 * 5) * 6) * 7) * 8
new[] { 5, 6, 7, 8 }.Aggregate((x, y) => x * y); // evaluates as: ((5 * 6) * 7) * 8
new[] { 5, 6, 7, 8 }.AggregateRight(2, (x, y) => x * y); // evaluates as: 5 * (6 * (7 * (8 * 2)))
new[] { 5, 6, 7, 8 }.AggregateRight((x, y) => x * y); // evaluates as: 5 * (6 * (7 * 8))
In this example it the results will not change depending on the use of right or left aggregation, since the aggregating function is associative. But when using non-associative function like division or matrix multiplication, the results will change.
var squareMatrices = new[] { m1, m2, m3, m4 };
squareMatrices.Aggregate(identityMatrix, multiply); // evaluates as: multiply(multiply(multiply(multiply(identityMatrix, m1), m2), m3), m4)
squareMatrices.AggregateRight(identityMatrix, multiply); // evaluates as: multiply(m1, multiply(m2, multiply(m3, multiply(m4, identityMatrix))))
Scan reductions are similar to the aggregations above, but instead of returning a final value, they return a list of all the intermediate values.
There are two types of scans:
Scan
- evaluated from the left-hand side, either without seed or with seed at the beginningScanRight
- evaluated from the right-hand side, either without seed or with seed at the end
The reduce function must be binary. When using scans without a seed value, the reduce function requires input and output of the same type.
var sequence = { x0, x1, x2, x3, ..., xn-2, xn-1, xn }; // pseudo-code
sequence.Scan(seed, f); // seed, f(seed, x0), f(f(seed, x0), x1), f(f(f(seed, x0), x1), x2), ...
sequence.Scan(f); // x0, f(x0, x1), f(f(x0, x1), x2), f(f(f(x0, x1), x2), x3), ...
sequence.ScanRight(seedf); // ..., f(xn-1, f(xn, seed)), f(xn, seed), seed
sequence.ScanRight(f); // ..., f(xn-2, f(xn-1, xn)), f(xn-1, xn)
Returns a random element from the given sequence.
new [] { 1, 2, 3, 4, 5, 6 }.Random(); // returns 4 <http://xkcd.com/221/>
Also can return random element satisfying given predicate.
new [] { 1, 2, 3, 4, 5, 6 }.Random(x => x % 2 == 0); // returns 2, 4, or 6
"Hello World!!!".Random(char.IsLetter); // returns random letter of the string
Sorts the given sequence randomly.
new [] { 1, 2, 3, 4, 5, 6 }.Shuffle(); // returns e.g. { 2, 3, 6, 1, 4, 5 }
// poker hand sample
var deck = from colour in new[] { "Spades", "Hearts", "Diamonds", "Clubs" }
from rank in new[] { "Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King" }
select new { rank, colour };
var hand = deck.Shuffle().Take(5);
Splits the given sequence to chunks of given size.
new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }.Chunk(4); // returns { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10 } }
Groups results by contiguous values.
new byte[] { 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1 }.ChunkBy();
// returns { { 0, 0 }, { 1, 1, 1, 1, 1 }, { 0 }, { 1, 1 }, { 0, 0 }, { 1, 1 } }
The result of ChunkBy
is always IGrouping
with the key being the value of all elements in the group.
Optionally we can also chunk list of objects using selector.
var person1 = new Person { Name = "Eddy", Age = 18 ... }
var person2 = new Person { Name = "Eddy", Age = 25 ... }
var person3 = new Person { Name = "Zachary", Age = 33 ... }
var person4 = new Person { Name = "Eddy", Age = 20 ... }
{ person1, person2, person3, person4 }.ChunkBy(x => x.Name);
// returns 3 groupings:
// "Eddy" => { person1, person2 }
// "Zachary" => { person3 }
// "Eddy" => { person4 }
The only difference as opposed to GroupBy
is that ChunkBy
requires the elements to be next to each other to group them, not only match on the selected key. That means that it is possible that there are multiple groups with the same key on the output (as can be seen on the example above).
The existing Cast
extension to IEnumerable
sequences does not make use of the operators used for conversion.
So even though there is conversion operator from byte
to char
, this code ends in InvalidCastException
:
var helloAsBytes = new byte[] { 104, 101, 108, 108, 111 };
var helloAsChars = helloAsBytes.Cast<char>().ToArray(); // throws InvalidCastException
If we did the following, it would work:
var helloAsBytes = new byte[] { 104, 101, 108, 108, 111 };
var helloAsChars = helloAsBytes.Select(x => (char) x).ToArray(); // equal to { 'h', 'e', 'l', 'l', 'o' }
DynamicCast
uses this exact method for type casting inside. So this same code can be rewritten like this:
var helloAsBytes = new byte[] { 104, 101, 108, 108, 111 };
var helloAsChars = helloAsBytes.DynamicCast<char>().ToArray(); // equal to { 'h', 'e', 'l', 'l', 'o' }
Another example might be:
new object[] { 'h', 1.5d, new BigInteger(5) }.DynamicCast<int>(); // yields ints 104, 1, 5
The library provides IEnumerable extension method called Cached
. This method ensures that no item in the enumerable will be evaluated twice. Once an item is evaluated, it is stored in the internal memory of the enumerable.
This is similar to methods like .ToList()
or .ToArray()
, but there is an important difference. As opposed to these methods, calling .Cached()
on an enumerable does not evaluate any items yet. The items are evaluated on first access of certain item, then they are stored in the internal list.
This makes it possible to use .Cached()
on infinite sequences. Extensions .ToList()
or .ToArray()
cannot be called on infinite sequences, because they try to evaluate the whole sequence immediately. The Cached
extension makes sure that the values are evaluated lazily, but ensures that no item in the sequence will be evaluated twice. Once it is evaluated, the result is stored.
Example of not using the Cached
extension:
var sequence1 = expensiveToEnumerate.Take(10).ToList();
var sequence2 = expensiveToEnumerate.Take(15).ToList();
Note that the sequence is enumerated twice, which means that we need to do the expensive evaluation 25 times.
Example of using the Cached
extension:
using (var cached = expensiveToEnumerate.Cached())
{
var sequence1 = cached.Take(10).ToList();
var sequence2 = cached.Take(15).ToList();
}
Now while computing sequence1
, we do expensive evaluation of 10 items, but the result is stored in internal list. So when we compute sequence2
, we take the 10 items from the internal list, then do expensive evaluation of other 5 items which were not cached yet.
Note that the Cached
example is used with the using
block. That is because it holds reference to the IEnumerator
of the sequence with current position, which itself is IDisposable
. When using the foreach
statements or LINQ methods, we don't need the using statements because they are automatically implemented inside when handling the IEnumerator
of the sequence. Here we might need to dispose the enumerator after we are done working with the sequence, thus the using
statement is recommended. Disposing the cached enumerable calls Dispose
on the enumerator of the cached sequence.
Returns true if this sequence starts with given subsequence.
Sample usage:
new[] { 1, 2, 3, 4, 5 }.StartsWith(new [] { 1, 2 }); // returns true
bytes.StartsWith<byte>(0xEF, 0xBB, 0xBF); // bytes sequence starts with UTF8 BOM
Extensions similar to the IEnumerable
extensions that already exist.
Init
returns everything from the sequence except the last element.
Sample usage:
new [] { 1, 2, 3, 4, 5 }.Init(); // returns { 1, 2, 3, 4 }
Tail
returns everything from the sequence except the first element.
Sample usage:
new [] { 1, 2, 3, 4, 5 }.Init(); // returns { 2, 3, 4, 5 }
IsEmpty
is the opposite of Any
, it returns true if the given sequence is empty.
Sample usage:
new int[] { 1, 2, 3 }.IsEmpty(); // returns false
new int[] { }.IsEmpty(); // returns true
TakeUntil
is the complement to TakeWhile
. It has three different overloads:
(this IEnumerable<T> source, T item)
- Returns all elements of the sequence from the begining until given element. The given element will not be part of the sequence.
- Sample:
new [] { 1, 2, 3 }.TakeUntil(3)
returns{ 1, 2 }
(this IEnumerable<T> source, Func<T, bool> predicate)
- Returns elements from a sequence until given predicate is true.
- Sample:
new [] { 1, 2, 3 }.TakeUntil(x => x > 2)
returns{ 1, 2 }
(this IEnumerable<T> source, Func<T, int, bool> predicate)
- Same as the previous, only element index can be used in the predicate as well.
SkipUntil
is the complement to SkipWhile
and very similar to TakeUntil
. It has three different overloads:
(this IEnumerable<T> source, T item)
- Bypasses elements in a sequence as long as they are not equal to given element, then returns the remaining elements.
- Sample:
new [] { 1, 2, 3 }.SkipUntil(3)
returns{ 3 }
(this IEnumerable<T> source, Func<T, bool> predicate)
- Bypasses elements in a sequence until given predicate is true, then returns the remaining elements.
- Sample:
new [] { 1, 2, 3 }.SkipUntil(x => x > 2)
returns{ 3 }
(this IEnumerable<T> source, Func<T, int, bool> predicate)
- Same as the previous, only element index can be used in the predicate as well.
Sort
and SortDescending
functions sorts the sequence using default comparer.
Equivalent to .OrderBy(x => x)
and .OrderByDescending(x => x)
.
IndexOf
provides the same functionality as existing IndexOf
for List<T>, only for all IEnumerable<T>
; it returns the zero-based index of the first occurrence.
Extensions overloading the existing IEnumerable
extensions.
The library provides parameterless overload to SelectMany
.
Calling sequence.SelectMany()
is equivalent to calling sequence.SelectMany(x => x)
.
Sample usage:
var numbers = new [] { new [] { 1, 2, 3 }, new [] { 4, 5, 6 }, new [] { 7, 8, 9 };
numbers.SelectMany(); // returns { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
The library provides overload to Except
accepting items as params.
Calling sequence.Except(item)
is equivalent to calling sequence.Except(new [] { item })
.
Sample usage:
new [] { 1, 2, 3 }.Except(3); // returns { 1, 2 }
"abc".Except('a', 'b'); // returns { 'c' }
The library provides overload to Concat
method on IEnumerable
with lazy second sequence.
As opposed to the standard type signature:
IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
The overload has signature:
IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, Func<IEnumerable<TSource>> second)
That allows to pass in function that will create the second sequence. That function will be called only when items at that position need to be evaluated (i.e. all the items of first sequences were already enumerated). Otherwise the function will not be called at all.
Sample usage:
var combined = new { 1, 2, 3 }.Concat(GetSecondSequence);
var result1 = combined.Take(3).ToList(); // GetSecondSequence is not called
var result2 = combined.Take(5).ToList(); // GetSecondSequence is called
The library provides overload to Zip
extension without the resultSelector
parameter. Without specifying the result, the sequences will be zipped to sequence of tuples.
Calling first.Zip(second)
is equivalent to calling first.Zip(second, Tuple.Create)
.
Sample usage:
var example = Natural.Numbers.Zip(Enumerate.From('a').To('c'));
// returns (1, 'a'), (2, 'b'), (3, 'c')
The library provides overload to ToDictionary
extension for sequence of binary tuples (as produced in the Zip
overload above).
It takes the first item of the tuple as key and the second item as value, which makes calling sequence.ToDictionary()
equivalent to calling sequence.ToDictionary(x => x.Item1, x => x.Item2)
assuming that the sequence has type IEnumerable<Tuple<T1, T2>>
.
Sample usage:
var example = Natural.Numbers.Zip(Enumerate.From('a').To('c')).ToDictionary();
// returns { 1 => 'a', 2 => 'b', 3 => 'c' }
The library provides overloads of Sum
, Min
and Max
for using with BigInteger
. The functions behave the same as the corresponding LINQ extensions, only they accept IEnumerable<BigInteger>
as well.
Working with streams and IEnumerable<byte>
in harmony.
Enumerate
and EnumerateBuffered
are extensions on the Stream
class which convert given stream to IEnumerable<byte>
. Iterating through the resulting enumerable will read bytes from the input stream. This allows to work with the byte streams in more functional way.
// Detect whether given file has UTF8 BOM
private static bool FileHasUTF8ByteOrderMark(string filename)
{
using (var stream = File.OpenRead(filename))
return stream.Enumerate().StartsWith<byte>(0xEF, 0xBB, 0xBF);
}
This allows to use functions like StartsWith
, which are extensions to IEnumerable<T>
. Whenever the enumerable is iterated, it seeks to the beginning of the stream and starts reading all over. If we don't want this behavior and want all the bytes, which were read to be stored (e.g. because the input stream is not seekable), we can use the Cached
extension:
using (var stream = File.OpenRead(filename))
using (var sequence = stream.Enumerate().Cached())
{
// do stuff with sequence, evaluate it multiple times
}
When using the Enumerate
extension, each time the IEnumerator.MoveNext
is called, one byte from the stream is read. This will not perform very well in most cases, since each read from stream is expensive. That's why there is an alternative way, which reads from stream by fixed-length buffer (8 KB by default, but customizable). By using EnumerateBuffered
we can minimize the number of reads from the stream.
using (var stream = File.OpenRead(filename))
{
// when using buffer of 16,000 bytes, the array of 20,000 bytes will result in two stream.Read() calls
var bytes = stream.EnumerateBuffered(16000).Take(20000).ToArray();
}
Write
and WriteBuffered
will write sequences of bytes (IEnumerable<byte>
) to given stream. Using WriteBuffered
is recommended as it performs better; this way, writes will be buffered by 8 KB by default.
IEnumerable<byte> bytes;
using (var stream = File.OpenWrite(filename))
{
stream.WriteBuffered(bytes);
}
Provides stream interface to given sequence of bytes.
Stream ToStream(this IEnumerable<byte> bytes)
The underlying stream is MemoryStream
but the bytes are only written to memory when needed (when they are being accessed from the stream). This allows for infinite sequences to be used with ToStream
extension.
var random = new Random();
// infinite sequence of random bytes
IEnumerable<byte> randomBytes = new Func<byte>(() => random.NextByte()).Repeat();
// infinite stream of random data
using (Stream randomStream = randomBytes.ToStream())
{
// ...
}
Extensions for easier work with strings.
BuildString
is an extension to IEnumerable<char>
which allows easier building of new string objects.
var chars = new[] { 'h', 'e', 'l', 'l', 'o' };
var result = chars.BuildString(); // "hello"
JoinStrings
is an extension to IEnumerable<string>
which does string.Join
or string.Concat
inside (depending on the separator parameter).
new[] { "hello", null, " ", "world", "", "!" }.JoinStrings(); // "hello world!"
new[] { "New York", "Mumbai", "Berlin", "Istanbul" }.JoinStrings(", "); // "New York, Mumbai, Berlin, Istanbul"
Extensions for System.Random
.
The library adds NextByte
and NextChar
in addition to existing Next
and NextDouble
methods.
var random = new Random();
var someByte = random.NextByte();
var someChar = random.NextChar();
(documentation TBD)
(documentation TBD)
(documentation TBD)
(documentation TBD)
Pipe applies given function to it's caller allowing to write function calls similar to Unix pipeline. It has the same effect as calling the function directly, but it might be more readable to write some processes in this way.
TOut Pipe<TIn, TOut>(this TIn _this, Func<TIn, TOut> func)
h(g(f(x))) // normal function call
x.Pipe(f).Pipe(g).Pipe(h) // pipeline
int DigitSum(int number)
{
return Math.Abs(number).Iterate(x => x / 10).TakeUntil(0).Select(x => x % 10).Sum();
}
Uses Endless extensions Iterate() and TakeUntil().
DateTime NextFridayThe13th()
{
var future = DateTime.Today.Iterate(x => x.AddDays(1));
return future.First(x => x.Day == 13 && x.DayOfWeek == DayOfWeek.Friday);
}
Uses Endless extension Iterate().
IEnumerable<string> FizzBuzz()
{
var fizzBuzz = Natural.Numbers.Select(x =>
{
if (x % 15 == 0) return "Fizz Buzz";
if (x % 3 == 0) return "Fizz";
if (x % 5 == 0) return "Buzz";
return x.ToString();
});
return fizzBuzz; // infinite Fizz buzz sequence
}
Uses Endless Natural.Numbers.
FizzBuzz().Take(15).JoinStrings(", "); // "1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, Fizz Buzz"
Uses Endless extension JoinStrings().
IEnumerable<int> GetPrimes()
{
return Sieve(Enumerate.From(2));
}
IEnumerable<int> Sieve(IEnumerable<int> list)
{
int prime = list.First();
yield return prime;
foreach (var other in Sieve(list.Tail().Where(x => x % prime != 0)))
{
yield return other;
}
}
Uses Endless extensions Enumerate.From() and Tail().
var rot = new Func<int, string, string>((degree, input) =>
{
var alphabet = Enumerate.From('a').To('z');
var shiftedAlphabet = alphabet.Cycle().Skip(degree).Take(26);
var repeatWithUpperCase = new Func<IEnumerable<char>, IEnumerable<char>>(x => x.Concat(x.Select(char.ToUpper)));
var transformation = alphabet.Pipe(repeatWithUpperCase)
.Zip(shiftedAlphabet.Pipe(repeatWithUpperCase))
.ToDictionary();
var result = input.Select(c => transformation.ContainsKey(c) ? transformation[c] : c).BuildString();
return result;
});
var rot13 = rot.Curry()(13);
Uses Endless methods Enumerate.From() and .To(), Cycle(), Pipe(), overloads of Zip() and ToDictionary(), BuildString() and Curry.
var randomGenerator = new Random(666);
// infinite sequence of random numbers
var randomNumbers = new Func<double>(randomGenerator.NextDouble).Repeat();
// infinite sequence of random points
var randomPoints = randomNumbers.Zip(randomNumbers, (x, y) => new { x, y });
// infinite sequence of pi estimates with increasing precision
var piSequence = randomPoints.Scan(
// n is the number of points used
// piQuarter is the current estimate for pi/4
new { n = 0, piQuarter = 0d }, (previous, point) => new {
n = previous.n + 1,
piQuarter =
(
previous.piQuarter * previous.n +
// if the new point falls into the circle, we add 1, otherwise we add 0
(Math.Sqrt(point.x * point.x + point.y * point.y) < 1 ? 1 : 0)
) / (previous.n + 1)
}).Select(t => t.piQuarter * 4);
Uses Endless methods Repeat() and Scan().
For other samples, visit this test file.