3206 |
3206 |
return enumerator;
|
3207 |
3207 |
}
|
3208 |
3208 |
|
|
3209 |
struct slicewhen_arg {
|
|
3210 |
VALUE pred;
|
|
3211 |
VALUE prev_elt;
|
|
3212 |
VALUE prev_elts;
|
|
3213 |
VALUE yielder;
|
|
3214 |
};
|
|
3215 |
|
|
3216 |
static VALUE
|
|
3217 |
slicewhen_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
|
|
3218 |
{
|
|
3219 |
#define UPDATE_MEMO ((void)(memo = MEMO_FOR(struct slicewhen_arg, _memo)))
|
|
3220 |
struct slicewhen_arg *memo;
|
|
3221 |
int split_p;
|
|
3222 |
UPDATE_MEMO;
|
|
3223 |
|
|
3224 |
ENUM_WANT_SVALUE();
|
|
3225 |
|
|
3226 |
if (memo->prev_elt == Qundef) {
|
|
3227 |
/* The first element */
|
|
3228 |
memo->prev_elt = i;
|
|
3229 |
memo->prev_elts = rb_ary_new3(1, i);
|
|
3230 |
}
|
|
3231 |
else {
|
|
3232 |
split_p = RTEST(rb_funcall(memo->pred, id_call, 2, memo->prev_elt, i));
|
|
3233 |
UPDATE_MEMO;
|
|
3234 |
|
|
3235 |
if (split_p) {
|
|
3236 |
rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
|
|
3237 |
UPDATE_MEMO;
|
|
3238 |
memo->prev_elts = rb_ary_new3(1, i);
|
|
3239 |
}
|
|
3240 |
else {
|
|
3241 |
rb_ary_push(memo->prev_elts, i);
|
|
3242 |
}
|
|
3243 |
|
|
3244 |
memo->prev_elt = i;
|
|
3245 |
}
|
|
3246 |
|
|
3247 |
return Qnil;
|
|
3248 |
#undef UPDATE_MEMO
|
|
3249 |
}
|
|
3250 |
|
|
3251 |
static VALUE
|
|
3252 |
slicewhen_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
|
|
3253 |
{
|
|
3254 |
VALUE enumerable;
|
|
3255 |
VALUE arg;
|
|
3256 |
struct slicewhen_arg *memo = NEW_MEMO_FOR(struct slicewhen_arg, arg);
|
|
3257 |
|
|
3258 |
enumerable = rb_ivar_get(enumerator, rb_intern("slicewhen_enum"));
|
|
3259 |
memo->pred = rb_attr_get(enumerator, rb_intern("slicewhen_pred"));
|
|
3260 |
memo->prev_elt = Qundef;
|
|
3261 |
memo->prev_elts = Qnil;
|
|
3262 |
memo->yielder = yielder;
|
|
3263 |
|
|
3264 |
rb_block_call(enumerable, id_each, 0, 0, slicewhen_ii, arg);
|
|
3265 |
memo = MEMO_FOR(struct slicewhen_arg, arg);
|
|
3266 |
if (!NIL_P(memo->prev_elts))
|
|
3267 |
rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
|
|
3268 |
return Qnil;
|
|
3269 |
}
|
|
3270 |
|
|
3271 |
/*
|
|
3272 |
* call-seq:
|
|
3273 |
* enum.slice_when {|elt_before, elt_after| bool } -> an_enumerator
|
|
3274 |
*
|
|
3275 |
* Creates an enumerator for each chunked elements.
|
|
3276 |
* The beginnings of chunks are defined by the block.
|
|
3277 |
*
|
|
3278 |
* This method split each chunk using adjacent elements,
|
|
3279 |
* _elt_before_ and _elt_after_,
|
|
3280 |
* in the receiver enumerator.
|
|
3281 |
* This method split chunks between _elt_before_ and _elt_after_ where
|
|
3282 |
* the block returns true.
|
|
3283 |
*
|
|
3284 |
* The block is called the length of the receiver enumerator minus one.
|
|
3285 |
*
|
|
3286 |
* The result enumerator yields the chunked elements as an array.
|
|
3287 |
* So +each+ method can be called as follows:
|
|
3288 |
*
|
|
3289 |
* enum.slice_when { |elt_before, elt_after| bool }.each { |ary| ... }
|
|
3290 |
*
|
|
3291 |
* Other methods of the Enumerator class and Enumerable module,
|
|
3292 |
* such as +map+, etc., are also usable.
|
|
3293 |
*
|
|
3294 |
* For example, one-by-one increasing subsequence can be chunked as follows:
|
|
3295 |
*
|
|
3296 |
* a = [1,2,4,9,10,11,12,15,16,19,20,21]
|
|
3297 |
* b = a.slice_when {|i, j| i+1 != j }
|
|
3298 |
* p b.to_a #=> [[1, 2], [4], [9, 10, 11, 12], [15, 16], [19, 20, 21]]
|
|
3299 |
* c = b.map {|a| a.length < 3 ? a : "#{a.first}-#{a.last}" }
|
|
3300 |
* p c #=> [[1, 2], [4], "9-12", [15, 16], "19-21"]
|
|
3301 |
* d = c.join(",")
|
|
3302 |
* p d #=> "1,2,4,9-12,15,16,19-21"
|
|
3303 |
*
|
|
3304 |
* Increasing (non-decreasing) subsequence can be chunked as follows:
|
|
3305 |
*
|
|
3306 |
* a = [0, 9, 2, 2, 3, 2, 7, 5, 9, 5]
|
|
3307 |
* p a.slice_when {|i, j| i > j }.to_a
|
|
3308 |
* #=> [[0, 9], [2, 2, 3], [2, 7], [5, 9], [5]]
|
|
3309 |
*
|
|
3310 |
* Adjacent evens and odds can be chunked as follows:
|
|
3311 |
* (Enumerable#chunk is another way to do it.)
|
|
3312 |
*
|
|
3313 |
* a = [7, 5, 9, 2, 0, 7, 9, 4, 2, 0]
|
|
3314 |
* p a.slice_when {|i, j| i.even? != j.even? }.to_a
|
|
3315 |
* #=> [[7, 5, 9], [2, 0], [7, 9], [4, 2, 0]]
|
|
3316 |
*
|
|
3317 |
* Paragraphs (non-empty lines with trailing empty lines) can be chunked as follows:
|
|
3318 |
* (See Enumerable#chunk to ignore empty lines.)
|
|
3319 |
*
|
|
3320 |
* lines = ["foo\n", "bar\n", "\n", "baz\n", "qux\n"]
|
|
3321 |
* p lines.slice_when {|l1, l2| /\A\s*\z/ =~ l1 && /\S/ =~ l2 }.to_a
|
|
3322 |
* #=> [["foo\n", "bar\n", "\n"], ["baz\n", "qux\n"]]
|
|
3323 |
*
|
|
3324 |
*/
|
|
3325 |
static VALUE
|
|
3326 |
enum_slice_when(VALUE enumerable)
|
|
3327 |
{
|
|
3328 |
VALUE enumerator;
|
|
3329 |
VALUE pred;
|
|
3330 |
|
|
3331 |
pred = rb_block_proc();
|
|
3332 |
|
|
3333 |
enumerator = rb_obj_alloc(rb_cEnumerator);
|
|
3334 |
rb_ivar_set(enumerator, rb_intern("slicewhen_enum"), enumerable);
|
|
3335 |
rb_ivar_set(enumerator, rb_intern("slicewhen_pred"), pred);
|
|
3336 |
|
|
3337 |
rb_block_call(enumerator, idInitialize, 0, 0, slicewhen_i, enumerator);
|
|
3338 |
return enumerator;
|
|
3339 |
}
|
|
3340 |
|
3209 |
3341 |
/*
|
3210 |
3342 |
* The <code>Enumerable</code> mixin provides collection classes with
|
3211 |
3343 |
* several traversal and searching methods, and with the ability to
|
... | ... | |
3275 |
3407 |
rb_define_method(rb_mEnumerable, "chunk", enum_chunk, -1);
|
3276 |
3408 |
rb_define_method(rb_mEnumerable, "slice_before", enum_slice_before, -1);
|
3277 |
3409 |
rb_define_method(rb_mEnumerable, "slice_after", enum_slice_after, -1);
|
|
3410 |
rb_define_method(rb_mEnumerable, "slice_when", enum_slice_when, 0);
|
3278 |
3411 |
|
3279 |
3412 |
id_next = rb_intern("next");
|
3280 |
3413 |
id_call = rb_intern("call");
|