├── signing.snk
├── .nuget
├── NuGet.exe
├── NuGet.Config
└── NuGet.targets
├── docs
└── Sequence.Docs
│ ├── icons
│ └── Help.png
│ ├── Media
│ └── pascals-triangle.png
│ ├── Content
│ ├── VersionHistory
│ │ ├── v1.0.0.0.aml
│ │ ├── VersionHistory.aml
│ │ └── v1.0.1.0.aml
│ ├── Welcome.aml
│ └── Examples.aml
│ ├── ContentLayout.content
│ └── Sequence.Docs.shfbproj
├── tests
├── Sequences.Tests.Functional
│ ├── packages.config
│ ├── Extensions
│ │ └── TupleEx.cs
│ ├── TriangularNumbers.cs
│ ├── LuckyNumbers.cs
│ ├── Fibonacci.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Factorials.cs
│ ├── PrimeNumbers.cs
│ ├── PascalsTriangle.cs
│ └── Sequences.Tests.Functional.csproj
└── Sequences.Tests.Unit
│ ├── packages.config
│ ├── SplitAtTests.cs
│ ├── SpanTests.cs
│ ├── PartitionTests.cs
│ ├── StringTests.cs
│ ├── EmptySequenceTests.cs
│ ├── InitTests.cs
│ ├── PadToTests.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── SlicingTests.cs
│ ├── GroupedTests.cs
│ ├── PatchTests.cs
│ ├── PermutationsTests.cs
│ ├── FlattenTests.cs
│ ├── CombinationTests.cs
│ ├── LengthTests.cs
│ ├── TailTests.cs
│ ├── CopyToTests.cs
│ ├── ZipTests.cs
│ ├── SlidingTests.cs
│ ├── AddRemoveTests.cs
│ ├── KmpSearchTests.cs
│ ├── AggregationTests.cs
│ ├── IndexTests.cs
│ ├── FactoryMethodsTests.cs
│ ├── SequenceBuilderTests.cs
│ ├── Sequences.Tests.Unit.csproj
│ ├── EnumerableTests.cs
│ └── ExtensionMethodsTests.cs
├── src
└── Sequences
│ ├── EnumeratorEx.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── EmptySequence`1.cs
│ ├── KmpSearchAlgorithm.cs
│ ├── Sequences.csproj
│ ├── SequenceBuilder.cs
│ ├── Sequence`1.Iterators.cs
│ └── Sequence.cs
├── dist
└── Sequences.1.0.0.nuspec
├── .gitignore
├── Sequences.sln
└── README.md
/signing.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dcastro/Sequences/HEAD/signing.snk
--------------------------------------------------------------------------------
/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dcastro/Sequences/HEAD/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/docs/Sequence.Docs/icons/Help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dcastro/Sequences/HEAD/docs/Sequence.Docs/icons/Help.png
--------------------------------------------------------------------------------
/docs/Sequence.Docs/Media/pascals-triangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dcastro/Sequences/HEAD/docs/Sequence.Docs/Media/pascals-triangle.png
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Functional/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Functional/Extensions/TupleEx.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Sequences.Tests.Functional.Extensions
8 | {
9 | public static class TupleEx
10 | {
11 | public static int Sum(this Tuple tuple)
12 | {
13 | return tuple.Item1 + tuple.Item2;
14 | }
15 |
16 | public static int Product(this Tuple tuple)
17 | {
18 | return tuple.Item1 * tuple.Item2;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/SplitAtTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class SplitAtTests
11 | {
12 | [Fact]
13 | public void SplitAt_SplitsAtSelectedIndex()
14 | {
15 | var sequence = Sequence.Range(1, 6);
16 | var parts = sequence.SplitAt(3);
17 |
18 | int[] expectedFirst = {1, 2, 3};
19 | int[] expectedSecond = {4, 5};
20 |
21 | Assert.Equal(expectedFirst, parts.Item1);
22 | Assert.Equal(expectedSecond, parts.Item2);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/SpanTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class SpanTests
11 | {
12 | [Fact]
13 | public void Span_SplitsIntoPrefixAndSuffix()
14 | {
15 | var sequence = Sequence.Range(1, 11);
16 | var parts = sequence.Span(e => e <= 5);
17 |
18 | int[] expectedFirst = {1, 2, 3, 4, 5};
19 | int[] expectedSecond = {6, 7, 8, 9, 10};
20 |
21 | Assert.Equal(expectedFirst, parts.Item1);
22 | Assert.Equal(expectedSecond, parts.Item2);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/PartitionTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class PartitionTests
11 | {
12 | [Fact]
13 | public void Partition_SelectsElementsUsingAPredicate()
14 | {
15 | var sequence = Sequence.From(1);
16 | var parts = sequence.Partition(e => e%2 == 0);
17 |
18 | int[] expectedFirst = {2, 4, 6, 8, 10};
19 | int[] expectedSecond = {1, 3, 5, 7, 9};
20 |
21 | Assert.Equal(expectedFirst, parts.Item1.Take(5));
22 | Assert.Equal(expectedSecond, parts.Item2.Take(5));
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/docs/Sequence.Docs/Content/VersionHistory/v1.0.0.0.aml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Version 1.0.0.0 was released on 12/05/2014.
6 |
7 |
8 |
9 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/StringTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Xunit;
8 |
9 | namespace Sequences.Tests
10 | {
11 | public class StringTests
12 | {
13 | [Fact]
14 | public void ToStringTest()
15 | {
16 | var seq = Sequence.Range(1, 4);
17 | Assert.Equal("Sequence(1, ?)", seq.ToString());
18 | }
19 |
20 | [Fact]
21 | public void MkString_JoinsElements()
22 | {
23 | var seq = Sequence.Range(1, 4);
24 | Assert.Equal("123", seq.MkString());
25 | }
26 |
27 | [Fact]
28 | public void MkString_JoinsElements_WithSeparator()
29 | {
30 | var seq = Sequence.Range(1, 4);
31 | Assert.Equal("1-2-3", seq.MkString("-"));
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Sequences/EnumeratorEx.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace Sequences
8 | {
9 | internal static class EnumeratorEx
10 | {
11 | ///
12 | /// Call MoveNext, and dispose of the iterator if MoveNext returns false or throws an exception.
13 | ///
14 | public static bool TryMoveNext(this IEnumerator iterator)
15 | {
16 | try
17 | {
18 | bool moved = iterator.MoveNext();
19 |
20 | if (!moved)
21 | iterator.Dispose();
22 |
23 | return moved;
24 | }
25 | catch (Exception)
26 | {
27 | if (iterator != null)
28 | iterator.Dispose();
29 |
30 | throw;
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/docs/Sequence.Docs/ContentLayout.content:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Functional/TriangularNumbers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests.Functional
9 | {
10 | public class TriangularNumbers
11 | {
12 | private readonly ISequence _expectedNumbers = Sequence.With(0, 1, 3, 6, 10, 15, 21, 28, 36, 45);
13 |
14 | [Fact]
15 | public void V1()
16 | {
17 | var numbers = Sequence.From(0)
18 | .Select(n => n*(n + 1)/2);
19 |
20 | Assert.Equal(_expectedNumbers, numbers.Take(10));
21 | }
22 |
23 | [Fact]
24 | public void V2()
25 | {
26 | var numbers = Triangular(0, 0);
27 |
28 | Assert.Equal(_expectedNumbers, numbers.Take(10));
29 | }
30 |
31 | private ISequence Triangular(int triangularNumber, int index)
32 | {
33 | return new Sequence(triangularNumber, () => Triangular(triangularNumber + index + 1, index + 1));
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/EmptySequenceTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class EmptySequenceTests
11 | {
12 | [Fact]
13 | public void Sequence_HasNoTail()
14 | {
15 | Assert.Throws(() => Sequence.Empty().Tail);
16 | }
17 |
18 | [Fact]
19 | public void Sequence_HasNoHead()
20 | {
21 | Assert.Throws(() => Sequence.Empty().Head);
22 | }
23 |
24 | [Fact]
25 | public void Sequence_IsEmpty()
26 | {
27 | Assert.True(Sequence.Empty().IsEmpty);
28 | }
29 |
30 | [Fact]
31 | public void Sequence_HasNoInit()
32 | {
33 | Assert.Throws(() => Sequence.Empty().Init);
34 | }
35 |
36 | [Fact]
37 | public void ToStringTest()
38 | {
39 | Assert.Equal("Sequence()", Sequence.Empty().ToString());
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/InitTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class InitTests
11 | {
12 | [Fact]
13 | public void Init_ExcludesLastElement()
14 | {
15 | var sequence = Sequence.Range(1, 6);
16 | var init = sequence.Init;
17 | int[] expectedInit = {1, 2, 3, 4};
18 |
19 | Assert.Equal(expectedInit, init);
20 | }
21 |
22 | [Fact]
23 | public void Inits_IteratesOverInits()
24 | {
25 | var sequence = Sequence.Range(1, 4);
26 | var inits = sequence.Inits().ToList();
27 | var expectedInits = new[]
28 | {
29 | new[] {1, 2, 3},
30 | new[] {1, 2},
31 | new[] {1},
32 | new int[] {}
33 | };
34 |
35 | Assert.Equal(expectedInits.Length, inits.Count);
36 |
37 | for (int i = 0; i < inits.Count; i++)
38 | Assert.Equal(expectedInits[i], inits[i]);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Sequences/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Resources;
2 | using System.Reflection;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 |
6 | // General Information about an assembly is controlled through the following
7 | // set of attributes. Change these attribute values to modify the information
8 | // associated with an assembly.
9 | [assembly: AssemblyTitle("Sequences")]
10 | [assembly: AssemblyDescription("")]
11 | [assembly: AssemblyConfiguration("")]
12 | [assembly: AssemblyCompany("")]
13 | [assembly: AssemblyProduct("Sequences")]
14 | [assembly: AssemblyCopyright("Copyright © 2014")]
15 | [assembly: AssemblyTrademark("")]
16 | [assembly: AssemblyCulture("")]
17 | [assembly: NeutralResourcesLanguage("en")]
18 |
19 | // Version information for an assembly consists of the following four values:
20 | //
21 | // Major Version
22 | // Minor Version
23 | // Build Number
24 | // Revision
25 | //
26 | // You can specify all the values or you can default the Build and Revision Numbers
27 | // by using the '*' as shown below:
28 | // [assembly: AssemblyVersion("1.0.*")]
29 | [assembly: AssemblyVersion("1.0.1")]
30 | [assembly: AssemblyFileVersion("1.0.1")]
31 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Functional/LuckyNumbers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests.Functional
9 | {
10 | public class LuckyNumbers
11 | {
12 | private readonly ISequence _expectedLuckyNumbers = Sequence.With(
13 | 1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63, 67, 69, 73, 75, 79);
14 |
15 | [Fact]
16 | public void V1()
17 | {
18 | var luckies = Lucky();
19 |
20 | Assert.Equal(_expectedLuckyNumbers, luckies.Take(20));
21 | }
22 |
23 | private ISequence Lucky()
24 | {
25 | var oddInts = Sequence.Iterate(1, e => e + 2);
26 | return new Sequence(1, () => Lucky(oddInts, 1));
27 | }
28 |
29 | private ISequence Lucky(ISequence seq, int index)
30 | {
31 | //select the next element
32 | var n = seq[index];
33 |
34 | //remove every nth element from the sequence
35 | var filtered = seq.Sliding(n - 1, n).Flatten();
36 |
37 | return new Sequence(n, () => Lucky(filtered, index + 1));
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Sequences/EmptySequence`1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace Sequences
8 | {
9 | internal class EmptySequence : Sequence
10 | {
11 | private static readonly Lazy> Empty = new Lazy>(() => new EmptySequence());
12 |
13 | private EmptySequence() : base(default(T), null as Lazy>)
14 | {
15 | }
16 |
17 | public static ISequence Instance
18 | {
19 | get { return Empty.Value; }
20 | }
21 |
22 | public override bool IsEmpty
23 | {
24 | get { return true; }
25 | }
26 |
27 | public override T Head
28 | {
29 | get { throw new InvalidOperationException("An empty sequence doesn't have a head."); }
30 | }
31 |
32 | public override ISequence Tail
33 | {
34 | get { throw new InvalidOperationException("An empty sequence doesn't have a tail."); }
35 | }
36 |
37 | public override ISequence Init
38 | {
39 | get { throw new InvalidOperationException("Cannot call Init on an empty sequence."); }
40 | }
41 |
42 | public override string ToString()
43 | {
44 | return "Sequence()";
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/docs/Sequence.Docs/Content/VersionHistory/VersionHistory.aml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | The topics in this section describe the various changes made to Sequences over the
7 | life of the project.
8 |
9 |
10 |
11 |
12 | Version History
13 |
14 | Select a version below to see a description of its changes.
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Functional/Fibonacci.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Sequences.Tests.Functional.Extensions;
7 | using Xunit;
8 |
9 | namespace Sequences.Tests.Functional
10 | {
11 | public class Fibonacci
12 | {
13 | private readonly int[] _expectedFibs = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 };
14 |
15 | [Fact]
16 | public void Declarative()
17 | {
18 | ISequence fibs = null;
19 | fibs = Sequence.With(0, 1) //start with 0, 1...
20 | .Concat(() => //and then
21 | fibs.Zip(fibs.Tail) //zip the sequence with its tail (i.e., (0,1), (1,1), (1,2), (2,3), (3, 5))
22 | .Select(TupleEx.Sum)); //select the sum of each pair (i.e., 1, 2, 3, 5, 8)
23 |
24 | Assert.Equal(_expectedFibs, fibs.Take(10));
25 | }
26 |
27 | [Fact]
28 | public void Loop()
29 | {
30 | var fibs = Fibs(0, 1);
31 |
32 | Assert.Equal(_expectedFibs, fibs.Take(10));
33 | }
34 |
35 | //current and next are any two consecutive fibonacci numbers.
36 | //e.g., 0 and 1, or 5 and 8
37 | private ISequence Fibs(int current, int next)
38 | {
39 | return new Sequence(current, () => Fibs(next, current + next));
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/PadToTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class PadToTests
11 | {
12 | [Fact]
13 | public void PadTo_AppendsElements()
14 | {
15 | var sequence = Sequence.Range(1, 4);
16 | var padded = sequence.PadTo(5, 0);
17 | int[] expectedPadded = {1, 2, 3, 0, 0};
18 |
19 | Assert.Equal(expectedPadded, padded);
20 | }
21 |
22 | [Fact]
23 | public void PadTo_DoesNothing_When_LengthIsLessThanCount()
24 | {
25 | var sequence = Sequence.Range(1, 4);
26 | var padded = sequence.PadTo(2, 0);
27 | int[] expectedPadded = {1, 2, 3};
28 |
29 | Assert.Equal(expectedPadded, padded);
30 | }
31 |
32 | [Fact]
33 | public void PadTo_DoesNothing_When_LengthIsEqualToCount()
34 | {
35 | var sequence = Sequence.Range(1, 4);
36 | var padded = sequence.PadTo(3, 0);
37 | int[] expectedPadded = {1, 2, 3};
38 |
39 | Assert.Equal(expectedPadded, padded);
40 | }
41 |
42 | [Fact]
43 | public void PadTo_Returns_SameSequence_When_LengthIsNegative()
44 | {
45 | var sequence = Sequence.Range(1, 4);
46 | var padded = sequence.PadTo(-1, 0);
47 |
48 | Assert.Same(sequence, padded);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/docs/Sequence.Docs/Content/VersionHistory/v1.0.1.0.aml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Version 1.0.1.0 was released on 27/05/2014.
7 |
8 |
9 |
10 |
11 |
12 | Changes in This Release
13 |
14 |
15 |
16 |
17 |
18 | Improved iterators to allow a more efficient garbage collection
19 |
20 |
21 |
22 |
23 |
24 | Added
25 |
26 | M:Sequences.ISequence`1.IndexOfSlice(System.Collections.Generic.IEnumerable{`0})
27 |
28 | and
29 |
30 | M:Sequences.ISequence`1.ContainsSlice(System.Collections.Generic.IEnumerable{`0})
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Sequences.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Sequences.Tests")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("ce69b025-4389-4000-bb99-4518e674ef92")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Functional/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Sequences.Tests.Functional")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Sequences.Tests.Functional")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("11e24634-c4a7-49ae-b737-b8c63f954a66")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/dist/Sequences.1.0.0.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sequences
5 | 1.0.1
6 | Sequences
7 | DCastro
8 | DCastro
9 | https://github.com/dcastro/Sequences
10 | false
11 | Sequences is a port of Scala's Stream[+A] to C#.
12 |
13 | A Sequence<T> is an immutable lazy list whose elements are only evaluated when they are needed. A sequence is composed by a head (the first element) and a lazily-evaluated tail (the remaining elements).
14 |
15 | The fact that the tail is lazily-evaluated, makes it easy to represent infinite series or sets.
16 |
17 | See the project's page for examples: https://github.com/dcastro/Sequences
18 | Sequences is a port of Scala's Streams to C#. It features lazy evaluation, immutability and memoization.
19 | https://github.com/dcastro/Sequences/releases
20 | © 2014 Diogo Castro
21 | enumerable sequence collection immutable data-structures scala lazy stream
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Functional/Factorials.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Sequences.Tests.Functional.Extensions;
7 | using Xunit;
8 |
9 | namespace Sequences.Tests.Functional
10 | {
11 | public class Factorials
12 | {
13 | private readonly ISequence _expectedFactorials =
14 | Sequence.With(1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880);
15 |
16 | [Fact]
17 | public void V1()
18 | {
19 | var naturals = Sequence.From(1);
20 |
21 | ISequence factorials = null;
22 | factorials = new Sequence(1, //start with (1) at index 0
23 | () => factorials.Zip(naturals) //zip factorials (1, ?) with naturals (1,2,3, ...)
24 | .Select(TupleEx.Product)); //select the product of each tuple (1,1) => 1, (1,2) => 2, (2,3) => 6
25 | /**
26 | * factorials[0] returns 1, eagerly evaluated.
27 | * Then, we zip factorials (1, ?) with naturals (1,2,3,...), and select the product of each tuple
28 | *
29 | * factorials[1] returns (1,?) zip (1,2,3,...) select(item1 * item2)[0] = factorials[0] * naturals[0] = 1 * 1 = 1
30 | * factorials[2] returns (1,1,?) zip (1,2,3,...) select(item1 * item2)[1] = factorials[1] * naturals[1] = 1 * 2 = 2
31 | * factorials[3] returns (1,1,2,?) zip (1,2,3,...) select(item1 * item2)[2] = factorials[2] * naturals[2] = 2 * 3 = 6
32 | */
33 |
34 | Assert.Equal(_expectedFactorials, factorials.Take(10));
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/SlicingTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class SlicingTests
11 | {
12 | [Fact]
13 | public void Slice_ReturnsSubset()
14 | {
15 | var sequence = Sequence.From(0);
16 | var slice = sequence.Slice(4, 8);
17 | int[] expectedSlice = {4, 5, 6, 7};
18 |
19 | Assert.Equal(expectedSlice, slice);
20 | }
21 |
22 | [Fact]
23 | public void Slice_StartsAtZero_When_FromIsNegative()
24 | {
25 | var sequence = Sequence.From(0);
26 | var slice = sequence.Slice(-5, 5);
27 | int[] expectedSlice = {0, 1, 2, 3, 4};
28 |
29 | Assert.Equal(expectedSlice, slice);
30 | }
31 |
32 | [Fact]
33 | public void Slice_ReturnsEmptySequence_When_SequenceIsEmpty()
34 | {
35 | var sequence = Sequence.Empty();
36 | var slice = sequence.Slice(0, 5);
37 | int[] expectedSlice = {};
38 |
39 | Assert.Equal(expectedSlice, slice);
40 | }
41 |
42 | [Fact]
43 | public void Slice_ReturnsEmptySequence_When_UntilIsLowerThanFrom()
44 | {
45 | var sequence = Sequence.From(0);
46 | var slice = sequence.Slice(5, 0);
47 | int[] expectedSlice = {};
48 |
49 | Assert.Equal(expectedSlice, slice);
50 | }
51 |
52 | [Fact]
53 | public void Slice_ReturnsShorterSlice_When_UntilIsHigherThanCount()
54 | {
55 | var sequence = Sequence.Range(0, 3, 1);
56 | var slice = sequence.Slice(1, 5);
57 | int[] expectedSlice = {1, 2};
58 |
59 | Assert.Equal(expectedSlice, slice);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/GroupedTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class GroupedTests
11 | {
12 | [Fact]
13 | public void Grouped_GroupsElements()
14 | {
15 | var sequence = Sequence.Range(0, 6);
16 | var groups = sequence.Grouped(3).ToList();
17 | var expectedGroups = new[]
18 | {
19 | new[] {0, 1, 2},
20 | new[] {3, 4, 5}
21 | };
22 |
23 | Assert.Equal(expectedGroups.Length, groups.Count);
24 |
25 | for (int i = 0; i < groups.Count; i++)
26 | Assert.Equal(expectedGroups[i], groups[i]);
27 | }
28 |
29 | [Fact]
30 | public void Grouped_TruncatesLastGroup()
31 | {
32 |
33 | var sequence = Sequence.Range(0, 5);
34 | var groups = sequence.Grouped(3).ToList();
35 | var expectedGroups = new[]
36 | {
37 | new[] {0, 1, 2},
38 | new[] {3, 4}
39 | };
40 |
41 | Assert.Equal(expectedGroups.Length, groups.Count);
42 |
43 | for (int i = 0; i < groups.Count; i++)
44 | Assert.Equal(expectedGroups[i], groups[i]);
45 | }
46 |
47 | [Fact]
48 | public void Grouped_Returns_EmptyEnumerable_When_SequenceIsEmpty()
49 | {
50 | var sequence = Sequence.Empty();
51 | var groups = sequence.Grouped(10);
52 |
53 | Assert.False(groups.Any());
54 | }
55 |
56 | [Fact]
57 | public void Grouped_ThrowsException_When_SizeIsNotPositive()
58 | {
59 | Assert.Throws(() => Sequence.From(0).Grouped(0));
60 | Assert.Throws(() => Sequence.From(0).Grouped(-1));
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/PatchTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class PatchTests
11 | {
12 | [Fact]
13 | public void Patch_ReplacesWithPatch()
14 | {
15 | var sequence = Sequence.Range(0, 6);
16 | var patched = sequence.Patch(2, new[] {9, 9}, 2);
17 | int[] expectedPatched = {0, 1, 9, 9, 4, 5};
18 |
19 | Assert.Equal(expectedPatched, patched);
20 | }
21 |
22 | [Fact]
23 | public void Patch_ErasesOriginalElements()
24 | {
25 | var sequence = Sequence.Range(0, 6);
26 | var patched = sequence.Patch(2, new int[] {}, 2);
27 | int[] expectedPatched = {0, 1, 4, 5};
28 |
29 | Assert.Equal(expectedPatched, patched);
30 | }
31 |
32 | [Fact]
33 | public void Patch_ConcatenatesPatch_When_FromIsEqualToCount()
34 | {
35 | var sequence = Sequence.Range(0, 4);
36 | var patched = sequence.Patch(4, new[] {9, 9}, 2);
37 | int[] expectedPatched = {0, 1, 2, 3, 9, 9};
38 |
39 | Assert.Equal(expectedPatched, patched);
40 | }
41 |
42 | [Fact]
43 | public void Patch_ConcatenatesPatch_When_FromIsGreaterThanCount()
44 | {
45 | var sequence = Sequence.Range(0, 4);
46 | var patched = sequence.Patch(10, new[] {9, 9}, 2);
47 | int[] expectedPatched = {0, 1, 2, 3, 9, 9};
48 |
49 | Assert.Equal(expectedPatched, patched);
50 | }
51 |
52 | [Fact]
53 | public void Patch_PrependsPatch_When_FromIsNegative()
54 | {
55 | var sequence = Sequence.Range(0, 4);
56 | var patched = sequence.Patch(-1, new[] {9, 9}, 2);
57 | int[] expectedPatched = {9, 9, 2, 3};
58 |
59 | Assert.Equal(expectedPatched, patched);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/PermutationsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class PermutationsTests
11 | {
12 | [Fact]
13 | public void Permutations_RearrangesElements()
14 | {
15 | var sequence = Sequence.Range(1, 4);
16 | var permutations = sequence.Permutations().ToList();
17 |
18 | var expectedPermutations = new[]
19 | {
20 | new[] {1, 2, 3},
21 | new[] {1, 3, 2},
22 | new[] {2, 1, 3},
23 | new[] {2, 3, 1},
24 | new[] {3, 1, 2},
25 | new[] {3, 2, 1}
26 | };
27 |
28 | Assert.Equal(expectedPermutations.Length, permutations.Count);
29 |
30 | for (int i = 0; i < permutations.Count; i++)
31 | Assert.Equal(expectedPermutations[i], permutations[i]);
32 | }
33 |
34 | [Fact]
35 | public void Permutations_Excludes_Duplicates()
36 | {
37 | var permutations = "abb".AsSequence().Permutations().ToList();
38 |
39 | var expectedPermutations = new[]
40 | {
41 | new[] {'a', 'b', 'b'},
42 | new[] {'b', 'a', 'b'},
43 | new[] {'b', 'b', 'a'}
44 | };
45 |
46 | Assert.Equal(expectedPermutations.Length, permutations.Count);
47 |
48 | for (int i = 0; i < permutations.Count; i++)
49 | Assert.Equal(expectedPermutations[i], permutations[i]);
50 | }
51 |
52 | [Fact]
53 | public void Permutations_Returns_OneEmptySequence_When_SequenceIsEmpty()
54 | {
55 | var sequence = Sequence.Empty();
56 | var permutations = sequence.Permutations().ToList();
57 |
58 | Assert.Equal(1, permutations.Count);
59 | Assert.Empty(permutations.First());
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/FlattenTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class FlattenTests
11 | {
12 | [Fact]
13 | public void Flatten_SequenceOfEnumerables_ConcatenatesEnumerables()
14 | {
15 | ISequence> sequence =
16 | Sequence.With>(
17 | new[] {1, 2, 3},
18 | new int[] {},
19 | new[] {4, 5},
20 | new[] {6, 7, 8},
21 | new int[] {}
22 | );
23 |
24 | var flattened = sequence.Flatten();
25 | int[] expectedFlattened = {1, 2, 3, 4, 5, 6, 7, 8};
26 |
27 | Assert.Equal(expectedFlattened, flattened);
28 | }
29 |
30 | [Fact]
31 | public void Flatten_EnumerableOfSequences_ConcatenatesSequences()
32 | {
33 | IEnumerable> sequence =
34 | new[]
35 | {
36 | Sequence.With(1, 2, 3),
37 | Sequence.Empty(),
38 | Sequence.With(4, 5),
39 | Sequence.With(6, 7, 8),
40 | Sequence.Empty()
41 | };
42 |
43 | var flattened = sequence.Flatten();
44 | int[] expectedFlattened = {1, 2, 3, 4, 5, 6, 7, 8};
45 |
46 | Assert.Equal(expectedFlattened, flattened);
47 | }
48 |
49 | [Fact]
50 | public void Flatten_SequenceOfSequences_ConcatenatesSequences()
51 | {
52 | ISequence> sequence =
53 | Sequence.With(
54 | Sequence.With(1, 2, 3),
55 | Sequence.Empty(),
56 | Sequence.With(4, 5),
57 | Sequence.With(6, 7, 8),
58 | Sequence.Empty()
59 | );
60 |
61 | var flattened = sequence.Flatten();
62 | int[] expectedFlattened = {1, 2, 3, 4, 5, 6, 7, 8};
63 |
64 | Assert.Equal(expectedFlattened, flattened);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/CombinationTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class CombinationTests
11 | {
12 | [Fact]
13 | public void Combinations_CombinesElements()
14 | {
15 | var sequence = Sequence.Range(1, 6);
16 | var combs = sequence.Combinations(3).ToList();
17 |
18 | var expectedCombs = new[]
19 | {
20 | new[] {1, 2, 3},
21 | new[] {1, 2, 4},
22 | new[] {1, 2, 5},
23 | new[] {1, 3, 4},
24 | new[] {1, 3, 5},
25 | new[] {1, 4, 5},
26 | new[] {2, 3, 4},
27 | new[] {2, 3, 5},
28 | new[] {2, 4, 5},
29 | new[] {3, 4, 5}
30 | };
31 |
32 | Assert.Equal(expectedCombs.Length, combs.Count);
33 |
34 | for (int i = 0; i < combs.Count; i++)
35 | Assert.Equal(expectedCombs[i], combs[i]);
36 | }
37 |
38 | [Fact]
39 | public void Combinations_Excludes_Duplicates()
40 | {
41 | var combs = "abbbc".AsSequence().Combinations(2).ToList();
42 |
43 | var expectedCombs = new[]
44 | {
45 | new[] {'a', 'b'},
46 | new[] {'a', 'c'},
47 | new[] {'b', 'b'},
48 | new[] {'b', 'c'}
49 | };
50 |
51 | Assert.Equal(expectedCombs.Length, combs.Count);
52 |
53 | for (int i = 0; i < combs.Count; i++)
54 | Assert.Equal(expectedCombs[i], combs[i]);
55 | }
56 |
57 | [Fact]
58 | public void Combinations_Returns_NoSequences_When_SequenceIsEmpty()
59 | {
60 | var sequence = Sequence.Empty();
61 | var combs = sequence.Combinations(2);
62 |
63 | Assert.Empty(combs);
64 | }
65 |
66 | [Fact]
67 | public void Combinations_Returns_OneEmptySequence_When_SizeIsZero()
68 | {
69 | var sequence = Sequence.Range(1, 6);
70 | var combs = sequence.Combinations(0).ToList();
71 |
72 | Assert.Equal(1, combs.Count);
73 | Assert.Empty(combs.First());
74 | }
75 |
76 | [Fact]
77 | public void Combinations_ThrowsException_When_SizeIsNegative()
78 | {
79 | var sequence = Sequence.Range(1, 6);
80 | Assert.Throws(() => sequence.Combinations(-1));
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/LengthTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 | using Xunit.Extensions;
8 |
9 | namespace Sequences.Tests
10 | {
11 | public class LengthTests
12 | {
13 | [Fact]
14 | public void Count_Returns_SequenceLength()
15 | {
16 | var sequence = Sequence.With(1, 2, 3);
17 | Assert.Equal(3, sequence.Count);
18 | }
19 |
20 | [Fact]
21 | public void HasDefiniteSize_ReturnsFalse_When_SequenceHasNotBeenFullyEvaluated()
22 | {
23 | var sequence = Sequence.Range(0, 6, 1);
24 |
25 | sequence.Take(5).Force();
26 |
27 | Assert.False(sequence.HasDefiniteSize);
28 | }
29 |
30 | [Fact]
31 | public void HasDefiniteSize_ReturnsTrue_When_SequenceHasBeenEvaluated()
32 | {
33 | var sequence = Sequence.Range(0, 6, 1);
34 |
35 | sequence.Force();
36 |
37 | Assert.True(sequence.HasDefiniteSize);
38 | }
39 |
40 | [Fact]
41 | public void HasDefiniteSize_DoesntEvaluateTail()
42 | {
43 | var sequence = Sequence.Range(0, 6, 1);
44 | bool hasDefiniteSize = sequence.HasDefiniteSize;
45 |
46 | Assert.False(sequence.IsTailDefined);
47 | }
48 |
49 | [Theory]
50 | [InlineData(-1, 1)]
51 | [InlineData(0, 1)]
52 | [InlineData(1, 1)]
53 | [InlineData(2, 1)]
54 | [InlineData(3, 0)]
55 | [InlineData(4, -1)]
56 | public void LengthCompare_ReturnsExpectedResult_When_CountEqualsThree(int length, int expectedResult)
57 | {
58 | var sequence = Sequence.Range(1, 4);
59 | Assert.Equal(expectedResult, sequence.LengthCompare(length));
60 | }
61 |
62 | [Theory]
63 | [InlineData(-1, 1)]
64 | [InlineData(0, 1)]
65 | [InlineData(1, 1)]
66 | [InlineData(2, 1)]
67 | public void LengthCompare_ReturnsExpectedResult_When_SequenceIsInfinite(int length, int expectedResult)
68 | {
69 | var sequence = Sequence.From(1);
70 | Assert.Equal(expectedResult, sequence.LengthCompare(length));
71 | }
72 |
73 | [Theory]
74 | [InlineData(-1, 1)]
75 | [InlineData(0, 0)]
76 | [InlineData(1, -1)]
77 | public void LengthCompare_ReturnsExpectedResult_When_SequenceIsEmpty(int length, int expectedResult)
78 | {
79 | var sequence = Sequence.Empty();
80 | Assert.Equal(expectedResult, sequence.LengthCompare(length));
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/TailTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Moq;
7 | using Xunit;
8 |
9 | namespace Sequences.Tests
10 | {
11 | public class TailTests
12 | {
13 | [Fact]
14 | public void Tail_Is_LazilyEvaluated()
15 | {
16 | //Arrange
17 | var tailMock = new Mock>>();
18 | tailMock.Setup(tail => tail()).Returns(Sequence.Empty());
19 |
20 | var sequence = new Sequence(1, tailMock.Object);
21 |
22 | //Act & Assert
23 | tailMock.Verify(tail => tail(), Times.Never);
24 |
25 | Assert.Equal(Sequence.Empty(), sequence.Tail);
26 | tailMock.Verify(tail => tail(), Times.Once);
27 | }
28 |
29 | [Fact]
30 | public void IsTailDefined_ReturnsFalse_When_TailHasntBeenEvaluated()
31 | {
32 | var sequence = new Sequence(1, Sequence.Empty);
33 | Assert.False(sequence.IsTailDefined);
34 | }
35 |
36 | [Fact]
37 | public void IsTailDefined_ReturnsTrue_When_TailHasBeenEvaluated()
38 | {
39 | var sequence = new Sequence(1, Sequence.Empty);
40 | var tail = sequence.Tail;
41 | Assert.True(sequence.IsTailDefined);
42 | }
43 |
44 | [Fact]
45 | public void Force_EvaluatesTail()
46 | {
47 | var sequence = Sequence.Range(0, 6, 1);
48 | Assert.False(sequence.HasDefiniteSize);
49 |
50 | sequence.Force();
51 | Assert.True(sequence.HasDefiniteSize);
52 | }
53 |
54 | [Fact]
55 | public void Tails_IteratesOverTails()
56 | {
57 | var sequence = Sequence.Range(1, 4);
58 | var tails = sequence.Tails().ToList();
59 | var expectedTails = new[]
60 | {
61 | new[] {1, 2, 3},
62 | new[] {2, 3},
63 | new[] {3},
64 | new int[] {}
65 | };
66 |
67 | Assert.Equal(expectedTails.Length, tails.Count);
68 |
69 | for (int i = 0; i < tails.Count; i++)
70 | Assert.Equal(expectedTails[i], tails[i]);
71 | }
72 |
73 | [Fact]
74 | public void NonEmptyTails_Excludes_EmptySequence()
75 | {
76 | var sequence = Sequence.Range(1, 4);
77 | var tails = sequence.NonEmptyTails().ToList();
78 | var expectedTails = new[]
79 | {
80 | new[] {1, 2, 3},
81 | new[] {2, 3},
82 | new[] {3}
83 | };
84 |
85 | Assert.Equal(expectedTails.Length, tails.Count);
86 |
87 | for (int i = 0; i < tails.Count; i++)
88 | Assert.Equal(expectedTails[i], tails[i]);
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 |
11 | [Dd]ebug/
12 | [Rr]elease/
13 | x64/
14 | build/
15 | [Bb]in/
16 | [Oo]bj/
17 |
18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
19 | !packages/*/build/
20 |
21 | # MSTest test Results
22 | [Tt]est[Rr]esult*/
23 | [Bb]uild[Ll]og.*
24 |
25 | *_i.c
26 | *_p.c
27 | *.ilk
28 | *.meta
29 | *.obj
30 | *.pch
31 | *.pdb
32 | *.pgc
33 | *.pgd
34 | *.rsp
35 | *.sbr
36 | *.tlb
37 | *.tli
38 | *.tlh
39 | *.tmp
40 | *.tmp_proj
41 | *.log
42 | *.vspscc
43 | *.vssscc
44 | .builds
45 | *.pidb
46 | *.log
47 | *.scc
48 |
49 | # Visual C++ cache files
50 | ipch/
51 | *.aps
52 | *.ncb
53 | *.opensdf
54 | *.sdf
55 | *.cachefile
56 |
57 | # Visual Studio profiler
58 | *.psess
59 | *.vsp
60 | *.vspx
61 |
62 | # Guidance Automation Toolkit
63 | *.gpState
64 |
65 | # ReSharper is a .NET coding add-in
66 | _ReSharper*/
67 | *.[Rr]e[Ss]harper
68 |
69 | # TeamCity is a build add-in
70 | _TeamCity*
71 |
72 | # DotCover is a Code Coverage Tool
73 | *.dotCover
74 |
75 | # NCrunch
76 | *.ncrunch*
77 | .*crunch*.local.xml
78 |
79 | # Installshield output folder
80 | [Ee]xpress/
81 |
82 | # DocProject is a documentation generator add-in
83 | DocProject/buildhelp/
84 | DocProject/Help/*.HxT
85 | DocProject/Help/*.HxC
86 | DocProject/Help/*.hhc
87 | DocProject/Help/*.hhk
88 | DocProject/Help/*.hhp
89 | DocProject/Help/Html2
90 | DocProject/Help/html
91 |
92 | # Click-Once directory
93 | publish/
94 |
95 | # Publish Web Output
96 | *.Publish.xml
97 | *.pubxml
98 |
99 | # NuGet Packages Directory
100 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
101 | packages/
102 | lib/
103 | !NuGet.exe
104 |
105 | # Windows Azure Build Output
106 | csx
107 | *.build.csdef
108 |
109 | # Windows Store app package directory
110 | AppPackages/
111 |
112 | # Others
113 | sql/
114 | *.Cache
115 | ClientBin/
116 | [Ss]tyle[Cc]op.*
117 | ~$*
118 | *~
119 | *.dbmdl
120 | *.[Pp]ublish.xml
121 | *.pfx
122 | *.publishsettings
123 |
124 | # RIA/Silverlight projects
125 | Generated_Code/
126 |
127 | # Backup & report files from converting an old project file to a newer
128 | # Visual Studio version. Backup files are not needed, because we have git ;-)
129 | _UpgradeReport_Files/
130 | Backup*/
131 | UpgradeLog*.XML
132 | UpgradeLog*.htm
133 |
134 | # SQL Server files
135 | App_Data/*.mdf
136 | App_Data/*.ldf
137 |
138 | # =========================
139 | # Windows detritus
140 | # =========================
141 |
142 | # Windows image file caches
143 | Thumbs.db
144 | ehthumbs.db
145 |
146 | # Folder config file
147 | Desktop.ini
148 |
149 | # Recycle Bin used on file shares
150 | $RECYCLE.BIN/
151 |
152 | # Mac crap
153 | .DS_Store
154 | *.nupkg
155 |
156 | #SandCastle
157 | docs/help/*
158 |
--------------------------------------------------------------------------------
/Sequences.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sequences.Tests.Unit", "tests\Sequences.Tests.Unit\Sequences.Tests.Unit.csproj", "{97B797FB-B2D8-414B-9459-71EB27113D60}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sequences", "src\Sequences\Sequences.csproj", "{6C9F9E66-E026-4966-AEB2-9B29B5F0DF85}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{C5490B70-626D-48B8-BE58-0F395820FF35}"
9 | ProjectSection(SolutionItems) = preProject
10 | .nuget\NuGet.Config = .nuget\NuGet.Config
11 | .nuget\NuGet.exe = .nuget\NuGet.exe
12 | .nuget\NuGet.targets = .nuget\NuGet.targets
13 | EndProjectSection
14 | EndProject
15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sequences.Tests.Functional", "tests\Sequences.Tests.Functional\Sequences.Tests.Functional.csproj", "{C0460AD0-BE50-4A91-BBDF-BDBA5F2634C6}"
16 | EndProject
17 | Project("{7CF6DF6D-3B04-46F8-A40B-537D21BCA0B4}") = "Sequence.Docs", "docs\Sequence.Docs\Sequence.Docs.shfbproj", "{7807882D-4578-40A2-9E24-970E1E992EC5}"
18 | EndProject
19 | Global
20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
21 | Debug|Any CPU = Debug|Any CPU
22 | Docs|Any CPU = Docs|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {97B797FB-B2D8-414B-9459-71EB27113D60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {97B797FB-B2D8-414B-9459-71EB27113D60}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {97B797FB-B2D8-414B-9459-71EB27113D60}.Docs|Any CPU.ActiveCfg = Docs|Any CPU
29 | {97B797FB-B2D8-414B-9459-71EB27113D60}.Docs|Any CPU.Build.0 = Docs|Any CPU
30 | {97B797FB-B2D8-414B-9459-71EB27113D60}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {97B797FB-B2D8-414B-9459-71EB27113D60}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {6C9F9E66-E026-4966-AEB2-9B29B5F0DF85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {6C9F9E66-E026-4966-AEB2-9B29B5F0DF85}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {6C9F9E66-E026-4966-AEB2-9B29B5F0DF85}.Docs|Any CPU.ActiveCfg = Docs|Any CPU
35 | {6C9F9E66-E026-4966-AEB2-9B29B5F0DF85}.Docs|Any CPU.Build.0 = Docs|Any CPU
36 | {6C9F9E66-E026-4966-AEB2-9B29B5F0DF85}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {6C9F9E66-E026-4966-AEB2-9B29B5F0DF85}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {C0460AD0-BE50-4A91-BBDF-BDBA5F2634C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {C0460AD0-BE50-4A91-BBDF-BDBA5F2634C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {C0460AD0-BE50-4A91-BBDF-BDBA5F2634C6}.Docs|Any CPU.ActiveCfg = Docs|Any CPU
41 | {C0460AD0-BE50-4A91-BBDF-BDBA5F2634C6}.Docs|Any CPU.Build.0 = Docs|Any CPU
42 | {C0460AD0-BE50-4A91-BBDF-BDBA5F2634C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {C0460AD0-BE50-4A91-BBDF-BDBA5F2634C6}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {7807882D-4578-40A2-9E24-970E1E992EC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {7807882D-4578-40A2-9E24-970E1E992EC5}.Docs|Any CPU.ActiveCfg = Docs|Any CPU
46 | {7807882D-4578-40A2-9E24-970E1E992EC5}.Docs|Any CPU.Build.0 = Docs|Any CPU
47 | {7807882D-4578-40A2-9E24-970E1E992EC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
48 | EndGlobalSection
49 | GlobalSection(SolutionProperties) = preSolution
50 | HideSolutionNode = FALSE
51 | EndGlobalSection
52 | EndGlobal
53 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Functional/PrimeNumbers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests.Functional
9 | {
10 | public class PrimeNumbers
11 | {
12 | private readonly ISequence _expectedPrimes = Sequence.With(2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
13 | 31, 37, 41, 43, 47, 53, 59, 61, 67, 71);
14 |
15 | [Fact]
16 | public void SieveOfEratosthenes()
17 | {
18 | /**
19 | * Generate integers in the range [2, Infinite) and then, for each index i:
20 | * 1) take the prime number p at position i of the input sequence
21 | * 2) remove all multiples of p (except p itself) from the input sequence
22 | */
23 | var primes = Primes(Sequence.From(2), 0);
24 |
25 | Assert.Equal(_expectedPrimes, primes.Take(20));
26 | }
27 |
28 | [Fact]
29 | public void SieveOfEratosthenes_Optimized()
30 | {
31 | var primes = PrimesOptimized(Sequence.From(2));
32 |
33 | Assert.Equal(_expectedPrimes, primes.Take(20));
34 | }
35 |
36 | [Fact]
37 | public void SieveOfEratosthenes_Optimized_WithRange()
38 | {
39 | /**
40 | * The first two versions work well for the first ~1000 prime numbers, but then we start getting StackOverflow exceptions
41 | * That's because we apply a lazily-evaluated "Where" filter at each step.
42 | * So when you go evaluate the 1000th prime number, you have to apply 999 filters.
43 | *
44 | * This version constrains the search for prime numbers to a range - here, [2,72) - and forces the evaluation of each filter
45 | * before movin onto the next step.
46 | */
47 |
48 | const int max = 72;
49 | var primes = PrimesWithin(Sequence.Range(2, max));
50 |
51 | Assert.Equal(_expectedPrimes, primes);
52 | }
53 |
54 | private ISequence Primes(ISequence seq, int index)
55 | {
56 | //take nth prime number
57 | var n = seq[index];
58 |
59 | //remove multiples of n, except n itself
60 | var filtered = seq.Where(e => e % n != 0 || e == n);
61 |
62 | return new Sequence(n, () => Primes(filtered, index + 1));
63 | }
64 |
65 | private ISequence PrimesOptimized(ISequence seq)
66 | {
67 | //take the next prime number
68 | var n = seq.Head;
69 |
70 | //skip n, and remove further multiples of n
71 | var filtered = seq.Tail.Where(e => e % n != 0);
72 |
73 | return new Sequence(n, () => PrimesOptimized(filtered));
74 | }
75 |
76 | private ISequence PrimesWithin(ISequence range)
77 | {
78 | if (range.IsEmpty)
79 | return range;
80 |
81 | //take the next prime number
82 | var p = range.Head;
83 |
84 | //skip p, and remove further multiples of p
85 | //force the evaluation of the filtered sequence, to avoid stacking filters
86 | var filtered = range.Tail.Where(e => e % p != 0).Force();
87 |
88 | return new Sequence(p, () => PrimesWithin(filtered));
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/Sequences/KmpSearchAlgorithm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Sequences
7 | {
8 | ///
9 | /// Searches for a subsequence within a sequence using a variation of the Knuth-Morris-Pratt algorithm.
10 | /// This variation doesn't require that we know the size of "source" beforehand.
11 | ///
12 | internal static class KmpSearchAlgorithm
13 | {
14 | ///
15 | /// Searches for a subsequence within a sequence using a variation of the Knuth-Morris-Pratt algorithm.
16 | /// This variation doesn't require that we know the size of "source" beforehand.
17 | ///
18 | internal static int Search(IEnumerable source, T[] word, IEqualityComparer cmp = null)
19 | {
20 | if (cmp == null)
21 | cmp = EqualityComparer.Default;
22 |
23 | using (var iterator = source.GetEnumerator())
24 | return Search(iterator, word, cmp);
25 | }
26 |
27 | /**
28 | * Assumes word.Length >= 2
29 | */
30 |
31 | private static int Search(IEnumerator source, T[] word, IEqualityComparer cmp)
32 | {
33 | var table = KmpTable(word, cmp);
34 |
35 | int wordIndex = 0;
36 | int index = 0;
37 |
38 | //if the source is empty
39 | if (!source.MoveNext())
40 | return -1;
41 |
42 | while (true)
43 | {
44 | var e = source.Current;
45 |
46 | if (cmp.Equals(e, word[wordIndex]))
47 | {
48 | if (wordIndex == word.Length - 1)
49 | return (index - word.Length + 1);
50 |
51 | wordIndex++;
52 | }
53 | else
54 | {
55 | int iTemp = wordIndex;
56 | wordIndex = table[wordIndex];
57 |
58 | //if an ongoing match just failed,
59 | //compare "e" again, but this time against word[table[i]]
60 | if (iTemp > 0)
61 | continue;
62 | }
63 |
64 | index++;
65 | if (!source.MoveNext())
66 | break;
67 | }
68 | return -1;
69 | }
70 |
71 | ///
72 | /// Builds the "partial match" table (or jump table).
73 | /// Table[i] represents how characters of "word" can be skipped, when the comparison between "e" and word[i] fails
74 | ///
75 | private static int[] KmpTable(T[] word, IEqualityComparer cmp)
76 | {
77 | var t = new int[word.Length];
78 |
79 | t[0] = 0;
80 | t[1] = 0;
81 |
82 | int pos = 2;
83 | int cnd = 0;
84 |
85 | while (pos < word.Length)
86 | {
87 | if (cmp.Equals(word[pos - 1], word[cnd]))
88 | {
89 | t[pos] = cnd + 1;
90 | cnd++;
91 | pos++;
92 | }
93 | else if (cnd > 0)
94 | {
95 | cnd = t[cnd];
96 | }
97 | else
98 | {
99 | t[pos] = 0;
100 | pos++;
101 | }
102 | }
103 |
104 | return t;
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/CopyToTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class CopyToTests
11 | {
12 | [Fact]
13 | public void CopyTo_CopiesWholeSequence()
14 | {
15 | var sequence = Sequence.Range(1, 6);
16 | var copy = new int[5];
17 | int[] expectedCopy = {1, 2, 3, 4, 5};
18 |
19 | int copied = sequence.CopyTo(copy);
20 |
21 | Assert.Equal(5, copied);
22 | Assert.Equal(expectedCopy, copy);
23 | }
24 |
25 | [Fact]
26 | public void CopyTo_StopsWhenAllElementsHaveBeenCopied()
27 | {
28 | var sequence = Sequence.Range(1, 4);
29 | var copy = new int[5];
30 | int[] expectedCopy = {1, 2, 3, 0, 0};
31 |
32 | int copied = sequence.CopyTo(copy);
33 |
34 | Assert.Equal(3, copied);
35 | Assert.Equal(expectedCopy, copy);
36 | }
37 |
38 | [Fact]
39 | public void CopyTo_StopsWhenArrayIsFull()
40 | {
41 | var sequence = Sequence.Range(1, 10);
42 | var copy = new int[5];
43 | int[] expectedCopy = {1, 2, 3, 4, 5};
44 |
45 | int copied = sequence.CopyTo(copy);
46 |
47 | Assert.Equal(5, copied);
48 | Assert.Equal(expectedCopy, copy);
49 | }
50 |
51 | [Fact]
52 | public void CopyTo_StopsWhenNElementsHaveBeenCopied()
53 | {
54 | var sequence = Sequence.Range(1, 10);
55 | var copy = new int[5];
56 | int[] expectedCopy = {1, 2, 3, 0, 0};
57 |
58 | int copied = sequence.CopyTo(0, copy, 0, 3);
59 |
60 | Assert.Equal(3, copied);
61 | Assert.Equal(expectedCopy, copy);
62 | }
63 |
64 | [Fact]
65 | public void CopyTo_SkipsOriginOffset()
66 | {
67 | var sequence = Sequence.Range(1, 10);
68 | var copy = new int[5];
69 | int[] expectedCopy = {3, 4, 5, 0, 0};
70 |
71 | int copied = sequence.CopyTo(2, copy, 0, 3);
72 |
73 | Assert.Equal(3, copied);
74 | Assert.Equal(expectedCopy, copy);
75 | }
76 |
77 | [Fact]
78 | public void CopyTo_SkipsDestinationOffset()
79 | {
80 | var sequence = Sequence.Range(1, 10);
81 | var copy = new int[5];
82 | int[] expectedCopy = {0, 0, 1, 2, 3};
83 |
84 | int copied = sequence.CopyTo(0, copy, 2, 10);
85 |
86 | Assert.Equal(3, copied);
87 | Assert.Equal(expectedCopy, copy);
88 | }
89 |
90 | [Fact]
91 | public void CopyTo_ThrowsException_When_DestinationOffsetIsNegative()
92 | {
93 | var sequence = Sequence.Range(1, 6);
94 | var copy = new int[5];
95 |
96 | Assert.Throws(() => sequence.CopyTo(copy, -1));
97 | }
98 |
99 | [Fact]
100 | public void CopyTo_ThrowsException_When_OriginOffsetIsNegative()
101 | {
102 | var sequence = Sequence.Range(1, 6);
103 | var copy = new int[5];
104 |
105 | Assert.Throws(() => sequence.CopyTo(-1, copy, 0, 1));
106 | }
107 |
108 | [Fact]
109 | public void CopyTo_ThrowsException_When_CountIsNegative()
110 | {
111 | var sequence = Sequence.Range(1, 6);
112 | var copy = new int[5];
113 |
114 | Assert.Throws(() => sequence.CopyTo(0, copy, 0, -1));
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/ZipTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class ZipTests
11 | {
12 | [Fact]
13 | public void Zip_AssociatesElements()
14 | {
15 | var sequence = Sequence.From(0);
16 | var zipped = sequence.Zip(sequence.Tail).Take(5);
17 | Tuple[] expectedZipped =
18 | {
19 | Tuple.Create(0, 1),
20 | Tuple.Create(1, 2),
21 | Tuple.Create(2, 3),
22 | Tuple.Create(3, 4),
23 | Tuple.Create(4, 5),
24 | };
25 |
26 | Assert.Equal(expectedZipped, zipped);
27 | }
28 |
29 | [Fact]
30 | public void Zip_TruncatesSecondSequence_If_LongerThanFirst()
31 | {
32 | var zipped = Sequence.Range(1, 4).Zip(
33 | Sequence.Range(10, 21));
34 |
35 | Tuple[] expectedZipped =
36 | {
37 | Tuple.Create(1, 10),
38 | Tuple.Create(2, 11),
39 | Tuple.Create(3, 12)
40 | };
41 |
42 | Assert.Equal(expectedZipped, zipped);
43 | }
44 |
45 |
46 | [Fact]
47 | public void Zip_TruncatesFirstSequence_If_LongerThanSecond()
48 | {
49 | var zipped = Sequence.Range(10, 21).Zip(
50 | Sequence.Range(1, 4));
51 |
52 | Tuple[] expectedZipped =
53 | {
54 | Tuple.Create(10, 1),
55 | Tuple.Create(11, 2),
56 | Tuple.Create(12, 3)
57 | };
58 |
59 | Assert.Equal(expectedZipped, zipped);
60 | }
61 |
62 | [Fact]
63 | public void ZipAll_ExtendsFirstSequence_If_ShorterThanSecond()
64 | {
65 | var zipped = Sequence.Range(1, 4).ZipAll(
66 | Sequence.Range(1, 6), 0, 9);
67 |
68 | Tuple[] expectedZipped =
69 | {
70 | Tuple.Create(1, 1),
71 | Tuple.Create(2, 2),
72 | Tuple.Create(3, 3),
73 | Tuple.Create(0, 4),
74 | Tuple.Create(0, 5),
75 | };
76 |
77 | Assert.Equal(expectedZipped, zipped);
78 | }
79 |
80 | [Fact]
81 | public void ZipAll_ExtendsSecondSequence_If_ShorterThanFirst()
82 | {
83 | var zipped = Sequence.Range(1, 6).ZipAll(
84 | Sequence.Range(1, 4), 0, 9);
85 |
86 | Tuple[] expectedZipped =
87 | {
88 | Tuple.Create(1, 1),
89 | Tuple.Create(2, 2),
90 | Tuple.Create(3, 3),
91 | Tuple.Create(4, 9),
92 | Tuple.Create(5, 9),
93 | };
94 |
95 | Assert.Equal(expectedZipped, zipped);
96 | }
97 |
98 | [Fact]
99 | public void ZipWithIndex_AssociatesElementsWithTheirIndex()
100 | {
101 | var sequence = Sequence.Range(10, 16);
102 | var zipped = sequence.ZipWithIndex();
103 |
104 | Tuple[] expectedZipped =
105 | {
106 | Tuple.Create(10, 0),
107 | Tuple.Create(11, 1),
108 | Tuple.Create(12, 2),
109 | Tuple.Create(13, 3),
110 | Tuple.Create(14, 4),
111 | Tuple.Create(15, 5),
112 | };
113 |
114 | Assert.Equal(expectedZipped, zipped);
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/SlidingTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Xunit;
8 |
9 | namespace Sequences.Tests
10 | {
11 | public class SlidingTests
12 | {
13 | [Fact]
14 | public void Sliding_Returns_SlidingWindows()
15 | {
16 | var sequence = Sequence.Range(0, 5);
17 | var windows = sequence.Sliding(3).ToList();
18 | var expectedWindows = new List>
19 | {
20 | new List {0, 1, 2},
21 | new List {1, 2, 3},
22 | new List {2, 3, 4}
23 | };
24 |
25 | Assert.Equal(expectedWindows.Count, windows.Count);
26 |
27 | for (int i = 0; i < windows.Count; i++)
28 | Assert.Equal(expectedWindows[i], windows[i]);
29 | }
30 |
31 | [Fact]
32 | public void Sliding_WithStep_TruncatesLastWindow()
33 | {
34 | var sequence = Sequence.Range(0, 6);
35 | var windows = sequence.Sliding(3, 2).ToList();
36 | var expectedWindows = new List>
37 | {
38 | new List {0, 1, 2},
39 | new List {2, 3, 4},
40 | new List {4, 5}
41 | };
42 |
43 | Assert.Equal(expectedWindows.Count, windows.Count);
44 |
45 | for (int i = 0; i < windows.Count; i++)
46 | Assert.Equal(expectedWindows[i], windows[i]);
47 | }
48 |
49 | [Fact]
50 | public void Sliding_Stops_When_LastElementIsSkipped()
51 | {
52 | var sequence = Sequence.Range(0, 8);
53 | var windows = sequence.Sliding(2, 4).ToList();
54 | var expectedWindows = new List>
55 | {
56 | new List {0, 1},
57 | new List {4, 5}
58 | };
59 |
60 | Assert.Equal(expectedWindows.Count, windows.Count);
61 |
62 | for (int i = 0; i < windows.Count; i++)
63 | Assert.Equal(expectedWindows[i], windows[i]);
64 | }
65 |
66 | [Fact]
67 | public void Sliding_Returns_WholeSequence_When_SizeIsHigherThanCount()
68 | {
69 | var sequence = Sequence.Range(0, 4);
70 | var windows = sequence.Sliding(10).ToList();
71 | var expectedWindow = new List {0, 1, 2, 3};
72 |
73 | Assert.Equal(1, windows.Count);
74 | Assert.Equal(expectedWindow, windows.Single());
75 | }
76 |
77 | [Fact]
78 | public void Sliding_Returns_EmptyEnumerable_When_SequenceIsEmpty()
79 | {
80 | var sequence = Sequence.Empty();
81 | var windows = sequence.Sliding(10);
82 |
83 | Assert.False(windows.Any());
84 | }
85 |
86 | [Fact]
87 | public void Sliding_ThrowsException_When_SizeIsNotPositive()
88 | {
89 | Assert.Throws(() => Sequence.Empty().Sliding(0));
90 | Assert.Throws(() => Sequence.Empty().Sliding(-1));
91 | Assert.Throws(() => Sequence.Empty().Sliding(0, 1));
92 | Assert.Throws(() => Sequence.Empty().Sliding(-1, 1));
93 | }
94 |
95 | [Fact]
96 | public void Sliding_ThrowsException_When_StepIsNotPositive()
97 | {
98 | Assert.Throws(() => Sequence.Empty().Sliding(1, 0));
99 | Assert.Throws(() => Sequence.Empty().Sliding(1, -1));
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/Sequences/Sequences.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 11.0
6 | Debug
7 | AnyCPU
8 | {6C9F9E66-E026-4966-AEB2-9B29B5F0DF85}
9 | Library
10 | Properties
11 | Sequences
12 | Sequences
13 | v4.5
14 | Profile111
15 | 512
16 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 | bin\Debug\Sequences.XML
27 |
28 |
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 | bin\Release\Sequences.XML
36 |
37 |
38 | bin\Docs\
39 | TRACE
40 | bin\Release\Sequences.XML
41 | true
42 | pdbonly
43 | AnyCPU
44 | prompt
45 | MinimumRecommendedRules.ruleset
46 |
47 |
48 | true
49 |
50 |
51 | ../../signing.snk
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | xcopy /Q /Y /E "$(TargetDir)*.*" "$(SolutionDir)bin\"
71 |
72 |
79 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/AddRemoveTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class AddRemoveTests
11 | {
12 | [Fact]
13 | public void Concat_ConcatenatesTwoSequences()
14 | {
15 | var first = Sequence.With(1, 2);
16 | var second = Sequence.With(3, 4);
17 |
18 | Assert.Equal(new[] {1, 2, 3, 4},
19 | first.Concat(() => second));
20 | }
21 |
22 | [Fact]
23 | public void Concat_ConcatenatesEmpty_WithNonEmpty()
24 | {
25 | var first = Sequence.Empty();
26 | var second = Sequence.With(1, 2, 3, 4);
27 |
28 | Assert.Equal(new[] {1, 2, 3, 4},
29 | first.Concat(() => second));
30 | }
31 |
32 | [Fact]
33 | public void Concat_ConcatenatesNonEmpty_WithEmpty()
34 | {
35 | var first = Sequence.With(1, 2, 3, 4);
36 | var second = Sequence.Empty();
37 |
38 | Assert.Equal(new[] {1, 2, 3, 4},
39 | first.Concat(() => second));
40 | }
41 |
42 | [Fact]
43 | public void Append_AppendsElementToSequence()
44 | {
45 | Assert.Equal(new[] {1, 2, 3, 4},
46 | Sequence.With(1, 2, 3).Append(4));
47 | }
48 |
49 | [Fact]
50 | public void Prepend_PrependsElementToSequence()
51 | {
52 | Assert.Equal(new[] {1, 2, 3, 4},
53 | Sequence.With(2, 3, 4).Prepend(1));
54 | }
55 |
56 | [Fact]
57 | public void Remove_RemovesElement()
58 | {
59 | var sequence = Sequence.Range(1, 6);
60 | var result = sequence.Remove(3);
61 | var expectedResult = new[] {1, 2, 4, 5};
62 |
63 | Assert.Equal(expectedResult, result);
64 | }
65 |
66 | [Fact]
67 | public void Remove_RemovesHead()
68 | {
69 | var sequence = Sequence.Range(1, 6);
70 | var result = sequence.Remove(1);
71 | var expectedResult = new[] {2, 3, 4, 5};
72 |
73 | Assert.Equal(expectedResult, result);
74 | }
75 |
76 | [Fact]
77 | public void Remove_Returns_EquivalentSequence_When_ElementIsNotFound()
78 | {
79 | var sequence = Sequence.Range(1, 6);
80 | var result = sequence.Remove(10);
81 | var expectedResult = new[] {1, 2, 3, 4, 5};
82 |
83 | Assert.Equal(expectedResult, result);
84 | }
85 |
86 | [Fact]
87 | public void Remove_Returns_SameSequence_When_SequenceIsEmpty()
88 | {
89 | var sequence = Sequence.Empty();
90 | var result = sequence.Remove(10);
91 |
92 | Assert.Same(sequence, result);
93 | }
94 |
95 | [Fact]
96 | public void Updated_UpdatesElement()
97 | {
98 | var sequence = Sequence.Range(1, 6);
99 | var updated = sequence.Updated(2, 10);
100 | int[] expectedUpdated = {1, 2, 10, 4, 5};
101 |
102 | Assert.Equal(expectedUpdated, updated);
103 | }
104 |
105 | [Fact]
106 | public void Updated_DoesNothing_When_IndexGreaterThanCount()
107 | {
108 | var sequence = Sequence.Range(1, 6);
109 | var updated = sequence.Updated(5, 10);
110 | int[] expectedUpdated = {1, 2, 3, 4, 5};
111 |
112 | Assert.Equal(expectedUpdated, updated);
113 | }
114 |
115 | [Fact]
116 | public void Updated_ThrowsException_When_IndexLessThanZero()
117 | {
118 | var sequence = Sequence.Range(1, 6);
119 | Assert.Throws(() => sequence.Updated(-1, 10));
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/KmpSearchTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Sequences.Tests
9 | {
10 | public class KmpSearchTests
11 | {
12 | private const int SourcesCount = 10000;
13 | private const int SourceLength = 1000;
14 |
15 | private const int WordMinLength = 2;
16 | private const int WordMaxLength = 10;
17 |
18 | private const string Chars = "0123456789";
19 | private readonly Random _rnd = new Random();
20 |
21 | [Fact]
22 | public void Kmp_FindsFirstIndexOfWord()
23 | {
24 | foreach (var data in GetTestData())
25 | {
26 | //with unknown size
27 | int strSearchResult = data.Source.IndexOf(data.Word, data.From, StringComparison.InvariantCulture);
28 | int seqSearchResult = data.Source.AsSequence().IndexOfSlice(data.Word, data.From);
29 |
30 | Assert.Equal(strSearchResult, seqSearchResult);
31 |
32 | //with known size
33 | int forcedSeqSearchResult = data.Source.AsSequence().Force().IndexOfSlice(data.Word, data.From);
34 | Assert.Equal(strSearchResult, forcedSeqSearchResult);
35 | }
36 | }
37 |
38 | [Fact]
39 | public void EdgeCases()
40 | {
41 | //source is empty + word is empty
42 | Assert.Equal(0, Sequence.Empty().IndexOfSlice(new int[] {}));
43 | Assert.Equal(0, Sequence.Empty().IndexOfSlice(new int[] {}, -1));
44 | Assert.Equal(-1, Sequence.Empty().IndexOfSlice(new int[] {}, 1));
45 |
46 | //word is empty
47 | Assert.Equal(0, Sequence.With(1, 2).IndexOfSlice(new int[] {}));
48 | Assert.Equal(0, Sequence.With(1, 2).IndexOfSlice(new int[] {}, -1));
49 | Assert.Equal(1, Sequence.With(1, 2).IndexOfSlice(new int[] {}, 1));
50 | Assert.Equal(-1, Sequence.With(1, 2).IndexOfSlice(new int[] {}, 2));
51 |
52 | //source == word
53 | Assert.Equal(0, Sequence.With(1, 2, 3).IndexOfSlice(new[] {1, 2, 3}));
54 | Assert.Equal(0, Sequence.With(1, 2, 3).IndexOfSlice(new[] {1, 2, 3}, -1));
55 | Assert.Equal(-1, Sequence.With(1, 2, 3).IndexOfSlice(new[] {1, 2, 3}, 1));
56 |
57 | Assert.Equal(0, Sequence.With(1, 2, 3).Force().IndexOfSlice(new[] {1, 2, 3}));
58 | Assert.Equal(0, Sequence.With(1, 2, 3).Force().IndexOfSlice(new[] {1, 2, 3}, -1));
59 | Assert.Equal(-1, Sequence.With(1, 2, 3).Force().IndexOfSlice(new[] {1, 2, 3}, 1));
60 |
61 | //word.Length == 1
62 | Assert.Equal(1, Sequence.With(1, 2).Force().IndexOfSlice(new[] {2}));
63 | Assert.Equal(1, Sequence.With(1, 2).Force().IndexOfSlice(new[] {2}, -1));
64 | Assert.Equal(0, Sequence.With(1, 2).Force().IndexOfSlice(new[] {2}, 1));
65 | Assert.Equal(-1, Sequence.With(1, 2).Force().IndexOfSlice(new[] {2}, 2));
66 | }
67 |
68 | private IEnumerable GetTestData()
69 | {
70 | for (int i = 0; i < SourcesCount; i++)
71 | {
72 | char[] sourceChars = Enumerable.Repeat(Chars, SourceLength)
73 | .Select(chars => chars[_rnd.Next(chars.Length)])
74 | .ToArray();
75 |
76 | int length = _rnd.Next(WordMinLength, WordMaxLength);
77 | int start = _rnd.Next(sourceChars.Length - length);
78 |
79 | var source = new string(sourceChars);
80 | var word = source.Substring(start, length);
81 |
82 | var from = _rnd.Next(0, start + 1);
83 |
84 | yield return new TestData
85 | {
86 | Source = source,
87 | Word = word,
88 | From = from
89 | };
90 | }
91 | }
92 |
93 | private class TestData
94 | {
95 | public string Source;
96 | public string Word;
97 | public int From;
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/docs/Sequence.Docs/Content/Welcome.aml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Sequences is a port of Scala's
6 | Stream[+A]
7 | http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Stream
8 | to C#.
9 |
10 |
11 | Sequences is available on
12 |
13 | NuGet
14 | https://www.nuget.org/packages/Sequences
15 |
16 | and the source code is on
17 |
18 | GitHub
19 | https://github.com/dcastro/Sequences
20 |
21 |
22 |
23 |
24 |
25 |
26 | Intro
27 |
28 |
29 |
30 | A T:Sequences.Sequence`1 is an immutable lazy list whose elements are only evaluated when they are needed.
31 | A sequence is composed by a head (the first element) and a lazily-evaluated tail (the remaining elements).
32 |
33 |
34 | The fact that the tail is lazily-evaluated, makes it easy to represent infinite series or sets. For example, here's how to represent the set of all natural numbers.
35 |
36 |
37 |
38 | Naturals(int start)
40 | {
41 | return new Sequence( head: start,
42 | tail: () => Naturals(start + 1));
43 | }
44 |
45 | var naturals = Naturals(1);
46 |
47 | //take the first 5 natural numbers
48 | naturals.Take(5).ForEach(Console.Write); //prints 12345]]>
49 |
50 |
51 |
52 | Or, even simpler:
53 |
54 |
55 |
56 |
58 |
59 |
60 |
61 | Sequences also features memoization, i.e., the sequence stores previously computed values to avoid re-evaluation.
62 |
63 |
64 |
65 |
68 | {
69 | Console.WriteLine("Adding " + odd + " + 2");
70 | return odd + 2;
71 | });
72 |
73 | odds.Take(3).ForEach(Console.WriteLine);
74 | odds.Take(5).ForEach(Console.WriteLine);
75 |
76 | //prints
77 | //1
78 | //Adding 1 + 2
79 | //3
80 | //Adding 3 + 2
81 | //5
82 |
83 | //and then
84 | //1
85 | //3
86 | //5
87 | //Adding 5 + 2
88 | //7
89 | //Adding 7 + 2
90 | //9]]>
91 |
92 |
93 |
94 | You can iterate through an infinite sequence for as long as you want.
95 | As long as you don't hold onto its head, each sequence will be elected for garbage collection as soon as you move to the next value.
96 | This prevents an infinite sequence from occupying a large and growing ammount of memory.
97 |
98 |
99 |
100 | odd + 2))
102 | {
103 | //when you move to Sequence(11, ?),
104 | //the previous Sequence(9, ?) is elected for collection.
105 | }]]>
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | T:Sequences.ISequence`1
116 | T:Sequences.Sequence
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/src/Sequences/SequenceBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Sequences
7 | {
8 | ///
9 | /// A mutable builder, capable of building sequences from elements and other collections.
10 | ///
11 | /// The type of the elements in the resulting sequences.
12 | public class SequenceBuilder
13 | {
14 | private readonly List>> _parts = new List>>();
15 |
16 | ///
17 | /// Appends one or more elements to this builder.
18 | ///
19 | /// One or more elements to be added to this builder.
20 | /// This builder.
21 | public SequenceBuilder Append(params T[] elems)
22 | {
23 | return Append(elems.AsEnumerable());
24 | }
25 |
26 | ///
27 | /// Appends a collection to this builder.
28 | ///
29 | /// The collection to be added to this builder.
30 | /// This builder.
31 | public SequenceBuilder Append(IEnumerable enumerable)
32 | {
33 | if (enumerable == null) throw new ArgumentNullException("enumerable");
34 |
35 | return Append(() => enumerable);
36 | }
37 |
38 | ///
39 | /// Appends a lazily-evaluated collection to this builder.
40 | ///
41 | /// The collection to be added to this builder.
42 | /// This builder.
43 | public SequenceBuilder Append(Func> enumerable)
44 | {
45 | if (enumerable == null) throw new ArgumentNullException("enumerable");
46 |
47 | _parts.Add(enumerable);
48 | return this;
49 | }
50 |
51 | ///
52 | /// Appends a single element to this builder.
53 | ///
54 | /// The element to be added to this builder.
55 | /// This builder.
56 | public SequenceBuilder Append(T elem)
57 | {
58 | return Append(() => elem);
59 | }
60 |
61 | ///
62 | /// Appends a single lazily-evaluated element to this builder.
63 | ///
64 | /// The element to be added to this builder.
65 | /// This builder.
66 | public SequenceBuilder Append(Func elem)
67 | {
68 | _parts.Add(() => Sequence.With(elem()));
69 | return this;
70 | }
71 |
72 | ///
73 | /// Produces a sequence from the added elements.
74 | ///
75 | /// A sequence containing the elements added to this builder.
76 | public ISequence ToSequence()
77 | {
78 | //realize sublist and flatten it as a sequence
79 | return _parts
80 | .Take(_parts.Count)
81 | .ToList()
82 | .SelectMany(enumerable => enumerable())
83 | .AsSequence();
84 | }
85 |
86 | ///
87 | /// Clears the contents of this builder.
88 | ///
89 | /// This builder.
90 | public SequenceBuilder Clear()
91 | {
92 | _parts.Clear();
93 | return this;
94 | }
95 |
96 | ///
97 | /// Appends a single element to the builder.
98 | ///
99 | /// The builder to which will be added.
100 | /// The element to be added to the builder.
101 | /// The given builder.
102 | public static SequenceBuilder operator +(SequenceBuilder builder, T elem)
103 | {
104 | if (builder == null)
105 | builder = new SequenceBuilder();
106 |
107 | return builder.Append(elem);
108 | }
109 |
110 | ///
111 | /// Appends a collection to the builder.
112 | ///
113 | /// The builder to which will be added.
114 | /// The collection to be added to the builder.
115 | /// The given builder.
116 | public static SequenceBuilder operator +(SequenceBuilder builder, IEnumerable enumerable)
117 | {
118 | if (builder == null)
119 | builder = new SequenceBuilder();
120 |
121 | return builder.Append(enumerable);
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/AggregationTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Xunit;
8 |
9 | namespace Sequences.Tests
10 | {
11 | public class AggregationTests
12 | {
13 | [Fact]
14 | public void Fold_AccumulatesValues()
15 | {
16 | int sum = Sequence.With(1, 2, 3, 4).Fold(0, (a, b) => a + b);
17 | Assert.Equal(10, sum);
18 | }
19 |
20 | [Fact]
21 | public void Fold_ReturnsSeed_When_SequenceIsEmpty()
22 | {
23 | int sum = Sequence.Empty().Fold(0, (a, b) => a + b);
24 | Assert.Equal(0, sum);
25 | }
26 |
27 | [Fact]
28 | public void Fold_GoesLeftToRight()
29 | {
30 | var additions = new List>();
31 | var expectedAdditions = new List>
32 | {
33 | Tuple.Create(0, 1),
34 | Tuple.Create(1, 2),
35 | Tuple.Create(3, 3),
36 | Tuple.Create(6, 4)
37 | };
38 |
39 | Sequence.With(1, 2, 3, 4).Fold(0, (a, b) =>
40 | {
41 | additions.Add(Tuple.Create(a, b));
42 | return a + b;
43 | });
44 |
45 | Assert.Equal(expectedAdditions, additions);
46 | }
47 |
48 | [Fact]
49 | public void FoldRight_AccumulatesValues()
50 | {
51 | int sum = Sequence.With(1, 2, 3, 4).FoldRight(0, (a, b) => a + b);
52 | Assert.Equal(10, sum);
53 | }
54 |
55 | [Fact]
56 | public void FoldRight_ReturnsSeed_When_SequenceIsEmpty()
57 | {
58 | int sum = Sequence.Empty().FoldRight(0, (a, b) => a + b);
59 | Assert.Equal(0, sum);
60 | }
61 |
62 | [Fact]
63 | public void FoldRight_GoesRightToLeft()
64 | {
65 |
66 | var additions = new List>();
67 | var expectedAdditions = new List>
68 | {
69 | Tuple.Create(4, 0),
70 | Tuple.Create(3, 4),
71 | Tuple.Create(2, 7),
72 | Tuple.Create(1, 9)
73 | };
74 |
75 | Sequence.With(1, 2, 3, 4).FoldRight(0, (a, b) =>
76 | {
77 | additions.Add(Tuple.Create(a, b));
78 | return a + b;
79 | });
80 |
81 | Assert.Equal(expectedAdditions, additions);
82 | }
83 |
84 | [Fact]
85 | public void Reduce_AccumulatesValues()
86 | {
87 | int sum = Sequence.With(1, 2, 3, 4).Reduce((a, b) => a + b);
88 | Assert.Equal(10, sum);
89 | }
90 |
91 | [Fact]
92 | public void Reduce_Throws_When_SequenceIsEmpty()
93 | {
94 | Assert.Throws(() => Sequence.Empty().Reduce((a, b) => a + b));
95 | }
96 |
97 | [Fact]
98 | public void ReduceRight_AccumulatesValues()
99 | {
100 | int sum = Sequence.With(1, 2, 3, 4).ReduceRight((a, b) => a + b);
101 | Assert.Equal(10, sum);
102 | }
103 |
104 | [Fact]
105 | public void ReduceRight_Throws_When_SequenceIsEmpty()
106 | {
107 | Assert.Throws(() => Sequence.Empty().ReduceRight((a, b) => a + b));
108 | }
109 |
110 | [Fact]
111 | public void Scan_Returns_ListOfAccumulations()
112 | {
113 | var sequence = Sequence.With(1, 2, 3, 4);
114 | var scan = sequence.Scan(0, (a, b) => a + b);
115 | var expected = new[] {0, 1, 3, 6, 10};
116 |
117 | Assert.Equal(expected, scan);
118 | }
119 |
120 | [Fact]
121 | public void Scan_Returns_Seed_When_SequenceIsEmpty()
122 | {
123 | var sequence = Sequence.Empty();
124 | var scan = sequence.Scan(0, (a, b) => a + b);
125 | var expected = new[] {0};
126 |
127 | Assert.Equal(expected, scan);
128 | }
129 |
130 | [Fact]
131 | public void ScanRight_Returns_ListOfAccumulations()
132 | {
133 | var sequence = Sequence.With(1, 2, 3, 4);
134 | var scan = sequence.ScanRight(0, (a, b) => a + b);
135 | var expected = new[] {10, 9, 7, 4, 0};
136 |
137 | Assert.Equal(expected, scan);
138 | }
139 |
140 | [Fact]
141 | public void ScanRight_Returns_Seed_When_SequenceIsEmpty()
142 | {
143 | var sequence = Sequence.Empty();
144 | var scan = sequence.ScanRight(0, (a, b) => a + b);
145 | var expected = new[] {0};
146 |
147 | Assert.Equal(expected, scan);
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Functional/PascalsTriangle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Sequences.Tests.Functional.Extensions;
7 | using Xunit;
8 |
9 | namespace Sequences.Tests.Functional
10 | {
11 | public class PascalsTriangle
12 | {
13 | private readonly ISequence> _expectedTriangle;
14 |
15 | public PascalsTriangle()
16 | {
17 | _expectedTriangle = Sequence.With(Sequence.With(1),
18 | Sequence.With(1, 1),
19 | Sequence.With(1, 2, 1),
20 | Sequence.With(1, 3, 3, 1),
21 | Sequence.With(1, 4, 6, 4, 1),
22 | Sequence.With(1, 5, 10, 10, 5, 1));
23 | }
24 |
25 | [Fact]
26 | public void V1()
27 | {
28 | Func, ISequence> func = //to build a row..
29 | row => row.Zip(row.Tail) //zip the previous row with its tail, i.e., (1,3,3,1) becomes ((1,3), (3,3), (3,1))
30 | .Select(TupleEx.Sum) //select the sum of each pair, i.e., (4, 6, 4)
31 | .Append(1) //add (1) to each end
32 | .Prepend(1);
33 |
34 | /*
35 | * Alternative syntax
36 | *
37 | Func, ISequence> func = //to build a row..
38 | row =>
39 | Sequence.With(1) //start with 1...
40 | .Concat(() => //followed by...
41 | row.Zip(row.Tail) //zipping the previous row with its tail, i.e., (1,3,3,1) becomes ((1,3), (3,3), (3,1))
42 | .Select(TupleEx.Sum)) //and select the sum of each pair, i.e., (4, 6, 4)
43 | .Append(1); //and, finally, another 1.
44 | */
45 |
46 |
47 | var triangle = Sequence.Iterate(
48 | Sequence.With(1), func);
49 |
50 | var sixRows = triangle.Take(6);
51 |
52 | //Assertions
53 | Assert.Equal(6, sixRows.Count);
54 |
55 | _expectedTriangle.Zip(sixRows).ForEach(
56 | rows => Assert.Equal(rows.Item1, rows.Item2));
57 | }
58 |
59 | [Fact]
60 | public void V2()
61 | {
62 | var triangle = Pascal();
63 | var sixRows = triangle.Take(6);
64 |
65 | //Assertions
66 | Assert.Equal(6, sixRows.Count);
67 |
68 | _expectedTriangle.Zip(sixRows).ForEach(
69 | rows => Assert.Equal(rows.Item1, rows.Item2));
70 | }
71 |
72 | private ISequence> Pascal()
73 | {
74 | ISequence firstRow = Sequence.With(1);
75 | ISequence secondRow = Sequence.With(1, 1);
76 |
77 | return Sequence.With(
78 | firstRow,
79 | secondRow
80 | ).Concat(() => PascalAux(secondRow));
81 | }
82 |
83 | private ISequence> PascalAux(ISequence previousRow)
84 | {
85 | ISequence newRow = previousRow
86 | .Sliding(2) //for each 2 consecutive elements...
87 | .Select(pair => pair.Sum()) //select their sum
88 | .AsSequence()
89 | .Append(1) //append (1)
90 | .Prepend(1); //prepend (1)
91 | return new Sequence>(newRow, () => PascalAux(newRow));
92 | }
93 |
94 | [Fact]
95 | public void V3()
96 | {
97 | //similar to V2, but doesn't define auxiliary methods
98 | Func, ISequence> func =
99 | row => row.Sliding(2)
100 | .Where(group => group.LengthCompare(2) == 0)
101 | .Select(pair => pair.Sum())
102 | .AsSequence()
103 | .Append(1)
104 | .Prepend(1);
105 |
106 | var triangle = Sequence.Iterate(Sequence.With(1), func);
107 | var sixRows = triangle.Take(6);
108 |
109 | //Assertions
110 | Assert.Equal(6, sixRows.Count);
111 |
112 | _expectedTriangle.Zip(sixRows).ForEach(
113 | rows => Assert.Equal(rows.Item1, rows.Item2));
114 | }
115 |
116 | [Fact]
117 | public void V4()
118 | {
119 | var triangle = Sequence.Iterate(
120 | Sequence.With(1), //start with row (1), and then...
121 | row => row.Append(0) //shift row to the left
122 | .Zip(row.Prepend(0)) //shift row to the right, and zip both shifted rows
123 | .Select(TupleEx.Sum)); //sum the two shifted rows
124 |
125 | var sixRows = triangle.Take(6);
126 |
127 | //Assertions
128 | Assert.Equal(6, sixRows.Count);
129 |
130 | _expectedTriangle.Zip(sixRows).ForEach(
131 | rows => Assert.Equal(rows.Item1, rows.Item2));
132 | }
133 | }
134 | }
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/IndexTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Xunit;
8 |
9 | namespace Sequences.Tests
10 | {
11 | public class IndexTests
12 | {
13 | [Fact]
14 | public void Indices_Returns_RangeOfIndices()
15 | {
16 | var sequence = Sequence.Fill(0, 5);
17 | var indices = sequence.Indices();
18 | int[] expectedIndices = {0, 1, 2, 3, 4};
19 |
20 | Assert.Equal(expectedIndices, indices);
21 | }
22 |
23 | [Fact]
24 | public void IndexOf_Returns_IndexOfElement()
25 | {
26 | var sequence = Sequence.Range(0, 5);
27 | var index = sequence.IndexOf(3);
28 |
29 | Assert.Equal(3, index);
30 | }
31 |
32 | [Fact]
33 | public void IndexOf_Returns_MinusOne_When_ElementIsNotFound()
34 | {
35 | var sequence = Sequence.Range(0, 5);
36 | var index = sequence.IndexOf(5);
37 |
38 | Assert.Equal(-1, index);
39 | }
40 |
41 | [Fact]
42 | public void IndexOf_Returns_MinusOne_When_ElementIsBeforeFrom()
43 | {
44 | var sequence = Sequence.Range(0, 5);
45 | var index = sequence.IndexOf(2, from: 3);
46 |
47 | Assert.Equal(-1, index);
48 | }
49 |
50 | [Fact]
51 | public void IndexOf_WithCount_Returns_MinusOne_When_ElementIsNotWithinRange()
52 | {
53 | var sequence = Sequence.Range(0, 5);
54 | var index = sequence.IndexOf(3, from: 0, count: 3);
55 |
56 | Assert.Equal(-1, index);
57 | }
58 |
59 | [Fact]
60 | public void IndexWhere_Returns_IndexOfElement()
61 | {
62 | var sequence = Sequence.Range(0, 5);
63 | var index = sequence.IndexWhere(i => i == 3);
64 |
65 | Assert.Equal(3, index);
66 | }
67 |
68 | [Fact]
69 | public void IndexWhere_Returns_MinusOne_When_ElementIsNotFound()
70 | {
71 | var sequence = Sequence.Range(0, 5);
72 | var index = sequence.IndexWhere(i => i == 5);
73 |
74 | Assert.Equal(-1, index);
75 | }
76 |
77 | [Fact]
78 | public void IndexWhere_Returns_MinusOne_When_ElementIsBeforeFrom()
79 | {
80 | var sequence = Sequence.Range(0, 5);
81 | var index = sequence.IndexWhere(i => i == 2, from: 3);
82 |
83 | Assert.Equal(-1, index);
84 | }
85 |
86 | [Fact]
87 | public void IndexWhere_WithCount_Returns_MinusOne_When_ElementIsNotWithinRange()
88 | {
89 | var sequence = Sequence.Range(0, 5);
90 | var index = sequence.IndexWhere(i => i == 3, from: 0, count: 3);
91 |
92 | Assert.Equal(-1, index);
93 | }
94 |
95 | [Fact]
96 | public void LastIndexOf_Returns_IndexOfElement()
97 | {
98 | var sequence = Sequence.With(1, 1, 2, 2, 3, 3);
99 | var index = sequence.LastIndexOf(2);
100 |
101 | Assert.Equal(3, index);
102 | }
103 |
104 | [Fact]
105 | public void LastIndexOf_Returns_MinusOne_When_ElementIsNotFound()
106 | {
107 | var sequence = Sequence.Range(0, 5);
108 | var index = sequence.LastIndexOf(5);
109 |
110 | Assert.Equal(-1, index);
111 | }
112 |
113 | [Fact]
114 | public void LastIndexOf_Returns_MinusOne_When_ElementIsAfterEnd()
115 | {
116 | var sequence = Sequence.Range(0, 5);
117 | var index = sequence.LastIndexOf(4, end: 3);
118 |
119 | Assert.Equal(-1, index);
120 | }
121 |
122 | [Fact]
123 | public void LastIndexOf_WithCount_Returns_MinusOne_When_ElementIsNotWithinRange()
124 | {
125 | var sequence = Sequence.Range(0, 5);
126 | var index = sequence.LastIndexOf(1, end: 3, count: 2);
127 |
128 | Assert.Equal(-1, index);
129 | }
130 |
131 | [Fact]
132 | public void LastIndexWhere_Returns_IndexOfElement()
133 | {
134 | var sequence = Sequence.With(1, 1, 2, 2, 3, 3);
135 | var index = sequence.LastIndexWhere(i => i == 2);
136 |
137 | Assert.Equal(3, index);
138 | }
139 |
140 | [Fact]
141 | public void LastIndexWhere_Returns_MinusOne_When_ElementIsNotFound()
142 | {
143 | var sequence = Sequence.Range(0, 5);
144 | var index = sequence.LastIndexWhere(i => i == 5);
145 |
146 | Assert.Equal(-1, index);
147 | }
148 |
149 | [Fact]
150 | public void LastIndexWhere_Returns_MinusOne_When_ElementIsAfterEnd()
151 | {
152 | var sequence = Sequence.Range(0, 5);
153 | var index = sequence.LastIndexWhere(i => i == 4, end: 3);
154 |
155 | Assert.Equal(-1, index);
156 | }
157 |
158 | [Fact]
159 | public void LastIndexWhere_WithCount_Returns_MinusOne_When_ElementIsNotWithinRange()
160 | {
161 | var sequence = Sequence.Range(0, 5);
162 | var index = sequence.LastIndexWhere(i => i == 1, end: 3, count: 2);
163 |
164 | Assert.Equal(-1, index);
165 | }
166 |
167 | [Fact]
168 | public void Indexer_Returns_ElementAtIndex()
169 | {
170 | var sequence = Sequence.Range(0, 5);
171 | var element = sequence[3];
172 |
173 | Assert.Equal(3, element);
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/docs/Sequence.Docs/Sequence.Docs.shfbproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 | Debug
7 | AnyCPU
8 | 2.0
9 | 7807882d-4578-40a2-9e24-970e1e992ec5
10 | 1.9.9.0
11 |
12 | Sequence.Docs
13 | Sequence.Docs
14 | Sequence.Docs
15 |
16 | .NET Portable Library 4.0 %28Legacy%29
17 | ..\Help\
18 | Sequence.Docs
19 | en-US
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | OnlyWarningsAndErrors
30 | Website
31 | False
32 | True
33 | False
34 | False
35 | True
36 |
37 |
38 |
39 | 2
40 | False
41 | Standard
42 | Blank
43 | False
44 | VS2013
45 | False
46 | Guid
47 | A Sandcastle Documented Class Library
48 | AboveNamespaces
49 | Attributes, InheritedMembers, InheritedFrameworkMembers, Protected, ProtectedInternalAsProtected
50 | diogo.filipe.acastro%40gmail.com
51 | Msdn
52 | Msdn
53 | False
54 | True
55 | ..\help\working\
56 |
57 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | ..\help\
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | pascals-triangle
100 | pascals-triangle
101 |
102 |
103 |
104 |
105 | Sequences
106 | {6c9f9e66-e026-4966-aeb2-9b29b5f0df85}
107 | True
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/FactoryMethodsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Numerics;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Moq;
8 | using Xunit;
9 |
10 | namespace Sequences.Tests
11 | {
12 | public class FactoryMethodsTests
13 | {
14 | [Fact]
15 | public void With_WrapsEnumerable()
16 | {
17 | IEnumerable enumerable = new[] {1, 2, 3};
18 | Assert.Equal(enumerable, Sequence.With(enumerable));
19 | }
20 |
21 | [Fact]
22 | public void With_WrapsArray()
23 | {
24 | int[] array = {1, 2, 3};
25 | Assert.Equal(array, Sequence.With(array));
26 | }
27 |
28 | [Fact]
29 | public void Iterate_RepeatedlyAppliesFunction()
30 | {
31 | var sequence = Sequence.Iterate(1, i => i*2);
32 | int[] expectedSequence = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512};
33 |
34 | Assert.Equal(expectedSequence, sequence.Take(10));
35 | }
36 |
37 | [Fact]
38 | public void Iterate_HasGivenLength()
39 | {
40 | var sequence = Sequence.Iterate(1, 10, i => i*2);
41 | int[] expectedSequence = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512};
42 |
43 | Assert.Equal(10, sequence.Count);
44 | Assert.Equal(expectedSequence, sequence);
45 | }
46 |
47 | [Fact]
48 | public void Tabulate_AppliesFuncToRangeOfIntegers()
49 | {
50 | var sequence = Sequence.Tabulate(5, i => i*2);
51 | int[] expectedSequence = {0, 2, 4, 6, 8};
52 |
53 | Assert.Equal(expectedSequence, sequence);
54 | }
55 |
56 | [Fact]
57 | public void Fill_RepeatsElement()
58 | {
59 | Assert.Equal(new[] {1, 1, 1}, Sequence.Fill(1, 3));
60 | }
61 |
62 | [Fact]
63 | public void Fill_ContinuouslyEvaluatesDelegate()
64 | {
65 | //Arrange
66 | var elemFuncMock = new Mock>();
67 | elemFuncMock.Setup(tail => tail()).Returns(1);
68 |
69 | var sequence = Sequence.Fill(elemFuncMock.Object, 3);
70 |
71 | //realize sequence
72 | var list = sequence.ToList();
73 |
74 | //Assert
75 | elemFuncMock.Verify(f => f(), Times.Exactly(3));
76 | }
77 |
78 | [Fact]
79 | public void Continually_ContinuouslyRepeatsElement()
80 | {
81 | var sequence = Sequence.Continually(1);
82 |
83 | Assert.True(sequence
84 | .Take(10)
85 | .All(i => i == 1));
86 | }
87 |
88 | [Fact]
89 | public void Continually_ContinuouslyEvaluatesDelegate()
90 | {
91 | //Arrange
92 | var elemFuncMock = new Mock>();
93 | elemFuncMock.Setup(tail => tail()).Returns(1);
94 |
95 | var sequence = Sequence.Continually(elemFuncMock.Object);
96 |
97 | //realize sequence
98 | var list = sequence.Take(10).ToList();
99 |
100 | //Assert
101 | elemFuncMock.Verify(f => f(), Times.AtLeast(10));
102 | }
103 |
104 | [Fact]
105 | public void FromInt_Generates_ConsecutiveIntegers()
106 | {
107 | int[] expected = {1, 2, 3, 4, 5};
108 |
109 | var intSequence = Sequence.From(1);
110 | var longSequence = Sequence.From(1L);
111 | var bigIntSequence = Sequence.From((BigInteger) 1);
112 |
113 | Assert.Equal(expected, intSequence.Take(5));
114 | Assert.Equal(expected.Select(i => (long) i), longSequence.Take(5));
115 | Assert.Equal(expected.Select(i => (BigInteger) i), bigIntSequence.Take(5));
116 | }
117 |
118 | [Fact]
119 | public void FromInt_WithStep_Generates_IncreasingSequence()
120 | {
121 | int[] expected = {0, 5, 10, 15, 20};
122 |
123 | var intSequence = Sequence.From(0, 5);
124 | var longSequence = Sequence.From(0L, 5);
125 | var bigIntSequence = Sequence.From((BigInteger) 0, 5);
126 |
127 | Assert.Equal(expected, intSequence.Take(5));
128 | Assert.Equal(expected.Select(i => (long) i), longSequence.Take(5));
129 | Assert.Equal(expected.Select(i => (BigInteger) i), bigIntSequence.Take(5));
130 | }
131 |
132 | [Fact]
133 | public void Range_Generates_FiniteSequence()
134 | {
135 | int[] expected = {0, 5, 10, 15, 20};
136 |
137 | var intSequence = Sequence.Range(0, 25, 5);
138 | var longSequence = Sequence.Range(0L, 25L, 5L);
139 | var bigIntSequence = Sequence.Range((BigInteger) 0, 25, 5);
140 |
141 | Assert.Equal(expected, intSequence);
142 | Assert.Equal(expected.Select(i => (long) i), longSequence);
143 | Assert.Equal(expected.Select(i => (BigInteger) i), bigIntSequence);
144 | }
145 |
146 | [Fact]
147 | public void Range_Excludes_UpperBound()
148 | {
149 | Assert.DoesNotContain(25, Sequence.Range(0, 25, 5));
150 | Assert.DoesNotContain(25, Sequence.Range(0L, 25, 5));
151 | Assert.DoesNotContain(25, Sequence.Range((BigInteger) 0, 25, 5));
152 | }
153 |
154 | [Fact]
155 | public void Range_WithNegativeStep_Excludes_UpperBound()
156 | {
157 | Assert.DoesNotContain(0, Sequence.Range(25, 0, -5));
158 | Assert.DoesNotContain(0, Sequence.Range(25L, 0, -5));
159 | Assert.DoesNotContain(0, Sequence.Range((BigInteger) 25, 0, -5));
160 | }
161 |
162 | [Fact]
163 | public void Range_When_StartEqualsEnd_Generates_EmptySequence()
164 | {
165 | Assert.Empty(Sequence.Range(0, 0, 0));
166 | Assert.Empty(Sequence.Range(0L, 0, 0));
167 | Assert.Empty(Sequence.Range((BigInteger) 0, 0, 0));
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Functional/Sequences.Tests.Functional.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {C0460AD0-BE50-4A91-BBDF-BDBA5F2634C6}
7 | Library
8 | Properties
9 | Sequences.Tests.Functional
10 | Sequences.Tests.Functional
11 | v4.5
12 | 512
13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 10.0
15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
17 | False
18 | UnitTest
19 | ..\
20 | true
21 |
22 |
23 |
24 | true
25 | full
26 | false
27 | bin\Debug\
28 | DEBUG;TRACE
29 | prompt
30 | 4
31 |
32 |
33 | pdbonly
34 | true
35 | bin\Release\
36 | TRACE
37 | prompt
38 | 4
39 |
40 |
41 | bin\Docs\
42 | TRACE
43 | true
44 | pdbonly
45 | AnyCPU
46 | prompt
47 | MinimumRecommendedRules.ruleset
48 |
49 |
50 |
51 |
52 |
53 | ..\..\packages\xunit.1.9.2\lib\net20\xunit.dll
54 |
55 |
56 | ..\..\packages\xunit.extensions.1.9.2\lib\net20\xunit.extensions.dll
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | {6c9f9e66-e026-4966-aeb2-9b29b5f0df85}
80 | Sequences
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | False
91 |
92 |
93 | False
94 |
95 |
96 | False
97 |
98 |
99 | False
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
118 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/SequenceBuilderTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Moq;
7 | using Xunit;
8 |
9 | namespace Sequences.Tests
10 | {
11 | public class SequenceBuilderTests
12 | {
13 | [Fact]
14 | public void ResultSequence_IncludesAppendedCollections()
15 | {
16 | var collection1 = new List {1, 2, 3};
17 | var collection2 = Sequence.Range(4, 7);
18 |
19 | var builder = Sequence.NewBuilder()
20 | .Append(collection1)
21 | .Append(collection2);
22 |
23 | Assert.Equal(new[] {1, 2, 3, 4, 5, 6}, builder.ToSequence());
24 | }
25 |
26 | [Fact]
27 | public void ResultSequence_IncludesAppendedLazyCollections()
28 | {
29 | var collection1 = new List {1, 2, 3};
30 | var collection2 = Sequence.Range(4, 7);
31 |
32 | var builder = Sequence.NewBuilder()
33 | .Append(() => collection1)
34 | .Append(() => collection2);
35 |
36 | Assert.Equal(new[] {1, 2, 3, 4, 5, 6}, builder.ToSequence());
37 | }
38 |
39 | [Fact]
40 | public void ResultSequence_IncludesAppendedElements()
41 | {
42 | var builder = Sequence.NewBuilder()
43 | .Append(1)
44 | .Append(2, 3);
45 |
46 | Assert.Equal(new[] {1, 2, 3}, builder.ToSequence());
47 | }
48 |
49 | [Fact]
50 | public void ResultSequence_IncludesAppendedLazyElements()
51 | {
52 | var builder = Sequence.NewBuilder()
53 | .Append(() => 1);
54 |
55 | Assert.Equal(new[] {1}, builder.ToSequence());
56 | }
57 |
58 | [Fact]
59 | public void ResultSequence_LazilyEvaluates_AppendedCollectionsContents()
60 | {
61 | var inputSequence = Sequence.Range(4, 7);
62 |
63 | var builder = Sequence.NewBuilder()
64 | .Append(inputSequence);
65 |
66 | var resultSequence = builder.ToSequence();
67 |
68 | //assert that the input sequence hasn't been fully realized yet
69 | Assert.False(inputSequence.IsTailDefined);
70 | }
71 |
72 | [Fact]
73 | public void ResultSequence_LazilyEvaluates_AppendedCollectionsAndElements()
74 | {
75 | //setup mocks for lazy elements/colection
76 | var lazyElemMock1 = new Mock>();
77 | lazyElemMock1.Setup(elem => elem()).Returns(1);
78 |
79 | var lazyElemMock2 = new Mock>();
80 | lazyElemMock2.Setup(elem => elem()).Returns(2);
81 |
82 | var lazyCollectionMock = new Mock>>();
83 | lazyCollectionMock.Setup(xs => xs()).Returns(new[] {3, 4, 5});
84 |
85 | var builder = Sequence.NewBuilder()
86 | .Append(lazyElemMock1.Object)
87 | .Append(lazyElemMock2.Object)
88 | .Append(lazyCollectionMock.Object);
89 |
90 | var iter = builder.ToSequence().GetEnumerator();
91 |
92 | //head is eagerly evaluated - all other members shouldn't have been evaluated yet.
93 | lazyElemMock1.Verify(e => e(), Times.Once);
94 | lazyElemMock2.Verify(e => e(), Times.Never);
95 | lazyCollectionMock.Verify(xs => xs(), Times.Never);
96 |
97 | //move to the 1st element
98 | iter.MoveNext();
99 |
100 | //state shouldn't have changed
101 | lazyElemMock1.Verify(e => e(), Times.Once);
102 | lazyElemMock2.Verify(e => e(), Times.Never);
103 | lazyCollectionMock.Verify(xs => xs(), Times.Never);
104 |
105 | //move to the 2nd element
106 | iter.MoveNext();
107 |
108 | //lazyElemMock2 should have been evaluated by now
109 | lazyElemMock1.Verify(e => e(), Times.Once);
110 | lazyElemMock2.Verify(e => e(), Times.Once);
111 | lazyCollectionMock.Verify(xs => xs(), Times.Never);
112 |
113 | //move to the 3rd element
114 | iter.MoveNext();
115 |
116 | //lazyCollectionMock should have been evaluated by now
117 | lazyElemMock1.Verify(e => e(), Times.Once);
118 | lazyElemMock2.Verify(e => e(), Times.Once);
119 | lazyCollectionMock.Verify(xs => xs(), Times.Once);
120 | }
121 |
122 | [Fact]
123 | public void ResultSequence_IgnoresFurtherClears()
124 | {
125 | var builder = Sequence.NewBuilder()
126 | .Append(1, 2, 3);
127 |
128 | var sequence = builder.ToSequence();
129 |
130 | //clear
131 | builder.Clear();
132 |
133 | Assert.Equal(new[] {1, 2, 3}, sequence);
134 | }
135 |
136 | [Fact]
137 | public void ResultSequence_IgnoresFurtherAppends()
138 | {
139 | var builder = Sequence.NewBuilder()
140 | .Append(1, 2, 3);
141 |
142 | var sequence = builder.ToSequence();
143 |
144 | //append more elements
145 | builder.Append(9);
146 |
147 | Assert.DoesNotContain(9, sequence);
148 | }
149 |
150 | [Fact]
151 | public void Clear_ClearsBuffer()
152 | {
153 | var builder = Sequence.NewBuilder()
154 | .Append(1)
155 | .Clear();
156 |
157 | Assert.Empty(builder.ToSequence());
158 | }
159 |
160 | [Fact]
161 | public void AppendParams_ThrowsException_When_InputIsNull()
162 | {
163 | Assert.Throws(() => Sequence.NewBuilder().Append(null as int[]));
164 | }
165 |
166 | [Fact]
167 | public void AppendEnumerable_ThrowsException_When_InputIsNull()
168 | {
169 | Assert.Throws(() => Sequence.NewBuilder().Append(null as IEnumerable));
170 | }
171 |
172 | [Fact]
173 | public void OperatorAdd_AppendsElement()
174 | {
175 | var builder = Sequence.NewBuilder() + 1 + 2 + 3;
176 |
177 | Assert.Equal(new[] {1, 2, 3}, builder.ToSequence());
178 | }
179 |
180 | [Fact]
181 | public void OperatorAdd_AppendsCollection()
182 | {
183 | var builder = Sequence.NewBuilder() +
184 | Sequence.With(1) +
185 | Sequence.With(2) +
186 | Sequence.With(3);
187 |
188 |
189 | Assert.Equal(new[] {1, 2, 3}, builder.ToSequence());
190 | }
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/Sequences.Tests.Unit.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {97B797FB-B2D8-414B-9459-71EB27113D60}
7 | Library
8 | Properties
9 | Sequences.Tests
10 | Sequences.Tests
11 | v4.5
12 | 512
13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 10.0
15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
17 | False
18 | UnitTest
19 | ..\..\
20 | true
21 |
22 |
23 | true
24 | full
25 | false
26 | bin\Debug\
27 | DEBUG;TRACE
28 | prompt
29 | 4
30 |
31 |
32 | pdbonly
33 | true
34 | bin\Release\
35 | TRACE
36 | prompt
37 | 4
38 |
39 |
40 | bin\Docs\
41 | TRACE
42 | true
43 | pdbonly
44 | AnyCPU
45 | prompt
46 | MinimumRecommendedRules.ruleset
47 |
48 |
49 |
50 | ..\..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll
51 |
52 |
53 |
54 |
55 | False
56 | ..\..\packages\xunit.1.9.2\lib\net20\xunit.dll
57 |
58 |
59 | False
60 | ..\..\packages\xunit.extensions.1.9.2\lib\net20\xunit.extensions.dll
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | {6c9f9e66-e026-4966-aeb2-9b29b5f0df85}
106 | Sequences
107 |
108 |
109 |
110 |
111 |
112 |
113 | False
114 |
115 |
116 | False
117 |
118 |
119 | False
120 |
121 |
122 | False
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
141 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sequences
2 |
3 | Sequences is a port of Scala's [`Stream[+A]`][3] to C#.
4 |
5 | A `Sequence` is an immutable lazy list whose elements are only evaluated when they are needed. A sequence is composed by a *head* (the first element) and a lazily-evaluated *tail* (the remaining elements).
6 |
7 | The fact that the tail is lazily-evaluated, makes it easy to represent infinite series or sets. For example, here's how to represent the set of all natural numbers.
8 |
9 |
10 | ```cs
11 | public ISequence Naturals(int start)
12 | {
13 | return new Sequence( head: start,
14 | tail: () => Naturals(start + 1));
15 | }
16 |
17 | var naturals = Naturals(1);
18 |
19 | //take the first 5 natural numbers
20 | naturals.Take(5).ForEach(Console.Write); //prints 12345
21 | ```
22 |
23 | Or, even simpler:
24 |
25 | ```cs
26 | var naturals = Sequence.From(1);
27 | ```
28 |
29 | Sequences also features memoization, i.e., the sequence stores previously computed values to avoid re-evaluation.
30 |
31 | ```cs
32 | //start with number 1, and then keep adding 2 to the previous number
33 | var odds = Sequence.Iterate(1, odd =>
34 | {
35 | Console.WriteLine("Adding " + odd + " + 2");
36 | return odd + 2;
37 | });
38 |
39 | odds.Take(3).ForEach(Console.WriteLine);
40 | odds.Take(5).ForEach(Console.WriteLine);
41 |
42 | //prints
43 | //1
44 | //Adding 1 + 2
45 | //3
46 | //Adding 3 + 2
47 | //5
48 |
49 | //and then
50 | //1
51 | //3
52 | //5
53 | //Adding 5 + 2
54 | //7
55 | //Adding 7 + 2
56 | //9
57 | ```
58 |
59 | You can iterate through an infinite sequence for as long as you want. As long as you don't hold onto its head, each sequence will be elected for garbage collection as soon as you move to the next value. This prevents an infinite sequence from occupying a large and growing ammount of memory.
60 |
61 | ```cs
62 | foreach (var odd in Sequence.Iterate(1, odd => odd + 2))
63 | {
64 | //when you move to Sequence(11, ?),
65 | //the previous Sequence(9, ?) is elected for collection.
66 | }
67 | ```
68 |
69 | ## Examples
70 |
71 | The above natural numbers example is very simple. But Sequences allow for so much more. So let's explore some more complex examples.
72 |
73 | #### Fibonacci sequence
74 |
75 | The Fibonacci sequence is a famous series in mathematics, where each fibonacci number is defined as the sum of the two previous fibonacci numbers, i.e. `F(n) = F(n-1) + F(n-2)`, with seed values `F(0) = 0` and `F(1) = 1`.
76 |
77 | In scala, the fibonacci sequence is commonly expressed as follows:
78 |
79 | ```scala
80 | val fibs: Stream[Int] = 0 #:: 1 #:: fibs.zip(fibs.tail).map { n => n._1 + n._2 }
81 | ```
82 |
83 | In C#, the syntax is a little more verbose, but still readable:
84 |
85 | ```cs
86 | Func, int> sum = pair => pair.Item1 + pair.Item2;
87 |
88 | ISequence fibs = null;
89 |
90 | fibs = Sequence.With(0, 1) //start with (0, 1, ?)
91 | .Concat(() => //and then
92 | fibs.Zip(fibs.Tail) //zip the sequence with its tail (i.e., (0,1), (1,1), (1,2), (2,3), (3, 5))
93 | .Select(sum)); //select the sum of each pair (i.e., 1, 2, 3, 5, 8)
94 | ```
95 |
96 | The code above creates more objects than needed. The following implementation shows a more efficient way of representing the fibonacci sequence:
97 |
98 | ```cs
99 | using System.Numerics;
100 |
101 | //current and next are any two consecutive fibonacci numbers.
102 | ISequence Fibs(BigInteger current, BigInteger next)
103 | {
104 | return new Sequence(current, () => Fibs(next, current + next));
105 | }
106 |
107 | var fibs = Fibs(0, 1);
108 |
109 | //prints 0 1 1 2 3 5 8 13 21 34
110 | fibs.Take(10).ForEach(Console.WriteLine);
111 | ```
112 |
113 | #### Prime numbers
114 |
115 | One way to find every prime number in a given range is to use the [Sieve of Eratosthenes][4].
116 | To find the prime numbers up to 100, a slight variation of the sieve goes like this:
117 |
118 | 1. Start with a list representing the range [2, 100].
119 | 2. Let *p* be the head of the list.
120 | 3. Take *p* as the next prime number, and remove every multiple of *p* from the list.
121 | 4. If the list is empty:
122 | * stop;
123 | * otherwise, repeat from step 2.
124 |
125 | Here's a way of implementing the sieve as a sequence.
126 |
127 | ```cs
128 | var range = Sequence.Range(2, 101);
129 | var primes = PrimesWithin(range);
130 |
131 | //prints: 2 3 5 7 11
132 | Console.WriteLine(primes.Take(5).MkString(" "));
133 |
134 | public ISequence PrimesWithin(ISequence range)
135 | {
136 | if (range.IsEmpty)
137 | return Sequence.Empty();
138 |
139 | //take the next prime number
140 | var p = range.Head;
141 |
142 | //skip p, and remove further multiples of p
143 | var filtered = range.Tail.Where(num => num % p != 0).Force();
144 |
145 | return new Sequence(p, () => PrimesWithin(filtered));
146 | }
147 | ```
148 |
149 | #### Pascal's Triangle
150 |
151 | Everyone knows the famous [Pascal's Triangle][6].
152 |
153 | ![Pascal's Triangle][pascal]
154 |
155 | The triangle starts with a 1 at the top. In every other row, each number is the sum of the two directly above it.
156 |
157 | There are all sorts of ways of representing Pascal's triangle using sequences, but here's an interesting one:
158 |
159 | ```cs
160 | Func, int> sum = pair => pair.Item1 + pair.Item2;
161 |
162 | Func, ISequence> rowFactory =
163 | row => row.Append(0) //shift row to the left
164 | .Zip(row.Prepend(0)) //shift row to the right, and zip both shifted rows
165 | .Select(sum); //sum the two shifted rows
166 |
167 | var triangle = Sequence.Iterate(
168 | start: Sequence.With(1),
169 | func: rowFactory);
170 | ```
171 |
172 | You start with row (1). From then on, every row is computed by shifting the row to the right, shifting the row to the left, zipping both shifted rows together and producing the sum of each tuple. For example, given the row (1, 3, 3, 1):
173 |
174 | ```
175 | 0 1 3 3 1 //shift right
176 | 1 3 3 1 0 //shift left
177 | ↓ ↓ ↓ ↓ ↓
178 | 1 4 6 4 1
179 | ```
180 |
181 | For more examples, refer to the [functional tests project][1].
182 |
183 | ## Limitations
184 |
185 | Due to a limitation in [generic type constraints][5] as of C# 5.0, sequences are *not* covariant, as opposed to Scala's `Stream[+A]`.
186 |
187 | Take the signature of `copyToBuffer` in Scala as an example:
188 |
189 | ```scala
190 | copyToBuffer[B >: A](dest: Buffer[B]): Unit
191 | ```
192 |
193 | The constraint `B >: A` (read *A derives from B*) cannot be expressed in C# (even though the opposite can be expressed as `where B : A` or *B derives from A*) unless `A` is also one of *copyToBuffer*'s type parameters - which it isn't.
194 |
195 | ## Documentation
196 | Documentation is available at [dcastro.github.io/Sequences][2].
197 |
198 | ## NuGet
199 | To install [Sequences][7], run the following command in the Package Manager Console
200 |
201 | ```
202 | PM> Install-Package Sequences
203 | ```
204 |
205 | [1]: https://github.com/dcastro/Sequences/tree/master/tests/Sequences.Tests.Functional
206 | [2]: http://diogocastro.com/Sequences
207 | [3]: http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Stream
208 | [4]: http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
209 | [5]: http://msdn.microsoft.com/en-gb/library/d5x73970.aspx
210 | [6]: http://en.wikipedia.org/wiki/Pascal's_triangle
211 | [7]: https://www.nuget.org/packages/Sequences/
212 | [pascal]: https://raw.githubusercontent.com/dcastro/Sequences/master/docs/Sequence.Docs/Media/pascals-triangle.png
213 |
--------------------------------------------------------------------------------
/.nuget/NuGet.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)\..\
5 |
6 |
7 | false
8 |
9 |
10 | false
11 |
12 |
13 | true
14 |
15 |
16 | false
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
31 |
32 |
33 |
34 |
35 | $(SolutionDir).nuget
36 |
37 |
38 |
39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config
40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config
41 |
42 |
43 |
44 | $(MSBuildProjectDirectory)\packages.config
45 | $(PackagesProjectConfig)
46 |
47 |
48 |
49 |
50 | $(NuGetToolsPath)\NuGet.exe
51 | @(PackageSource)
52 |
53 | "$(NuGetExePath)"
54 | mono --runtime=v4.0.30319 $(NuGetExePath)
55 |
56 | $(TargetDir.Trim('\\'))
57 |
58 | -RequireConsent
59 | -NonInteractive
60 |
61 | "$(SolutionDir) "
62 | "$(SolutionDir)"
63 |
64 |
65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
67 |
68 |
69 |
70 | RestorePackages;
71 | $(BuildDependsOn);
72 |
73 |
74 |
75 |
76 | $(BuildDependsOn);
77 | BuildPackage;
78 |
79 |
80 |
81 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
99 |
100 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/tests/Sequences.Tests.Unit/EnumerableTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Moq;
8 | using Xunit;
9 |
10 | namespace Sequences.Tests
11 | {
12 | public class EnumerableTests
13 | {
14 | [Fact]
15 | public void Enumerator_IteratesThroughElements()
16 | {
17 | var sequence = new Sequence(1, () =>
18 | new Sequence(2, () =>
19 | new Sequence(3, Sequence.Empty)));
20 |
21 | Assert.Equal(new[] {1, 2, 3}, sequence);
22 | }
23 |
24 | [Fact]
25 | public void ForEach_AppliesFunctionToEachElement()
26 | {
27 | var funcMock = new Mock>();
28 | var sequence = Sequence.Range(1, 4);
29 |
30 | sequence.ForEach(funcMock.Object);
31 |
32 | funcMock.Verify(f => f(It.IsAny()), Times.Exactly(3));
33 |
34 | funcMock.Verify(f => f(1), Times.Exactly(1));
35 | funcMock.Verify(f => f(2), Times.Exactly(1));
36 | funcMock.Verify(f => f(3), Times.Exactly(1));
37 | }
38 |
39 | [Fact]
40 | public void Enumerator_AllowsGCToCollectSequences()
41 | {
42 | var seqReference = new WeakReference>(
43 | Sequence.From(1));
44 |
45 | ISequence temp;
46 | Assert.True(seqReference.TryGetTarget(out temp));
47 |
48 | IEnumerator iter = temp.GetEnumerator();
49 | temp = null;
50 |
51 | iter.MoveNext(); //move to the original sequence - Sequence(1, ?)
52 | iter.MoveNext(); //move to the second sequence - Sequence(2, ?)
53 |
54 | //collect
55 | GC.Collect();
56 |
57 | //the iterator should no longer hold a reference to the original Sequence(1, ?)
58 | //and, so, the GC should have been able to clear the sequence the WeakReference points to.
59 | Assert.False(seqReference.TryGetTarget(out temp));
60 |
61 | /**
62 | * Keep the iterator alive.
63 | * If the iterator was collected, the above assertion could be a "false positive" - the weak reference could have been collected
64 | * not because the iterator allows it, but because the iterator was *also* collected.
65 | */
66 | GC.KeepAlive(iter);
67 | }
68 |
69 | [Fact]
70 | public void SlidingEnumerator_AllowsGCToCollectSequences()
71 | {
72 | var seqReference = new WeakReference>(
73 | Sequence.From(1));
74 |
75 | ISequence temp;
76 | Assert.True(seqReference.TryGetTarget(out temp));
77 |
78 | IEnumerator> iter = temp.Sliding(2).GetEnumerator();
79 | temp = null;
80 |
81 | iter.MoveNext(); //move to the first sliding window - Sequence(1, 2)
82 | iter.MoveNext(); //move to the second sliding window - Sequence(2, 3)
83 |
84 | //collect
85 | GC.Collect();
86 |
87 | //the iterator should no longer hold a reference to the original Sequence(1, ?)
88 | //and, so, the GC should have been able to clear the sequence the WeakReference points to.
89 | Assert.False(seqReference.TryGetTarget(out temp));
90 |
91 | /**
92 | * Keep the iterator alive.
93 | * If the iterator was collected, the above assertion could be a "false positive" - the weak reference could have been collected
94 | * not because the iterator allows it, but because the iterator was *also* collected.
95 | */
96 | GC.KeepAlive(iter);
97 | }
98 |
99 | [Fact]
100 | public void GroupedEnumerator_AllowsGCToCollectSequences()
101 | {
102 | var seqReference = new WeakReference>(
103 | Sequence.From(1));
104 |
105 | ISequence temp;
106 | Assert.True(seqReference.TryGetTarget(out temp));
107 |
108 | IEnumerator> iter = temp.Grouped(2).GetEnumerator();
109 | temp = null;
110 |
111 | iter.MoveNext(); //move to the first group - Sequence(1, 2)
112 | iter.MoveNext(); //move to the second group - Sequence(3, 4)
113 |
114 | //collect
115 | GC.Collect();
116 |
117 | //the iterator should no longer hold a reference to the original Sequence(1, ?)
118 | //and, so, the GC should have been able to clear the sequence the WeakReference points to.
119 | Assert.False(seqReference.TryGetTarget(out temp));
120 |
121 | /**
122 | * Keep the iterator alive.
123 | * If the iterator was collected, the above assertion could be a "false positive" - the weak reference could have been collected
124 | * not because the iterator allows it, but because the iterator was *also* collected.
125 | */
126 | GC.KeepAlive(iter);
127 | }
128 |
129 | [Fact]
130 | public void NonEmptyTailsEnumerator_AllowsGCToCollectSequences()
131 | {
132 | var seqReference = new WeakReference>(
133 | Sequence.From(1));
134 |
135 | ISequence temp;
136 | Assert.True(seqReference.TryGetTarget(out temp));
137 |
138 | IEnumerator> iter = temp.NonEmptyTails().GetEnumerator();
139 | temp = null;
140 |
141 | iter.MoveNext(); //move to the sequence itself - Sequence(1, ?)
142 | iter.MoveNext(); //move to its tail - Sequence(2, ?)
143 |
144 | //collect
145 | GC.Collect();
146 |
147 | //the iterator should no longer hold a reference to the original Sequence(1, ?)
148 | //and, so, the GC should have been able to clear the sequence the WeakReference points to.
149 | Assert.False(seqReference.TryGetTarget(out temp));
150 |
151 | /**
152 | * Keep the iterator alive.
153 | * If the iterator was collected, the above assertion could be a "false positive" - the weak reference could have been collected
154 | * not because the iterator allows it, but because the iterator was *also* collected.
155 | */
156 | GC.KeepAlive(iter);
157 | }
158 |
159 | [Fact]
160 | public void TailsEnumerator_AllowsGCToCollectSequences()
161 | {
162 | var seqReference = new WeakReference>(
163 | Sequence.From(1));
164 |
165 | ISequence temp;
166 | Assert.True(seqReference.TryGetTarget(out temp));
167 |
168 | IEnumerator> iter = temp.Tails().GetEnumerator();
169 | temp = null;
170 |
171 | iter.MoveNext(); //move to the sequence itself - Sequence(1, ?)
172 | iter.MoveNext(); //move to its tail - Sequence(2, ?)
173 |
174 | //collect
175 | GC.Collect();
176 |
177 | //the iterator should no longer hold a reference to the original Sequence(1, ?)
178 | //and, so, the GC should have been able to clear the sequence the WeakReference points to.
179 | Assert.False(seqReference.TryGetTarget(out temp));
180 |
181 | /**
182 | * Keep the iterator alive.
183 | * If the iterator was collected, the above assertion could be a "false positive" - the weak reference could have been collected
184 | * not because the iterator allows it, but because the iterator was *also* collected.
185 | */
186 | GC.KeepAlive(iter);
187 | }
188 |
189 | [Fact]
190 | public void IndicesEnumerator_AllowsGCToCollectSequences()
191 | {
192 | var seqReference = new WeakReference>(
193 | Sequence.From(1));
194 |
195 | ISequence temp;
196 | Assert.True(seqReference.TryGetTarget(out temp));
197 |
198 | IEnumerator iter = temp.Indices().GetEnumerator();
199 | temp = null;
200 |
201 | iter.MoveNext();
202 | iter.MoveNext();
203 |
204 | //collect
205 | GC.Collect();
206 |
207 | //the iterator should no longer hold a reference to the original Sequence(1, ?)
208 | //and, so, the GC should have been able to clear the sequence the WeakReference points to.
209 | Assert.False(seqReference.TryGetTarget(out temp));
210 |
211 | /**
212 | * Keep the iterator alive.
213 | * If the iterator was collected, the above assertion could be a "false positive" - the weak reference could have been collected
214 | * not because the iterator allows it, but because the iterator was *also* collected.
215 | */
216 | GC.KeepAlive(iter);
217 | }
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/docs/Sequence.Docs/Content/Examples.aml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
12 |
13 |
14 |
18 |
19 |
20 |
21 | In , we saw how to represent sequences of natural and odd numbers.
22 | In this section, we take a look at a few more complex examples.
23 |
24 |
25 |
26 | For more examples, refer to the
27 | functional tests project
28 | https://github.com/dcastro/Sequences/tree/master/tests/Sequences.Tests.Functional
29 | .
30 |
31 |
32 |
33 |
34 | Fibonacci sequence
35 |
36 |
37 | The Fibonacci sequence is a famous series in mathematics,
38 | where each fibonacci number is defined as the sum of the two previous fibonacci numbers,
39 | i.e. F(n) = F(n-1) + F(n-2), with seed values F(0) = 0 and F(1) = 1.
40 |
41 |
42 |
43 | In scala, the fibonacci sequence is commonly expressed as follows:
44 |
45 |
46 |
47 | n._1 + n._2 }]]>
49 |
50 |
51 |
52 | In C#, the syntax is a little more verbose, but still readable:
53 |
54 |
55 |
56 | , int> sum = pair => pair.Item1 + pair.Item2;
58 |
59 | ISequence fibs = null;
60 |
61 | fibs = Sequence.With(0, 1) //start with (0, 1, ?)
62 | .Concat(() => //and then
63 | fibs.Zip(fibs.Tail) //zip the sequence with its tail (i.e., (0,1), (1,1), (1,2), (2,3), (3, 5))
64 | .Select(sum)); //select the sum of each pair (i.e., 1, 2, 3, 5, 8)]]>
65 |
66 |
67 |
68 | The code above creates more objects than needed. The following implementation shows a more efficient way of representing the fibonacci sequence:
69 |
70 |
71 |
72 | Fibs(BigInteger current, BigInteger next)
77 | {
78 | return new Sequence(current, () => Fibs(next, current + next));
79 | }
80 |
81 | var fibs = Fibs(0, 1);
82 |
83 | //prints 0 1 1 2 3 5 8 13 21 34
84 | fibs.Take(10).ForEach(Console.WriteLine);]]>
85 |
86 |
87 |
88 |
104 |
105 |
106 |
107 | Prime numbers
108 |
109 |
110 | One way to find every prime number in a given range is to use the
111 | Sieve of Eratosthenes
112 | http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
113 | .
114 | To find the prime numbers up to 100, a slight variation of the sieve goes like this:
115 |
116 |
117 |
118 |
119 | Start with a list representing the range [2, 100].
120 |
121 |
122 | Let p be the head of the list.
123 |
124 |
125 | Take p as the next prime number, and remove every multiple of p from the list.
126 |
127 |
128 | If the list is empty:
129 |
130 |
131 | stop;
132 |
133 |
134 | otherwise, repeat from step 2.
135 |
136 |
137 |
138 |
139 |
140 |
141 | Here's a way of implementing the sieve as a sequence.
142 |
143 |
144 |
145 | PrimesWithin(ISequence range)
153 | {
154 | if (range.IsEmpty)
155 | return Sequence.Empty();
156 |
157 | //take the next prime number
158 | var p = range.Head;
159 |
160 | //skip p, and remove further multiples of p
161 | var filtered = range.Tail.Where(num => num % p != 0).Force();
162 |
163 | return new Sequence(p, () => PrimesWithin(filtered));
164 | }]]>
165 |
166 |
167 |
168 |
169 |
170 |
171 | Pascal's Triangle
172 |
173 |
174 | Everyone knows the famous
175 | Pascal's Triangle
176 | http://en.wikipedia.org/wiki/Pascal's_triangle
177 | .
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 | The triangle starts with a 1 at the top. In every other row, each number is the sum of the two directly above it.
186 |
187 |
188 |
189 | There are all sorts of ways of representing Pascal's triangle using sequences, but here's an interesting one:
190 |
191 |
192 |
193 | , int> sum = pair => pair.Item1 + pair.Item2;
195 |
196 | Func, ISequence> rowFactory =
197 | row => row.Append(0) //shift row to the left
198 | .Zip(row.Prepend(0)) //shift row to the right, and zip both shifted rows
199 | .Select(sum); //sum the two shifted rows
200 |
201 | var triangle = Sequence.Iterate(
202 | start: Sequence.With(1),
203 | func: rowFactory);]]>
204 |
205 |
206 |
207 | You start with row (1).
208 | From then on, every row is computed by shifting the row to the right, shifting the row to the left, zipping both shifted rows together and producing the sum of each tuple.
209 | For example, given the row (1, 3, 3, 1):
210 |
211 |
212 |
213 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | T:Sequences.ISequence`1
225 | T:Sequences.Sequence
226 |
227 |
228 |
229 |
--------------------------------------------------------------------------------
/src/Sequences/Sequence`1.Iterators.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | namespace Sequences
9 | {
10 | public partial class Sequence
11 | {
12 |
13 | /**
14 | * An "iterator block" lets the compiler generate an for us.
15 | * However, that enumerator holds onto the sequence that created it, and doesn't let the GC collect it.
16 | *
17 | * These specialized iterators don't do that.
18 | * Instead, when they move to a sequence's tail, they replace the reference to the sequence with a reference to its tail,
19 | * letting GC collect the original sequence.
20 | *
21 | * This lets us, for example, iterate through an infinite sequence for as long as we want, e.g., foreach(var e in Sequence.From(1L)) { }.
22 | * A compiler-generated iterator would keep a reference to the first sequence - Sequence(1, ?) - and, therefore, to all its tails.
23 | * Using these iterators, the GC will be able to collect all intermediate sequences as the loop progresses.
24 | */
25 |
26 | private class TailsIterator : IEnumerator
27 | {
28 | private ISequence _seq;
29 | private readonly Func, TElem> _selector;
30 | private readonly bool _returnEmptyTail;
31 | private bool _hasMoved;
32 | private bool _hasFinished;
33 |
34 | public TailsIterator(ISequence seq, Func, TElem> selector, bool returnEmptyTail)
35 | {
36 | _seq = seq;
37 | _selector = selector;
38 | _returnEmptyTail = returnEmptyTail;
39 | }
40 |
41 | public bool MoveNext()
42 | {
43 | //check if the iterator has reached the end of the sequence
44 | if (_hasFinished)
45 | return false;
46 |
47 | //move to the sequence's tail on every call but the first
48 | if (_hasMoved)
49 | _seq = _seq.Tail;
50 | else
51 | _hasMoved = true;
52 |
53 | //check if the iterator has reached the end of the sequence
54 | if (_seq.IsEmpty)
55 | _hasFinished = true;
56 |
57 | if (_seq.NonEmpty || _returnEmptyTail)
58 | {
59 | Current = _selector(_seq);
60 | return true;
61 | }
62 |
63 | return false;
64 | }
65 |
66 | void IEnumerator.Reset()
67 | {
68 | throw new NotSupportedException();
69 | }
70 |
71 | public TElem Current { get; private set; }
72 |
73 | object IEnumerator.Current
74 | {
75 | get { return Current; }
76 | }
77 |
78 | public void Dispose()
79 | {
80 | }
81 | }
82 |
83 | private class IndexIterator : IEnumerator
84 | {
85 | private readonly IEnumerator _iter;
86 | private int _index;
87 | private bool _hasFinished;
88 |
89 | public IndexIterator(IEnumerable seq)
90 | {
91 | _iter = seq.GetEnumerator();
92 | }
93 |
94 | public bool MoveNext()
95 | {
96 | if (_hasFinished || !_iter.TryMoveNext())
97 | {
98 | _hasFinished = true;
99 | return false;
100 | }
101 |
102 | Current = _index++;
103 | return true;
104 | }
105 |
106 | void IEnumerator.Reset()
107 | {
108 | throw new NotSupportedException();
109 | }
110 |
111 | public int Current { get; private set; }
112 |
113 | object IEnumerator.Current
114 | {
115 | get { return Current; }
116 | }
117 |
118 | public void Dispose()
119 | {
120 | }
121 | }
122 |
123 | private class SlidingIterator : IEnumerator>
124 | {
125 | private ISequence _seq;
126 | private readonly int _size;
127 | private readonly int _step;
128 |
129 | private bool _hasMoved;
130 | private bool _hasMoreElems;
131 |
132 | private readonly List _buffer;
133 |
134 | public SlidingIterator(ISequence seq, int size, int step)
135 | {
136 | _seq = seq;
137 | _size = size;
138 | _step = step;
139 |
140 | _buffer = new List(_size);
141 | _hasMoreElems = _seq.NonEmpty;
142 | }
143 |
144 | public bool MoveNext()
145 | {
146 | //move "_step" elements on every call but the first
147 | if (_hasMoved)
148 | _seq = _seq.Skip(_step);
149 | else
150 | _hasMoved = true;
151 |
152 | //in addition to checking if the previous iterator had more elements,
153 | //we also need to check if the sequence is still not empty after advancing "_step" elements
154 | _hasMoreElems &= _seq.NonEmpty;
155 |
156 | if (!_hasMoreElems)
157 | return false;
158 |
159 | //group elements into a buffer
160 | var iterator = _seq.GetEnumerator();
161 |
162 | for (int i = 0; i < _size && iterator.TryMoveNext(); i++)
163 | _buffer.Add(iterator.Current);
164 |
165 | //force the evaluation of the buffer's contents, before we clear the buffer.
166 | Current = _buffer.AsSequence().Force();
167 | _buffer.Clear();
168 |
169 | //check if there are any more elements
170 | _hasMoreElems = iterator.TryMoveNext();
171 |
172 | return true;
173 | }
174 |
175 | void IEnumerator.Reset()
176 | {
177 | throw new NotSupportedException();
178 | }
179 |
180 | public ISequence Current { get; private set; }
181 |
182 | object IEnumerator.Current
183 | {
184 | get { return Current; }
185 | }
186 |
187 | public void Dispose()
188 | {
189 | }
190 | }
191 |
192 | private class GroupedIterator : IEnumerator>
193 | {
194 | private ISequence _seq;
195 | private readonly int _size;
196 |
197 | private bool _hasMoved;
198 |
199 | public GroupedIterator(ISequence seq, int size)
200 | {
201 | _seq = seq;
202 | _size = size;
203 | }
204 |
205 | public bool MoveNext()
206 | {
207 | if (_hasMoved)
208 | _seq = _seq.Skip(_size);
209 | else
210 | _hasMoved = true;
211 |
212 | if (_seq.IsEmpty)
213 | return false;
214 |
215 | Current = _seq.Take(_size);
216 | return true;
217 | }
218 |
219 | void IEnumerator.Reset()
220 | {
221 | throw new NotSupportedException();
222 | }
223 |
224 | public ISequence Current { get; private set; }
225 |
226 | object IEnumerator.Current
227 | {
228 | get { return Current; }
229 | }
230 |
231 | public void Dispose()
232 | {
233 | }
234 | }
235 |
236 | private class CombinationsEnumerable : IEnumerable>
237 | {
238 | private readonly ISequence _sequence;
239 | private readonly int _size;
240 |
241 | public CombinationsEnumerable(ISequence sequence, int size)
242 | {
243 | _sequence = sequence;
244 | _size = size;
245 | }
246 |
247 | public IEnumerator> GetEnumerator()
248 | {
249 | if (_size == 0)
250 | yield return Sequence.Empty();
251 | else
252 | //combine each distinct element (subsequence.Head)
253 | //with each possible combination of the remaining elements (tailCombination)
254 | foreach (var subSequence in _sequence.NonEmptyTails().Distinct(new CompareByHead()))
255 | foreach (var tailCombination in subSequence.Tail.Combinations(_size - 1))
256 | yield return new Sequence