Project

General

Profile

Feature #9071 ยป slice_after.patch

akr (Akira Tanaka), 05/10/2014 11:34 AM

View differences:

enum.c (working copy)
3083 3083
    return enumerator;
3084 3084
}
3085 3085

  
3086

  
3087
struct sliceafter_arg {
3088
    VALUE pat;
3089
    VALUE pred;
3090
    VALUE prev_elts;
3091
    VALUE yielder;
3092
};
3093

  
3094
static VALUE
3095
sliceafter_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
3096
{
3097
#define UPDATE_MEMO ((memo = MEMO_FOR(struct sliceafter_arg, _memo)), 1)
3098
    struct sliceafter_arg *memo;
3099
    int split_p;
3100
    UPDATE_MEMO;
3101

  
3102
    ENUM_WANT_SVALUE();
3103

  
3104
    if (NIL_P(memo->prev_elts)) {
3105
        memo->prev_elts = rb_ary_new3(1, i);
3106
    }
3107
    else {
3108
        rb_ary_push(memo->prev_elts, i);
3109
    }
3110

  
3111
    split_p = 1;
3112
    if (!NIL_P(memo->pat)) {
3113
        split_p = RTEST(rb_funcall(memo->pat, id_eqq, 1, i));
3114
        UPDATE_MEMO;
3115
    }
3116
    if (split_p && !NIL_P(memo->pred)) {
3117
        split_p = RTEST(rb_funcall(memo->pred, id_call, 1, i));
3118
        UPDATE_MEMO;
3119
    }
3120

  
3121
    if (split_p) {
3122
        rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
3123
        UPDATE_MEMO;
3124
        memo->prev_elts = Qnil;
3125
    }
3126

  
3127
    return Qnil;
3128
#undef UPDATE_MEMO
3129
}
3130

  
3131
static VALUE
3132
sliceafter_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
3133
{
3134
    VALUE enumerable;
3135
    VALUE arg;
3136
    struct sliceafter_arg *memo = NEW_MEMO_FOR(struct sliceafter_arg, arg);
3137

  
3138
    enumerable = rb_ivar_get(enumerator, rb_intern("sliceafter_enum"));
3139
    memo->pat = rb_ivar_get(enumerator, rb_intern("sliceafter_pat"));
3140
    memo->pred = rb_attr_get(enumerator, rb_intern("sliceafter_pred"));
3141
    memo->prev_elts = Qnil;
3142
    memo->yielder = yielder;
3143

  
3144
    rb_block_call(enumerable, id_each, 0, 0, sliceafter_ii, arg);
3145
    memo = MEMO_FOR(struct sliceafter_arg, arg);
3146
    if (!NIL_P(memo->prev_elts))
3147
        rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
3148
    return Qnil;
3149
}
3150

  
3151
/*
3152
 *  call-seq:
3153
 *     enum.slice_after(pattern)       -> an_enumerator
3154
 *     enum.slice_after { |elt| bool } -> an_enumerator
3155
 *
3156
 *  Creates an enumerator for each chunked elements.
3157
 *  The ends of chunks are defined by _pattern_ and the block.
3158
 *
3159
 *  If <code>_pattern_ === _elt_</code> returns <code>true</code> or the block
3160
 *  returns <code>true</code> for the element, the element is end of a
3161
 *  chunk.
3162
 *
3163
 *  The <code>===</code> and _block_ is called from the first element to the last
3164
 *  element of _enum_.
3165
 *
3166
 *  The result enumerator yields the chunked elements as an array.
3167
 *  So +each+ method can be called as follows:
3168
 *
3169
 *    enum.slice_after(pattern).each { |ary| ... }
3170
 *    enum.slice_after { |elt| bool }.each { |ary| ... }
3171
 *
3172
 *  Other methods of the Enumerator class and Enumerable module,
3173
 *  such as map, etc., are also usable.
3174
 *
3175
 *  For example, continuation lines (lines end with backslash) can be
3176
 *  concatenated as follows:
3177
 *
3178
 *    lines = ["foo\n", "bar\\\n", "baz\n", "\n", "qux\n"]
3179
 *    e = lines.slice_after(/(?<!\\)\n\z/)
3180
 *    p e.to_a
3181
 *    #=> [["foo\n"], ["bar\\\n", "baz\n"], ["\n"], ["qux\n"]]
3182
 *    p e.map {|ll| ll[0...-1].map {|l| l.sub(/\\\n\z/, "") }.join + ll.last }
3183
 *    #=>["foo\n", "barbaz\n", "\n", "qux\n"]
3184
 *
3185
 */
