Skip to content

Commit

Permalink
Update documentation for Subsets
Browse files Browse the repository at this point in the history
  • Loading branch information
viceroypenguin committed Nov 14, 2023
1 parent fa62fd2 commit eda144b
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 44 deletions.
13 changes: 13 additions & 0 deletions Docs/SuperLinq.Docs/apidoc/SuperLinq.SuperEnumerable.Subsets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
uid: SuperLinq.SuperEnumerable.Subsets``1(System.Collections.Generic.IEnumerable{``0})
example: [*content]
---
The following code example demonstrates how to get all subsets of a sequence using `Subsets`.
[!code-csharp[](SuperLinq/Subsets/Subsets1.linq#L6-)]

---
uid: SuperLinq.SuperEnumerable.Subsets``1(System.Collections.Generic.IEnumerable{``0},System.Int32)
example: [*content]
---
The following code example demonstrates how get all subsets of a particular length using `Subsets`.
[!code-csharp[](SuperLinq/Subsets/Subsets2.linq#L6-)]
37 changes: 37 additions & 0 deletions Docs/SuperLinq.Docs/apidoc/SuperLinq/Subsets/Subsets1.linq
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Query Kind="Statements">
<NuGetReference>SuperLinq</NuGetReference>
<Namespace>SuperLinq</Namespace>
</Query>

var sequence = Enumerable.Range(0, 4);

// check that sequence starts with a known sequence of values
var result = sequence
.Subsets();

Console.WriteLine(
"[" + Environment.NewLine +
string.Join(
", " + Environment.NewLine,
result.Select(c => " [" + string.Join(", ", c) + "]")) +
Environment.NewLine + "]");

// This code produces the following output:
// [
// [],
// [0],
// [1],
// [2],
// [3],
// [0, 1],
// [0, 2],
// [0, 3],
// [1, 2],
// [1, 3],
// [2, 3],
// [0, 1, 2],
// [0, 1, 3],
// [0, 2, 3],
// [1, 2, 3],
// [0, 1, 2, 3]
// ]
27 changes: 27 additions & 0 deletions Docs/SuperLinq.Docs/apidoc/SuperLinq/Subsets/Subsets2.linq
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Query Kind="Statements">
<NuGetReference>SuperLinq</NuGetReference>
<Namespace>SuperLinq</Namespace>
</Query>

var sequence = Enumerable.Range(0, 4);

// check that sequence starts with a known sequence of values
var result = sequence
.Subsets(2);

Console.WriteLine(
"[" + Environment.NewLine +
string.Join(
", " + Environment.NewLine,
result.Select(c => " [" + string.Join(", ", c) + "]")) +
Environment.NewLine + "]");

// This code produces the following output:
// [
// [0, 1],
// [0, 2],
// [0, 3],
// [1, 2],
// [1, 3],
// [2, 3]
// ]
88 changes: 44 additions & 44 deletions Source/SuperLinq/Subsets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,33 @@ namespace SuperLinq;
public static partial class SuperEnumerable
{
/// <summary>
/// Returns a sequence of <see cref="IList{T}"/> representing all of
/// the subsets of any size that are part of the original sequence. In
/// mathematics, it is equivalent to the <em>power set</em> of a set.
/// Returns a sequence of <see cref="IList{T}"/> representing all of the subsets of any size that are part of
/// the original sequence. In mathematics, it is equivalent to the <em>power set</em> of a set.
/// </summary>
/// <param name="sequence">
/// Sequence for which to produce subsets
/// </param>
/// <typeparam name="T">
/// The type of the elements in the sequence
/// </typeparam>
/// <exception cref="ArgumentNullException">
/// <paramref name="sequence"/> is <see langword="null"/>
/// </exception>
/// <returns>
/// A sequence of lists that represent the all subsets of the original sequence
/// </returns>
/// <remarks>
/// This operator produces all of the subsets of a given sequence. Subsets are returned
/// in increasing cardinality, starting with the empty set and terminating with the
/// entire original sequence.<br/>
/// Subsets are produced in a deferred, streaming manner; however, each subset is returned
/// as a materialized list.<br/>
/// There are 2^N subsets of a given sequence, where N => sequence.Count().
/// <para>
/// This operator produces all of the subsets of a given sequence. Subsets are returned in increasing
/// cardinality, starting with the empty set and terminating with the entire original sequence. Subsets are
/// produced in a deferred, streaming manner; however, each subset is returned as a materialized list. There are
/// 2^N subsets of a given sequence, where <c>N = sequence.Count()</c>.
/// </para>
/// <para>
/// This method is implemented by using deferred execution. However, <paramref name="sequence"/> will be
/// consumed in it's entirety immediately when first element of the returned sequence is consumed.
/// </para>
/// </remarks>
/// <param name="sequence">Sequence for which to produce subsets</param>
/// <typeparam name="T">The type of the elements in the sequence</typeparam>
/// <returns>A sequence of lists that represent the all subsets of the original sequence</returns>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="sequence"/> is <see langword="null"/></exception>

public static IEnumerable<IList<T>> Subsets<T>(this IEnumerable<T> sequence)
{
Guard.IsNotNull(sequence);
Expand Down Expand Up @@ -55,39 +65,35 @@ static IEnumerable<IList<T>> Core(IEnumerable<T> sequence)
}

/// <summary>
/// Returns a sequence of <see cref="IList{T}"/> representing all
/// subsets of a given size that are part of the original sequence. In
/// mathematics, it is equivalent to the <em>combinations</em> or
/// <em>k-subsets</em> of a set.
/// Returns a sequence of <see cref="IList{T}"/> representing all subsets of a given size that are part of the
/// original sequence. In mathematics, it is equivalent to the <em>combinations</em> or <em>k-subsets</em> of a
/// set.
/// </summary>
/// <param name="sequence">Sequence for which to produce subsets</param>
/// <param name="subsetSize">The size of the subsets to produce</param>
/// <typeparam name="T">The type of the elements in the sequence</typeparam>
/// <returns>A sequence of lists that represents of K-sized subsets of the original sequence</returns>
/// <param name="sequence">
/// Sequence for which to produce subsets
/// </param>
/// <param name="subsetSize">
/// The size of the subsets to produce
/// </param>
/// <typeparam name="T">
/// The type of the elements in the sequence
/// </typeparam>
/// <exception cref="ArgumentNullException">
/// Thrown if <paramref name="sequence"/> is <see langword="null"/>
/// <paramref name="sequence"/> is <see langword="null"/>
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <paramref name="subsetSize"/> is less than zero.
/// <paramref name="subsetSize"/> is less than <c>0</c>.
/// </exception>

/// <returns>
/// A sequence of lists that represents of K-sized subsets of the original sequence
/// </returns>
public static IEnumerable<IList<T>> Subsets<T>(this IEnumerable<T> sequence, int subsetSize)
{
Guard.IsNotNull(sequence);
Guard.IsGreaterThanOrEqualTo(subsetSize, 0);

// NOTE: There's an interesting trade-off that we have to make in this operator.
// Ideally, we would throw an exception here if the {subsetSize} parameter is
// greater than the sequence length. Unfortunately, determining the length of a
// sequence is not always possible without enumerating it. Herein lies the rub.
// We want Subsets() to be a deferred operation that only iterates the sequence
// when the caller is ready to consume the results. However, this forces us to
// defer the precondition check on the {subsetSize} upper bound. This can result
// in an exception that is far removed from the point of failure - an unfortunate
// and undesirable outcome.
// At the moment, this operator prioritizes deferred execution over fail-fast
// preconditions. This however, needs to be carefully considered - and perhaps
// may change after further thought and review.
if (sequence.TryGetCollectionCount() is int length)
Guard.IsLessThanOrEqualTo(subsetSize, length);

return new SubsetGenerator<T>(sequence, subsetSize);
}
Expand Down Expand Up @@ -119,13 +125,7 @@ private sealed class SubsetEnumerator : IEnumerator<IList<T>>

public SubsetEnumerator(IList<T> set, int subsetSize)
{
// precondition: subsetSize <= set.Count
if (subsetSize > set.Count)
{
ThrowHelper.ThrowArgumentOutOfRangeException(
nameof(subsetSize),
"Subset size must be <= sequence.Count()");
}
Guard.IsLessThanOrEqualTo(subsetSize, set.Count);

// initialize set arrays...
_set = set;
Expand Down

0 comments on commit eda144b

Please sign in to comment.