假设您有一些称为 S 的长度为 N 的 IEnumerable。我想从 S 中选择所有长度为 n <= N 的连续子序列。
如果 S 是,比如说,一个字符串,这会很容易。有 (S.Length - n + 1) 个长度为 n 的子序列。例如,“abcdefg”的长度为 (7),这意味着它有 (5) 个长度为 (3) 的子字符串:“abc”、“bcd”、“cde”、“def”、“efg”。
但是 S 可以是任何 IEnumerable,所以这条路线是不开放的。如何使用扩展方法来解决这个问题?
假设您有一些称为 S 的长度为 N 的 IEnumerable。我想从 S 中选择所有长度为 n <= N 的连续子序列。
如果 S 是,比如说,一个字符串,这会很容易。有 (S.Length - n + 1) 个长度为 n 的子序列。例如,“abcdefg”的长度为 (7),这意味着它有 (5) 个长度为 (3) 的子字符串:“abc”、“bcd”、“cde”、“def”、“efg”。
但是 S 可以是任何 IEnumerable,所以这条路线是不开放的。如何使用扩展方法来解决这个问题?
F# 为此提供了一个名为 Seq.windowed 的库函数。
// windowed : int -> seq<'a> -> seq<array<'a>>
let windowed n (s: seq<_>) =
if n <= 0 then Helpers.invalid_arg2 "n" "the window size must be positive"
{ let arr = Array.zero_create n
let r = ref (n-1)
let i = ref 0
use e = s.GetEnumerator()
while e.MoveNext() do
do arr.[!i] <- e.Current
do i := (!i + 1) % n
if !r = 0 then
yield Array.init n (fun j -> arr.[(!i+j) % n])
else
do r := (!r - 1) }
实际上,您可以使用 LINQ 来解决这个问题,例如
var subList = list.Skip(x).Take(y);
其中列表是IEnumerable
您可以使用提供索引的 Select 扩展来创建包含索引和值的对象,然后将索引与长度分开以将它们分组:
var x = values.Select((n, i) => new { Index = i, Value = n }).GroupBy(a => a.Index / 3);
IEnumerable<IEnumerable<T>> ContiguousSubseqences<T>(this IEnumerable<T> seq, Func<T,bool> constraint)
{
int i = 0;
foreach (T t in seq)
{
if (constraint(t))
yield return seq.Skip(i).TakeWhile(constraint);
i++;
}
}
这是一个新的扩展方法,可以在 C# 中做你想做的事
static IEnumerable<IEnumerable<T>> Subseqs<T>(this IEnumerable<T> xs, int n)
{
var cnt = xs.Count() - n;
Enumerable.Range(0, cnt < 0 ? 0 : cnt).Select(i => xs.Skip(i).Take(n));
}
对于未来的读者。
这是一个小例子。
private static void RunTakeSkipExample()
{
int takeSize = 10; /* set takeSize to 10 */
/* create 25 exceptions, so 25 / 10 .. means 3 "takes" with sizes of 10, 10 and 5 */
ICollection<ArithmeticException> allArithExceptions = new List<ArithmeticException>();
for (int i = 1; i <= 25; i++)
{
allArithExceptions.Add(new ArithmeticException(Convert.ToString(i)));
}
int counter = 0;
IEnumerable<ArithmeticException> currentTakeArithExceptions = allArithExceptions.Skip(0).Take(takeSize);
while (currentTakeArithExceptions.Any())
{
Console.WriteLine("Taking! TakeSize={0}. Counter={1}. Count={2}.", takeSize, (counter + 1), currentTakeArithExceptions.Count());
foreach (ArithmeticException ae in currentTakeArithExceptions)
{
Console.WriteLine(ae.Message);
}
currentTakeArithExceptions = allArithExceptions.Skip(++counter * takeSize).Take(takeSize);
}
}
输出:
Taking! TakeSize=10. Counter=1. Count=10.
1
2
3
4
5
6
7
8
9
10
Taking! TakeSize=10. Counter=2. Count=10.
11
12
13
14
15
16
17
18
19
20
Taking! TakeSize=10. Counter=3. Count=5.
21
22
23
24
25
您可以通过 .Message 看到每个异常,以验证每个不同的异常是否被“采取”。
现在,电影报价!
但我确实拥有一套非常特殊的技能;我在很长的职业生涯中获得的技能。让我成为像你这样的人的噩梦的技能。