3186

  
3187
static VALUE
3188
enum_slice_after(int argc, VALUE *argv, VALUE enumerable)
3189
{
3190
    VALUE enumerator;
3191
    VALUE pat, pred;
3192

  
3193
    rb_scan_args(argc, argv, "01", &pat);
3194
    if (rb_block_given_p())
3195
        pred = rb_block_proc();
3196
    else
3197
        pred = Qnil;
3198

  
3199
    if (NIL_P(pat) && NIL_P(pred)) {
3200
	rb_raise(rb_eArgError, "no pattan/block given");
3201
    }
3202

  
3203
    if (!NIL_P(pat) && !NIL_P(pred)) {
3204
	rb_raise(rb_eArgError, "both pattan and block are given");
3205
    }
3206

  
3207
    enumerator = rb_obj_alloc(rb_cEnumerator);
3208
    rb_ivar_set(enumerator, rb_intern("sliceafter_pat"), pat);
3209
    rb_ivar_set(enumerator, rb_intern("sliceafter_pred"), pred);
3210
    rb_ivar_set(enumerator, rb_intern("sliceafter_enum"), enumerable);
3211

  
3212
    rb_block_call(enumerator, idInitialize, 0, 0, sliceafter_i, enumerator);
3213
    return enumerator;
3214
}
3215

  
3086 3216
/*
3087 3217
 *  The <code>Enumerable</code> mixin provides collection classes with
3088 3218
 *  several traversal and searching methods, and with the ability to
......
3151 3281
    rb_define_method(rb_mEnumerable, "cycle", enum_cycle, -1);
3152 3282
    rb_define_method(rb_mEnumerable, "chunk", enum_chunk, -1);
3153 3283
    rb_define_method(rb_mEnumerable, "slice_before", enum_slice_before, -1);
3284
    rb_define_method(rb_mEnumerable, "slice_after", enum_slice_after, -1);
3154 3285

  
3155 3286
    id_next = rb_intern("next");
3156 3287
    id_call = rb_intern("call");
enumerator.c (working copy)
2036 2036
    rb_define_method(rb_cLazy, "lazy", lazy_lazy, 0);
2037 2037
    rb_define_method(rb_cLazy, "chunk", lazy_super, -1);
2038 2038
    rb_define_method(rb_cLazy, "slice_before", lazy_super, -1);
2039
    rb_define_method(rb_cLazy, "slice_after", lazy_super, -1);
2039 2040

  
2040 2041
    rb_define_alias(rb_cLazy, "force", "to_a");
2041 2042

  
test/ruby/test_enum.rb (working copy)
531 531
    assert_not_warn{ss.slice_before(/\A...\z/).to_a}
532 532
  end
533 533

  
534
  def test_slice_after0
535
    assert_raise(ArgumentError) { [].slice_after }
536
  end
537

  
538
  def test_slice_after1
539
    e = [].slice_after {|a| flunk "should not be called" }
540
    assert_equal([], e.to_a)
541

  
542
    e = [1,2].slice_after(1)
543
    assert_equal([[1], [2]], e.to_a)
544

  
545
    e = [1,2].slice_after(3)
546
    assert_equal([[1, 2]], e.to_a)
547

  
548
    [true, false].each {|b|
549
      block_results = [true, b]
550
      e = [1,2].slice_after {|a| block_results.shift }
551
      assert_equal([[1], [2]], e.to_a)
552
      assert_equal([], block_results)
553

  
554
      block_results = [false, b]
555
      e = [1,2].slice_after {|a| block_results.shift }
556
      assert_equal([[1, 2]], e.to_a)
557
      assert_equal([], block_results)
558
    }
559
  end
560

  
561
  def test_slice_after_both_pattern_and_block
562
    assert_raise(ArgumentError) { [].slice_after(1) {|a| true } }
563
  end
564

  
565
  def test_slice_after_continuation_lines
566
    lines = ["foo\n", "bar\\\n", "baz\n", "\n", "qux\n"]
567
    e = lines.slice_after(/[^\\]\n\z/)
568
    assert_equal([["foo\n"], ["bar\\\n", "baz\n"], ["\n", "qux\n"]], e.to_a)
569
  end
570

  
571
  def test_slice_before_empty_line
572
    lines = ["foo", "", "bar"]
573
    e = lines.slice_after(/\A\s*\z/)
574
    assert_equal([["foo", ""], ["bar"]], e.to_a)
575
  end
576

  
534 577
  def test_detect
535 578
    @obj = ('a'..'z')
536 579
    assert_equal('c', @obj.detect {|x| x == 'c' })
test/ruby/test_lazy_enumerator.rb (working copy)
470 470
    bug7507 = '[ruby-core:51510]'
471 471
    {
472 472
      slice_before: //,
473
      slice_after: //,
473 474
      with_index: nil,
474 475
      cycle: nil,
475 476
      each_with_object: 42,