2009-03-07 2 views
4

Допустим, вы получили некоторые IEnumerable называют S длины N. Я хотел бы, чтобы выбрать все непрерывные подпоследовательности длины п < = N от S.Выберите следующие N элементов в IEnumerable <T>

Если S были, скажем, строка, это будет довольно легко. Существуют (S.Length - n + 1) подпоследовательности длины n. Например, «abcdefg» - это длина (7), поэтому означает, что она имеет (5) подстроки длины (3): «abc», «bcd», «cde», «def», «efg».

Но S может быть любым IEnumerable, поэтому этот маршрут не открыт. Как использовать методы расширения для решения этой проблемы?

ответ

4

F # имеет функцию библиотеки под названием Seq.windowed для этого.

http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/FSharp.Core/Microsoft.FSharp.Collections.Seq.html

// 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) } 
+0

Awesome! Это очень близко к тому, что я хочу. Я посмотрю, смогу ли я перевести это на C#. – sassafrass

+1

Это сложнее, чем необходимо. См. Http://stackoverflow.com/a/12238627/184528 – cdiggins

2

На самом деле вы можете использовать LINQ для решения этой проблемы, например.

var subList = list.Skip(x).Take(y); 

где список является IEnumerable

+0

Я на самом деле не пытался это сделать, но не только возвращает первые y элементов после первых элементов x? Нам нужно будет сделать «Пропустить» несколько раз. Помните, нам нужны * все * непрерывные подпоследовательности, а не только одна из них! :) – sassafrass

+0

да, вам придется перебирать каждую подпоследовательность –

0

Вы можете использовать Выберите расширение, которое обеспечивает индекс, чтобы создать объект, содержащий индекс и значение, а затем разделить индекс с длиной, чтобы поместить их в группы:

var x = values.Select((n, i) => new { Index = i, Value = n }).GroupBy(a => a.Index/3); 
+0

Это не совсем так. Вы действительно просто разбиваете их на группы размером N, не создавая отдельные подпоследовательности. Например, «abcde» с N = 3 должен давать «abc», «bcd» <«cde». Но ваш метод дает «abc», «de». – sassafrass

0
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++; 
    } 
} 
0

Вот новый метод расширения, чтобы делать то, что вы хотите в 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)); 
} 
0

Для будущих читателей.

Вот небольшой пример.

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 для каждого исключения для проверки каждого отдельного исключения был «взят».

И вот цитата из фильма!

Но у меня есть очень специфический набор навыков; У меня есть , приобретенный за очень долгую карьеру. Навыки, которые делают меня кошмаром для таких людей, как вы.