Feature #12744 ยป add-reverse-string-iteration.patch
| ChangeLog | ||
|---|---|---|
|
Thu Sep 10 00:22:18 2016 Bouke van der Bijl <boukevanderbijl@gmail.com>
|
||
|
* string.c (rb_str_reverse_each_char, rb_str_reverse_chars): Add
|
||
|
str.reverse_each_char and str.reverse_chars that allow for efficiently
|
||
|
iterating over a string in reverse.
|
||
|
* test/ruby/test_string.rb: add tests for above
|
||
|
Thu Sep 8 17:47:18 2016 Kazuki Tsujimoto <kazuki@callcc.net>
|
||
|
* array.c (flatten): use rb_obj_class instead of rb_class_of
|
||
| string.c | ||
|---|---|---|
|
return rb_str_enumerate_chars(str, 1);
|
||
|
}
|
||
|
static VALUE
|
||
|
rb_str_reverse_enumerate_chars(VALUE str, int wantarray)
|
||
|
{
|
||
|
VALUE orig = str;
|
||
|
VALUE substr;
|
||
|
long len, n;
|
||
|
const char *ptr, *end;
|
||
|
char *p;
|
||
|
rb_encoding *enc;
|
||
|
VALUE UNINITIALIZED_VAR(ary);
|
||
|
str = rb_str_new_frozen(str);
|
||
|
ptr = RSTRING_PTR(str);
|
||
|
len = RSTRING_LEN(str);
|
||
|
end = ptr + len;
|
||
|
enc = rb_enc_get(str);
|
||
|
if (rb_block_given_p()) {
|
||
|
if (wantarray) {
|
||
|
#if STRING_ENUMERATORS_WANTARRAY
|
||
|
rb_warn("given block not used");
|
||
|
ary = rb_ary_new_capa(str_strlen(str, enc)); /* str's enc*/
|
||
|
#else
|
||
|
rb_warning("passing a block to String#chars is deprecated");
|
||
|
wantarray = 0;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (wantarray)
|
||
|
ary = rb_ary_new_capa(str_strlen(str, enc)); /* str's enc*/
|
||
|
else
|
||
|
return SIZED_ENUMERATOR(str, 0, 0, rb_str_each_char_size);
|
||
|
}
|
||
|
if (ENC_CODERANGE_CLEAN_P(ENC_CODERANGE(str))) {
|
||
|
for(p = rb_enc_left_char_head(ptr, end - 1, end, enc); p >= ptr; p = rb_enc_left_char_head(ptr, p - 1, end, enc)) {
|
||
|
n = rb_enc_fast_mbclen(p, end, enc);
|
||
|
substr = rb_str_subseq(str, p - ptr, n);
|
||
|
if (wantarray)
|
||
|
rb_ary_push(ary, substr);
|
||
|
else
|
||
|
rb_yield(substr);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
for(p = rb_enc_left_char_head(ptr, end - 1, end, enc); p >= ptr; p = rb_enc_left_char_head(ptr, p - 1, end, enc)) {
|
||
|
n = rb_enc_mbclen(p, end, enc);
|
||
|
substr = rb_str_subseq(str, p - ptr, n);
|
||
|
if (wantarray)
|
||
|
rb_ary_push(ary, substr);
|
||
|
else
|
||
|
rb_yield(substr);
|
||
|
}
|
||
|
}
|
||
|
RB_GC_GUARD(str);
|
||
|
if (wantarray)
|
||
|
return ary;
|
||
|
else
|
||
|
return orig;
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* str.reverse_each_char {|cstr| block } -> str
|
||
|
* str.reverse_each_char -> an_enumerator
|
||
|
*
|
||
|
* Passes each character in <i>str</i> in reverse to the given block, or returns
|
||
|
* an enumerator if no block is given.
|
||
|
*
|
||
|
* "hello".reverse_each_char {|c| print c, ' ' }
|
||
|
*
|
||
|
* <em>produces:</em>
|
||
|
*
|
||
|
* o l l e h
|
||
|
*/
|
||
|
static VALUE
|
||
|
rb_str_reverse_each_char(VALUE str)
|
||
|
{
|
||
|
return rb_str_reverse_enumerate_chars(str, 0);
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* str.reverse_chars -> an_array
|
||
|
*
|
||
|
* Returns an array of characters in <i>str</i> in reverse. This is a shorthand
|
||
|
* for <code>str.reverse_each_char.to_a</code>.
|
||
|
*
|
||
|
* If a block is given, which is a deprecated form, works the same as
|
||
|
* <code>reverse_each_char</code>.
|
||
|
*/
|
||
|
static VALUE
|
||
|
rb_str_reverse_chars(VALUE str)
|
||
|
{
|
||
|
return rb_str_reverse_enumerate_chars(str, 1);
|
||
|
}
|
||
|
static VALUE
|
||
|
rb_str_enumerate_codepoints(VALUE str, int wantarray)
|
||
| ... | ... | |
|
rb_define_method(rb_cString, "lines", rb_str_lines, -1);
|
||
|
rb_define_method(rb_cString, "bytes", rb_str_bytes, 0);
|
||
|
rb_define_method(rb_cString, "chars", rb_str_chars, 0);
|
||
|
rb_define_method(rb_cString, "reverse_chars", rb_str_reverse_chars, 0);
|
||
|
rb_define_method(rb_cString, "codepoints", rb_str_codepoints, 0);
|
||
|
rb_define_method(rb_cString, "reverse", rb_str_reverse, 0);
|
||
|
rb_define_method(rb_cString, "reverse!", rb_str_reverse_bang, 0);
|
||
| ... | ... | |
|
rb_define_method(rb_cString, "each_line", rb_str_each_line, -1);
|
||
|
rb_define_method(rb_cString, "each_byte", rb_str_each_byte, 0);
|
||
|
rb_define_method(rb_cString, "each_char", rb_str_each_char, 0);
|
||
|
rb_define_method(rb_cString, "reverse_each_char", rb_str_reverse_each_char, 0);
|
||
|
rb_define_method(rb_cString, "each_codepoint", rb_str_each_codepoint, 0);
|
||
|
rb_define_method(rb_cString, "sum", rb_str_sum, -1);
|
||
| test/ruby/test_string.rb | ||
|---|---|---|
|
end
|
||
|
end
|
||
|
def test_reverse_each_char
|
||
|
s = S("ABC")
|
||
|
res = []
|
||
|
assert_equal s.object_id, s.reverse_each_char {|x| res << x }.object_id
|
||
|
assert_equal("C", res[0])
|
||
|
assert_equal("B", res[1])
|
||
|
assert_equal("A", res[2])
|
||
|
assert_equal "๐", S("ABC๐").reverse_each_char.next
|
||
|
end
|
||
|
def test_reverse_chars
|
||
|
s = S("ABC")
|
||
|
assert_equal ["C", "B", "A"], s.reverse_chars
|
||
|
if ENUMERATOR_WANTARRAY
|
||
|
assert_warn(/block not used/) {
|
||
|
assert_equal ["C", "B", "A"], s.reverse_chars {}
|
||
|
}
|
||
|
else
|
||
|
assert_warning(/deprecated/) {
|
||
|
res = []
|
||
|
assert_equal s.object_id, s.reverse_chars {|x| res << x }.object_id
|
||
|
assert_equal("C", res[0])
|
||
|
assert_equal("B", res[1])
|
||
|
assert_equal("A", res[2])
|
||
|
}
|
||
|
end
|
||
|
end
|
||
|
def test_each_line
|
||
|
save = $/
|
||
|
$/ = "\n"
|
||