Skip to main content
Code Review

Return to Answer

replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link

EDIT: Modified Chunk function using a predicate. original original

EDIT: Modified Chunk function using a predicate. original

EDIT: Modified Chunk function using a predicate. original

added modified chunk to use predicate.
Source Link
Gareth
  • 301
  • 2
  • 7

EDIT: Modified Chunk function using a predicate. original

 public static IEnumerable<IEnumerable<TValue>> Chunk<TValue>(
 this IEnumerable<TValue> values,
 Func<TValue,bool> pred)
 {
 using (var enumerator = values.GetEnumerator())
 {
 while (enumerator.MoveNext())
 {
 yield return GetChunk(enumerator, pred).ToList();
 }
 }
 }
 private static IEnumerable<T> GetChunk<T>(
 IEnumerator<T> enumerator,
 Func<T,bool> pred)
 {
 do
 {
 yield return enumerator.Current;
 } while (!pred(enumerator.Current) && enumerator.MoveNext());
 }

EDIT: Modified Chunk function using a predicate. original

 public static IEnumerable<IEnumerable<TValue>> Chunk<TValue>(
 this IEnumerable<TValue> values,
 Func<TValue,bool> pred)
 {
 using (var enumerator = values.GetEnumerator())
 {
 while (enumerator.MoveNext())
 {
 yield return GetChunk(enumerator, pred).ToList();
 }
 }
 }
 private static IEnumerable<T> GetChunk<T>(
 IEnumerator<T> enumerator,
 Func<T,bool> pred)
 {
 do
 {
 yield return enumerator.Current;
 } while (!pred(enumerator.Current) && enumerator.MoveNext());
 }
Source Link
Gareth
  • 301
  • 2
  • 7

So I thought I'd try this out with a test enumerable.

public class NumberValues : IEnumerable<int>
{
 public NumberValues(int startValue, int endValue)
 {
 _endValue = endValue;
 _startValue = startValue;
 counter = 0;
 }
 public NumberValues(int endValue) : this(0, 10) { }
 public int counter { get; set; }
 public IEnumerator<int> GetEnumerator()
 {
 var iterator = _startValue;
 while (iterator < _endValue)
 {
 Trace.Write(iterator + ",");
 iterator++;
 counter++;
 yield return iterator;
 }
 }
 IEnumerator IEnumerable.GetEnumerator()
 {
 return GetEnumerator();
 }
}

followed closely by

 [TestCase(0,5)]
 [TestCase(1,5)]
 [TestCase(0,6)]
 [TestCase(1,6)]
 [TestCase(0,10)]
 [TestCase(1,10)]
 public void TestSkipBeforeIf(int start, int end)
 {
 var numberValue = new NumberValues(start,end);
 numberValue.SplitBeforeIf(i => i%2 == 0).ToList();
 }

which results in the following output.

0,5 > 0,0,1,0,1,2,0,1,2,3,0,1,2,3,4,0,1,2,3,4,0,1,2,3,4, - 25
1,5 > 1,1,1,2,1,2,3,1,2,3,4,1,2,3,4,1,2,3,4, - 19
0,6 > 0,0,1,0,1,2,0,1,2,3,0,1,2,3,4,0,1,2,3,4,5,0,1,2,3,4,5, - 27
1,6 > 1,1,1,2,1,2,3,1,2,3,4,1,2,3,4,5,1,2,3,4,5, - 21
0,10 > 0,0,1,0,1,2,0,1,2,3,0,1,2,3,4,0,1,2,3,4,5,0,1,2,3,4,5,6,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,8,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9, - 65
1,10 > 1,1,1,2,1,2,3,1,2,3,4,1,2,3,4,5,1,2,3,4,5,6,1,2,3,4,5,6,7,1,2,3,4,5,6,7,8,1,2,3,,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9, - 55
0,20 > 230
0,40 > 860
0,100 > 5150

But why?

 while (input.Any())
 {
 var temp = input.TakeWhile(v => !pred(v));
 if (predSatis == null || predSatis.Equals(default(T)))
 yield return temp;
 else
 yield return (new List<T> { predSatis }).Concat(temp);
 input = input.SkipWhile(v => !pred(v));
 predSatis = input.FirstOrDefault();
 input = input.Skip(1);
 }

if you evaluate your 2 lines

input = input.SkipWhile(v => !pred(v));
predSatis = input.FirstOrDefault();

over iterations of your while loop, it is necessary to expand the input variable for each iteration.

input.SkipWhile(pred).FirstOrDefault
input.SkipWhile(pred).SkipWhile(pred).FirstOrDefault
input.SkipWhile(pred).SkipWhile(pred).SkipWhile(pred).FirstOrDefault
input.SkipWhile(pred).SkipWhile(pred).SkipWhile(pred).SkipWhile(pred).FirstOrDefault

having seen this, we now consider the evaluation of input.SkipWhile(!pred)

so over 100 iterations

pred = x => x % 2 == 0 : 5150
pred = x => x % 3 == 0 : 3600
pred = x => x % 4 == 0 : 2625
pred = x => x % 5 == 0 : 2120
pred = x => x % 6 == 0 : 1849
pred = x => x % 15 == 0 : 837
pred = x => x % 20 == 0 : 605
pred = x => x % 25 == 0 : 504
pred = x => x % 30 == 0 : 564
pred = x => x % 30 == 0 : 413

Your functions behaviour is therefore dependent not just on the size of your data, but on the form of your predicate.

For your own analysis, this is what I used for evaluating the predicate

 [TestCase(1,5000,10)]
 public void TestSkipBeforeIf(int start, int end, int mod)
 {
 var numberValue = new NumberValues(start,end);
 numberValue.SplitBeforeIf(i => i%mod == 0).ToList();
 }

I'm not clear what you meant by stateful predicate or IEnumerable, however in terms of the IEnumerable I have maintained a count as state. Similarly I could maintain a count for the pred, or add some bizarre checking behaviour.

Func<object, int, bool> predStateful = (y,x) =>
{
 if (x == y.Check(x))
 {
 return false;
 }
 return x%mod == 0;
};
Func<int, bool> pred = x => predStateful(MyCheckingObject,x);
numberValue.SplitBeforeIf(pred).ToList();

Thanks for the interesting question.

lang-cs

AltStyle によって変換されたページ (->オリジナル) /