diff options
Diffstat (limited to 'mrbgems/mruby-array-ext/src')
| -rw-r--r-- | mrbgems/mruby-array-ext/src/array.c | 177 |
1 files changed, 174 insertions, 3 deletions
diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c index 8df9d7f30..d97778642 100644 --- a/mrbgems/mruby-array-ext/src/array.c +++ b/mrbgems/mruby-array-ext/src/array.c @@ -95,6 +95,12 @@ mrb_ary_at(mrb_state *mrb, mrb_value ary) } static mrb_value +ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n) +{ + return mrb_ary_entry(ary, n); +} + +static mrb_value mrb_ary_values_at(mrb_state *mrb, mrb_value self) { mrb_int argc; @@ -102,10 +108,9 @@ mrb_ary_values_at(mrb_state *mrb, mrb_value self) mrb_get_args(mrb, "*", &argv, &argc); - return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref); + return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, ary_ref); } - /* * call-seq: * ary.slice!(index) -> obj or nil @@ -183,6 +188,168 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self) return ary; } +/* + * call-seq: + * ary.compact -> new_ary + * + * Returns a copy of +self+ with all +nil+ elements removed. + * + * [ "a", nil, "b", nil, "c", nil ].compact + * #=> [ "a", "b", "c" ] + */ + +static mrb_value +mrb_ary_compact(mrb_state *mrb, mrb_value self) +{ + mrb_value ary = mrb_ary_new(mrb); + mrb_int len = RARRAY_LEN(self); + mrb_value *p = RARRAY_PTR(self); + + for (mrb_int i = 0; i < len; ++i) { + if (!mrb_nil_p(p[i])) { + mrb_ary_push(mrb, ary, p[i]); + } + } + return ary; +} + +/* + * call-seq: + * ary.compact! -> ary or nil + * + * Removes +nil+ elements from the array. + * Returns +nil+ if no changes were made, otherwise returns + * <i>ary</i>. + * + * [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ] + * [ "a", "b", "c" ].compact! #=> nil + */ +static mrb_value +mrb_ary_compact_bang(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self); + mrb_int i, j = 0; + mrb_int len = ARY_LEN(a); + mrb_value *p = ARY_PTR(a); + + mrb_ary_modify(mrb, a); + for (i = 0; i < len; ++i) { + if (!mrb_nil_p(p[i])) { + if (i != j) p[j] = p[i]; + j++; + } + } + if (i == j) return mrb_nil_value(); + if (j < len) ARY_SET_LEN(RARRAY(self), j); + return self; +} + + +/* + * call-seq: + * ary.rotate(count=1) -> new_ary + * + * Returns a new array by rotating +self+ so that the element at +count+ is + * the first element of the new array. + * + * If +count+ is negative then it rotates in the opposite direction, starting + * from the end of +self+ where +-1+ is the last element. + * + * a = [ "a", "b", "c", "d" ] + * a.rotate #=> ["b", "c", "d", "a"] + * a #=> ["a", "b", "c", "d"] + * a.rotate(2) #=> ["c", "d", "a", "b"] + * a.rotate(-3) #=> ["b", "c", "d", "a"] + */ +static mrb_value +mrb_ary_rotate(mrb_state *mrb, mrb_value self) +{ + mrb_value ary = mrb_ary_new(mrb); + mrb_int len = RARRAY_LEN(self); + mrb_value *p = RARRAY_PTR(self); + mrb_int count=1, idx; + + mrb_get_args(mrb, "|i", &count); + if (len <= 0) return ary; + if (count < 0) { + idx = len - (~count % len) - 1; + } + else { + idx = count % len; + } + for (mrb_int i = 0; i<len; i++) { + mrb_ary_push(mrb, ary, p[idx++]); + if (idx == len) idx = 0; + } + return ary; +} + +static void +rev(mrb_value *p, mrb_int beg, mrb_int end) +{ + for (mrb_int i=beg,j=end-1; i<j; i++,j--) { + mrb_value v = p[i]; + p[i] = p[j]; + p[j] = v; + } +} + +/* + * call-seq: + * ary.rotate!(count=1) -> ary + * + * Rotates +self+ in place so that the element at +count+ comes first, and + * returns +self+. + * + * If +count+ is negative then it rotates in the opposite direction, starting + * from the end of the array where +-1+ is the last element. + * + * a = [ "a", "b", "c", "d" ] + * a.rotate! #=> ["b", "c", "d", "a"] + * a #=> ["b", "c", "d", "a"] + * a.rotate!(2) #=> ["d", "a", "b", "c"] + * a.rotate!(-3) #=> ["a", "b", "c", "d"] + */ +static mrb_value +mrb_ary_rotate_bang(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self); + mrb_int len = ARY_LEN(a); + mrb_value *p = ARY_PTR(a); + mrb_int count=1, idx; + + mrb_get_args(mrb, "|i", &count); + mrb_ary_modify(mrb, a); + if (len == 0 || count == 0) return self; + if (count == 1) { + mrb_value v = p[0]; + for (mrb_int i=1; i<len; i++) { + p[i-1] = p[i]; + } + p[len-1] = v; + return self; + } + if (count < 0) { + idx = len - (~count % len) - 1; + } + else { + idx = count % len; + } + /* e.g. [1,2,3,4,5].rotate!(2) -> [3,4,5,1,2] */ + /* first, reverse the whole array */ + /* [1,2,3,4,5] -> [5,4,3,2,1] */ + rev(p, 0, len); + /* then, re-reverse part before idx */ + /* [5,4,3,2,1] -> [3,4,5,2,1] */ + /* ^idx ~~~~~ */ + rev(p, 0, len-idx); + /* finally, re-reverse part after idx */ + /* [3,4,5,2,1] -> [3,4,5,1,2] */ + /* ^idx ~~~ */ + rev(p, len-idx, len); + return self; +} + void mrb_mruby_array_ext_gem_init(mrb_state* mrb) { @@ -192,7 +359,11 @@ mrb_mruby_array_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, a, "at", mrb_ary_at, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY()); - mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ARG(1,1)); + mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ARG(1,1)); + mrb_define_method(mrb, a, "compact", mrb_ary_compact, MRB_ARGS_NONE()); + mrb_define_method(mrb, a, "compact!", mrb_ary_compact_bang, MRB_ARGS_NONE()); + mrb_define_method(mrb, a, "rotate", mrb_ary_rotate, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, a, "rotate!", mrb_ary_rotate_bang, MRB_ARGS_OPT(1)); } void |
