Proposal: Array#walker

View: New views
20 Messages — Rating Filter:   Alert me  
< Prev | 1 - 2 | Next >

Proposal: Array#walker

by Wolfgang Nádasi-donner-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Good morning all together!

I know it is very late for Ruby 1.9.1 proposals, but I have a very simple and
easy suggestion. While writing a program to generate the Ruby program for some
guessUTF test (border cases which should not success), I recognized, that it can
be very helpful for several use cases to have a method Array#walker with the
following Ruby implementation...

class Array
   def walker
     l = self.length
     l.times do |i|
       yield self[0, i], self[i], self[i+1, l-i-1]
     end
   end
end

A simple usage example is...

[1,2,3,4].walker{|pre,el,post|puts "#{pre.inspect}-#{el} - #{post.inspect}"}

Output:
[]-1 - [2, 3, 4]
[1]-2 - [3, 4]
[1, 2]-3 - [4]
[1, 2, 3]-4 - []

What do you think about this (hopefully) minor change proposal?

Wolfgang Nádasi-Donner


Re: Proposal: Array#walker

by Wolfgang Nádasi-donner-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

A nicer version may be...

class Array
   def walker
     l = self.length
     if block_given?
       l.times do |i|
         yield self[0, i], self[i], self[i+1, l-i-1]
       end
     else
       return Enumerable::Enumerator.new(self, :walker)
     end
   end
end

Wolfgang Nádasi-Donner


Re: Proposal: Array#walker

by Kornelius Kalnbach :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



Am 14.11.2007 um 01:37 schrieb Wolfgang Nádasi-Donner <ed.odanow@...
e>:

> Good morning all together!
>
> I know it is very late for Ruby 1.9.1 proposals, but I have a very  
> simple and easy suggestion. While writing a program to generate the  
> Ruby program for some guessUTF test (border cases which should not  
> success), I recognized, that it can be very helpful for several use  
> cases to have a method Array#walker with the following Ruby  
> implementation...
>
> class Array
>  def walker
>    l = self.length
>    l.times do |i|
>      yield self[0, i], self[i], self[i+1, l-i-1]
>    end
>  end
> end
>
> A simple usage example is...
>
> [1,2,3,4].walker{|pre,el,post|puts "#{pre.inspect}-#{el} - #
> {post.inspect}"}
>
> Output:
> []-1 - [2, 3, 4]
> [1]-2 - [3, 4]
> [1, 2]-3 - [4]
> [1, 2, 3]-4 - []
>
> What do you think about this (hopefully) minor change proposal?
>
> Wolfgang Nádasi-Donner
>
>

I think this is a very specialized method that should not go into the  
core library...

[murphy]

Re: Proposal: Array#walker

by trans :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



On Nov 13, 8:11 pm, Wolfgang Nádasi-Donner <ed.oda...@...>
wrote:

> A nicer version may be...
>
> class Array
>    def walker
>      l = self.length
>      if block_given?
>        l.times do |i|
>          yield self[0, i], self[i], self[i+1, l-i-1]
>        end
>      else
>        return Enumerable::Enumerator.new(self, :walker)
>      end
>    end
> end

Hmm...

  a.each_with_index do |el, i|
    pre, post = a[0,i], a[i+1..-1]
    ...
  end

That's not too bad, so I'm not sure it's worth it (?)

However, a few times I've wanted a nicer way to split the array at an
index. Something like:

  a.each_with_index do |el, i|
    pre, post = *a.crack(i)
    ...
  end

T.



Re: Proposal: Array#walker

by trans :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



On Nov 13, 10:51 pm, Trans <transf...@...> wrote:

> However, a few times I've wanted a nicer way to split the array at an
> index. Something like:
>
>   a.each_with_index do |el, i|
>     pre, post = *a.crack(i)
>     ...
>   end

Actually, can anyone recommend a better name than #crack? --or a way
to already do this?

T.



Re: Proposal: Array#walker

by Hugh Sasse :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Wed, 14 Nov 2007, Trans wrote:

> On Nov 13, 10:51 pm, Trans <transf...@...> wrote:
>
> > However, a few times I've wanted a nicer way to split the array at an
> > index. Something like:
        [...]
> >     pre, post = *a.crack(i)
        [...]
> Actually, can anyone recommend a better name than #crack? --or a way
> to already do this?

partition (already taken I think), split (taken), divide, cleave, rend,
tear, segment, decouple (railway analogy), snap, share, separate,
divorce.
>
> T.
>
        Hugh



Réf. : Re: Proposal: Array#walker

by Tadeusz Bochan :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Whats wrong with

       pre,post=a[0..i-1],a[i..-1]

?


On Nov 13, 10:51 pm, Trans <transf...@...> wrote:

> However, a few times I've wanted a nicer way to split the array at an
> index. Something like:
>
>   a.each_with_index do |el, i|
>     pre, post = *a.crack(i)
>     ...
>   end

Actually, can anyone recommend a better name than #crack? --or a way
to already do this?

T.




This message and any attachments (the "message") is
intended solely for the addressees and is confidential.
If you receive this message in error, please delete it and
immediately notify the sender. Any use not in accord with
its purpose, any dissemination or disclosure, either whole
or partial, is prohibited except formal approval. The internet
can not guarantee the integrity of this message.
BNP PARIBAS (and its subsidiaries) shall (will) not
therefore be liable for the message if modified.

                ---------------------------------------------

Ce message et toutes les pieces jointes (ci-apres le
"message") sont etablis a l'intention exclusive de ses
destinataires et sont confidentiels. Si vous recevez ce
message par erreur, merci de le detruire et d'en avertir
immediatement l'expediteur. Toute utilisation de ce
message non conforme a sa destination, toute diffusion
ou toute publication, totale ou partielle, est interdite, sauf
autorisation expresse. L'internet ne permettant pas
d'assurer l'integrite de ce message, BNP PARIBAS (et ses
filiales) decline(nt) toute responsabilite au titre de ce
message, dans l'hypothese ou il aurait ete modifie.



Parent Message unknown Re: Proposal: Array#walker

by Wolfgang Nádasi-donner-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

David A. Black schrieb:
> On Wed, 14 Nov 2007, Kornelius Kalnbach wrote:
>> I think this is a very specialized method that should not go into the
>> core library...
> I agree; it might be a technique you'd want to do sometimes, but I'd
> put it in the category of use cases that can be addressed with what's
> there already and don't need dedicated core methods of their own.

The only reason I put it here is, that I don't see a way to express it without
using the index positions on the basis of existing methods. Insofar it is an
elementary method of class Array. Some method that splits an Array and presents
the whole Array for each iteration is missing.m Maybe something like this proposal:

Trans schrieb:
 >   a.each_with_index do |el, i|
 >     pre, post = a[0,i], a[i+1..-1]
 >     ...
 >   end
 >
 > That's not too bad, so I'm not sure it's worth it (?)
 >
 > However, a few times I've wanted a nicer way to split the array at an
 > index. Something like:
 >
 >   a.each_with_index do |el, i|
 >     pre, post = *a.crack(i)
 >     ...
 >   end

Wolfgang Nádasi-Donner


Re: Proposal: Array#walker

by Patrick Mahoney-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Wed, Nov 14, 2007 at 09:23:04PM +0900, Trans wrote:

> On Nov 13, 10:51 pm, Trans <transf...@...> wrote:
>
> > However, a few times I've wanted a nicer way to split the array at an
> > index. Something like:
> >
> >   a.each_with_index do |el, i|
> >     pre, post = *a.crack(i)
> >     ...
> >   end
>
> Actually, can anyone recommend a better name than #crack? --or a way
> to already do this?

Maybe Array#partition_at to match Enumarable#partition ?

class Array
  def partition_at(i)
    [ self[0..i], self[(i+1)..length] ]
  end
end


Re: Proposal: Array#walker

by trans :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



On Nov 14, 8:32 am, Wolfgang Nádasi-Donner <ed.oda...@...>
wrote:

> The only reason I put it here is, that I don't see a way to express it without
> using the index positions on the basis of existing methods. Insofar it is an
> elementary method of class Array. Some method that splits an Array and presents
> the whole Array for each iteration is missing.

That's a fair point.

T.



Re: Proposal: Array#walker

by trans :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



On Nov 13, 7:37 pm, Wolfgang Nádasi-Donner <ed.oda...@...>
wrote:

> Good morning all together!
>
> I know it is very late for Ruby 1.9.1 proposals, but I have a very simple and
> easy suggestion. While writing a program to generate the Ruby program for some
> guessUTF test (border cases which should not success), I recognized, that it can
> be very helpful for several use cases to have a method Array#walker with the
> following Ruby implementation...
>
> class Array
>    def walker
>      l = self.length
>      l.times do |i|
>        yield self[0, i], self[i], self[i+1, l-i-1]
>      end
>    end
> end
>
> A simple usage example is...
>
> [1,2,3,4].walker{|pre,el,post|puts "#{pre.inspect}-#{el} - #{post.inspect}"}
>
> Output:
> []-1 - [2, 3, 4]
> [1]-2 - [3, 4]
> [1, 2]-3 - [4]
> [1, 2, 3]-4 - []
>
> What do you think about this (hopefully) minor change proposal?

Not so minor but this may interest you:

  # Iteration object.

  class It
    attr_reader :index, :value, :prior, :after
    def initialize(array)
      @array = array
      @index = 0
      @value = array[0]
      @prior = []
      @after = array[1..-1]
    end
    def first? ; @index == 0 ; end
    def last?  ; @index == @array.length ; end
    private
    def next_iteration
      @index += 1
      @prior << @value
      @value = @after.shift
    end
  end

  # Iterate over each element of array using an iteration object.

  def each_iteration
    if block_given?
      l = length
      it = It.new(self)
      l.times do |i|
        yield it
        it.send(:next_iteration)
      end
    else
      return Enumerable::Enumerator.new(self, :each_iteration)
    end
  end

Example:

irb(main):002:0> [:a,:b,:c].each_iteration{ |it|
irb(main):003:1*   p it.index, it.value, it.prior, it.after
irb(main):004:1> }

T.



Re: Proposal: Array#walker

by Kornelius Kalnbach :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hugh Sasse wrote:
>> Actually, can anyone recommend a better name than #crack? --or a way
>> to already do this?
>
> ...split (taken)...
no, it's not. however, String.split works in a different way, so maybe
this isn't the best name.

Array#split_at seems a logical option. I would expect it to behave like
an anti-slice:

/*
 *  call-seq:
 *     array.split_at(index)          -> [left_array, right_array]
 *     array.split_at(start, length)  -> [left_array, right_array]
 *     array.split_at(range)          -> [left_array, right_array]
 *
 *  Splits an array into two parts, with the first one containing
 *  all elements before the given range, and the second one containing
 *  all elements after it. See Array#slice for information about the
 *  range expressions.
 *
 *     a = [ "a", "b", "c", "d", "e" ]
 *     a.split_at(0)          #=> [[], ["b", "c", "d", "e"]]
 *     a.split_at(2)          #=> [["a", "b"], ["d", "e"]]
 *     a.split_at(-1)         #=> [["a", "b", "c", "d"], []]
 *     a.split_at(1, 2)       #=> [["a"], ["d", "e"]]
 *     a.split_at(1, 0)       #=> [["a"], ["b", "c", "d", "e"]]
 *     a.split_at(1..3)       #=> [["a"], ["e"]]
 *     a.split_at(0..-1)      #=> [[], []]
 *
 *     range = 1..3
 *     slice = a.slice(range)
 *     pre, post = a.split_at(range)
 *     a == pre + slice + post  #=> true
 *
 */

[murphy]


Re: Proposal: Array#walker

by Wolfgang Nádasi-donner-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

There is one big difference between the actual proposals and my original
"walker" usage.

I wanted to have a Method of class Array, which "walks" through an Array object
like "each" without using any index at all, and presents the left and the right
context of the actual element of the "each like" processing inside the block.

The variant of this method, that creates an Enumerator object when used without
a block, can be used in map/filter/reduce chains.

A variant of an method like this which uses indices, is different from my
original approach.

Wolfgang Nádasi-Donner


Re: Proposal: Array#walker

by trans :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



On Nov 14, 7:47 pm, Wolfgang Nádasi-Donner <ed.oda...@...>
wrote:

> There is one big difference between the actual proposals and my original
> "walker" usage.
>
> I wanted to have a Method of class Array, which "walks" through an Array object
> like "each" without using any index at all, and presents the left and the right
> context of the actual element of the "each like" processing inside the block.
>
> The variant of this method, that creates an Enumerator object when used without
> a block, can be used in map/filter/reduce chains.
>
> A variant of an method like this which uses indices, is different from my
> original approach.

#each_iteration does not use index.

plus, what happens if you later decide you DO need the index too? then
your method will not be so useful.

i'm not against your method, mind you. i just agree with others that
it is probably too specialized for core. i am considering adding it to
Facets. but it's important to look at the alternatives first.
#each_iteration might be a more flexible approach, if not quite as
convenient.

T.


Re: Proposal: Array#walker

by Kornelius Kalnbach :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Wolfgang Nádasi-Donner wrote:
> I wanted to have a Method of class Array, which "walks" through an Array
> object like "each" without using any index at all, and presents the left
> and the right context of the actual element of the "each like"
> processing inside the block.
I know :) I wanted to propose a more general method that would still
help you to simpify / speed up your code. there currently is no method
to split an array into two with one call based on indices.

even a method that splits an array into two at a given index seems to
specialized for me. that's why split_at should work as the opposite of
Array#slice/Array#[], which has well-known semantics. (a syntax like
a]i[ would be funny, but please ignore it ^_^)

I'm not sure about the name though..."at" sounds like it always takes an
index. however, here's a version that does just that:

VALUE
rb_ary_split_at(VALUE ary, VALUE pos)
{
    long index = NUM2LONG(pos);

    if (RARRAY_LEN(ary) == 0) {
        return rb_assoc_new(rb_ary_new2(0), rb_ary_new2(0));
    }

    if (index < 0) {
        index += RARRAY_LEN(ary);
    }

    /* special cases */
    if (index < 0) {
        return rb_assoc_new(rb_ary_new2(0), rb_ary_dup(ary));
    }
    if (index >= RARRAY_LEN(ary)) {
        return rb_assoc_new(rb_ary_dup(ary), rb_ary_new2(0));
    }

    return rb_assoc_new(
        rb_ary_subseq(ary, 0, index),
        rb_ary_subseq(ary, index + 1, RARRAY_LEN(ary) - (index + 1)) );
}

irb(main):005:0> a = Array(1..10); a.each_with_index { |el,i|
  pre, post = a.split_at(i); p [pre, el, post] }
[[], 1, [2, 3, 4, 5, 6, 7, 8, 9, 10]]
[[1], 2, [3, 4, 5, 6, 7, 8, 9, 10]]
[[1, 2], 3, [4, 5, 6, 7, 8, 9, 10]]
[[1, 2, 3], 4, [5, 6, 7, 8, 9, 10]]
[[1, 2, 3, 4], 5, [6, 7, 8, 9, 10]]
[[1, 2, 3, 4, 5], 6, [7, 8, 9, 10]]
[[1, 2, 3, 4, 5, 6], 7, [8, 9, 10]]
[[1, 2, 3, 4, 5, 6, 7], 8, [9, 10]]
[[1, 2, 3, 4, 5, 6, 7, 8], 9, [10]]
[[1, 2, 3, 4, 5, 6, 7, 8, 9], 10, []]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

[murphy]


Re: Proposal: Array#walker

by Wolfgang Nádasi-donner-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Trans schrieb:
> i'm not against your method, mind you. i just agree with others that
> it is probably too specialized for core. i am considering adding it to
> Facets.

Oops - it is not easy to express in English what I really mean. I don't
understand your argumentation as "against the method". I use it in the form I
have it now, it is easy to write and very short. If you want to have it in
Facets or in another library, the decision is up to the library designer or, if
it is wanted to put into the kernel, the decision is up to Matz, I believe.

I personally have no special intention to see the method somewhere in
libtraries, because I need less that a minute to rewrite it, if need it.

To write this proposal came from another point of view. Class Array contains two
different views to its objects - the Array view via indices, als used in
classical Array types, and the list view, with everything that is influenced by
functional programming. The last view allows to add/remove elements to/from both
ends of the list, to walk through the list, map/filter/reduce combinations, etc.
In addition there are some methods that combine both views, e.g. "each_with_index".

In Ruby 1.9 some method are added for the list view - Array#permutation,
Array#combination, Array#product, which can be used with a block or will produce
an Enumerator object.

 From this viewpoint I recognized when writing the "walker" method for a special
reason, that in class Array a method is missing, that produces an Enumumerator
object or uses a block, which delivers each object of the Array together with
the pre-list and post-list context.

Thats all.

Wolfgang Nádasi-Donner


Re: Proposal: Array#walker

by Nikolai Weibull-11 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Nov 15, 2007 11:32 AM, Wolfgang Nádasi-Donner <ed.odanow@...> wrote:

> In Ruby 1.9 some method are added for the list view - Array#permutation,
> Array#combination, Array#product, which can be used with a block or will produce
> an Enumerator object.
>
>  From this viewpoint I recognized when writing the "walker" method for a special
> reason, that in class Array a method is missing, that produces an Enumumerator
> object or uses a block, which delivers each object of the Array together with
> the pre-list and post-list context.
>
> Thats all.

And the concencus so far has been that this isn't useful enough to have in core.

Trans suggested that he might add it to his library that currently
contains a lot of stuff that isn't useful enough to have in core, but
yes, that's a separate issue.


Re: Proposal: Array#walker

by Wolfgang Nádasi-donner-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Nikolai Weibull schrieb:
> Trans suggested that he might add it to his library that currently
> contains a lot of stuff that isn't useful enough to have in core, but
> yes, that's a separate issue.
I think it is more an example for extending standard classes in a Ruby book.

Wolfgang Nádasi-Donner


Re: Proposal: Array#walker

by trans :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



On Nov 15, 4:49 am, murphy <mur...@...> wrote:

> VALUE
> rb_ary_split_at(VALUE ary, VALUE pos)
> {
>     long index = NUM2LONG(pos);
>
>     if (RARRAY_LEN(ary) == 0) {
>         return rb_assoc_new(rb_ary_new2(0), rb_ary_new2(0));
>     }
>
>     if (index < 0) {
>         index += RARRAY_LEN(ary);
>     }
>
>     /* special cases */
>     if (index < 0) {
>         return rb_assoc_new(rb_ary_new2(0), rb_ary_dup(ary));
>     }
>     if (index >= RARRAY_LEN(ary)) {
>         return rb_assoc_new(rb_ary_dup(ary), rb_ary_new2(0));
>     }
>
>     return rb_assoc_new(
>         rb_ary_subseq(ary, 0, index),
>         rb_ary_subseq(ary, index + 1, RARRAY_LEN(ary) - (index + 1)) );
>
> }
>
> irb(main):005:0> a = Array(1..10); a.each_with_index { |el,i|
>   pre, post = a.split_at(i); p [pre, el, post] }
> [[], 1, [2, 3, 4, 5, 6, 7, 8, 9, 10]]
> [[1], 2, [3, 4, 5, 6, 7, 8, 9, 10]]
> [[1, 2], 3, [4, 5, 6, 7, 8, 9, 10]]
> [[1, 2, 3], 4, [5, 6, 7, 8, 9, 10]]
> [[1, 2, 3, 4], 5, [6, 7, 8, 9, 10]]
> [[1, 2, 3, 4, 5], 6, [7, 8, 9, 10]]
> [[1, 2, 3, 4, 5, 6], 7, [8, 9, 10]]
> [[1, 2, 3, 4, 5, 6, 7], 8, [9, 10]]
> [[1, 2, 3, 4, 5, 6, 7, 8], 9, [10]]
> [[1, 2, 3, 4, 5, 6, 7, 8, 9], 10, []]
> => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

This is nice. Add in an extra length option, and IMO it's a worthy of
consideration for core.

T.


Array#split_at

by Kornelius Kalnbach :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Trans wrote:
> On Nov 15, 4:49 am, murphy <mur...@...> wrote:
>> VALUE
>> rb_ary_split_at(VALUE ary, VALUE pos)
> This is nice. Add in an extra length option, and IMO it's a worthy of
> consideration for core.
I tried, and it's working fine with tests, but I have performance issues:

      short.each_with_index do |el, i|
        pre, post = short.split_at(i)
      end

is always slower (!) than

      last = long.size - 1
      short.each_with_index do |el, i|
        pre = short.first(i)
        post = short.last(last - i)
      end

and I assume it's because of the parallel assignment. I'm using
ary_shared_array just like #first and #last, rb_assoc_new for the result
and avoid rb_scan_args. can anything more be done to make it faster?

I'm still not sure about the name...
[murphy]

Index: array.c
===================================================================
--- array.c (revision 13948)
+++ array.c (working copy)
@@ -781,6 +781,76 @@
     return rb_ary_entry(ary, NUM2LONG(pos));
 }
 
+/*
+ *  call-seq:
+ *     array.split_at(index)          -> [left_array, right_array]
+ *     array.split_at(start, length)  -> [left_array, right_array]
+ *
+ *  Splits an array into two parts, with the first one containing
+ *  all elements before the given range, and the second one containing
+ *  all elements after it. See Array#slice for information about the
+ *  range expressions.
+ *
+ *     a = [ "a", "b", "c", "d", "e" ]
+ *     a.split_at(0)          #=> [[], ["b", "c", "d", "e"]]
+ *     a.split_at(2)          #=> [["a", "b"], ["d", "e"]]
+ *     a.split_at(-1)         #=> [["a", "b", "c", "d"], []]
+ *     a.split_at(1, 2)       #=> [["a"], ["d", "e"]]
+ *     a.split_at(1, 0)       #=> [["a"], ["b", "c", "d", "e"]]
+ *  
+ */
+
+VALUE
+rb_ary_split_at(int argc, VALUE *argv, VALUE ary)
+{
+    return Qnil;
+    
+    long pos, len;
+    len = 1;  /* default */
+    
+    switch (argc) {
+      case 2:
+ len = NUM2LONG(argv[1]);
+ /* fall through */
+      case 1:
+ pos = NUM2LONG(argv[0]);
+ break;
+      default:
+ rb_scan_args(argc, argv, "11", 0, 0);
+    }
+    
+    if (RARRAY_LEN(ary) == 0) {
+ return rb_assoc_new(rb_ary_new2(0), rb_ary_new2(0));
+    }
+    
+    if (pos < 0) {
+ pos += RARRAY_LEN(ary);
+    }
+    
+    /* pos out of bounds? */
+    if (pos <= 0) {
+      return rb_assoc_new(rb_ary_new2(0), ary_shared_array(rb_cArray, ary));
+    }
+    
+    /* pos+len out of bounds? */
+    if (len < 0) {
+ /* Array#slice doesn't fail on negative lengths, so we copy */
+ len = 0;
+    }
+    if (pos + len > RARRAY_LEN(ary)) {
+      return rb_assoc_new(ary_shared_array(rb_cArray, ary), rb_ary_new2(0));
+    }
+    
+    VALUE left  = ary_shared_array(rb_cArray, ary);
+    VALUE right = ary_shared_array(rb_cArray, ary);
+    
+    RARRAY(left)->len = pos;
+    RARRAY(right)->ptr += pos + len;
+    RARRAY(right)->len += RARRAY_LEN(ary) - (pos + len);
+    
+    return rb_assoc_new(left, right);
+}
+
 /*
  *  call-seq:
  *     array.first     ->   obj or nil
@@ -2557,7 +2627,7 @@
     hash = ary_make_hash(ary2, 0);
 
     if (RHASH_EMPTY_P(hash))
-        return ary3;
+ return ary3;
 
     for (i=0; i<RARRAY_LEN(ary1); i++) {
  v = vv = rb_ary_elt(ary1, i);
@@ -3288,6 +3358,7 @@
 
     rb_define_method(rb_cArray, "slice", rb_ary_aref, -1);
     rb_define_method(rb_cArray, "slice!", rb_ary_slice_bang, -1);
+    rb_define_method(rb_cArray, "split_at", rb_ary_split_at, -1);
 
     rb_define_method(rb_cArray, "assoc", rb_ary_assoc, 1);
     rb_define_method(rb_cArray, "rassoc", rb_ary_rassoc, 1);

Index: test/ruby/test_array.rb
===================================================================
--- test/ruby/test_array.rb (revision 13948)
+++ test/ruby/test_array.rb (working copy)
@@ -1103,6 +1103,80 @@
     assert_equal(@cls[], @cls[].sort!)
   end
 
+  def test_split_at
+    a = @cls[*(1..100).to_a]
+
+    assert_equal [ @cls[],                      a ],  a.split_at(0,0)
+    assert_equal [ @cls[1],  @cls[*(2..100).to_a] ],  a.split_at(1,0)
+    assert_equal [ @cls[*(1..99).to_a], @cls[100] ],  a.split_at(99,0)
+    assert_equal [ a,                      @cls[] ],  a.split_at(100,0)
+    assert_equal [ @cls[*(1..99).to_a], @cls[100] ],  a.split_at(-1,0)
+    assert_equal [ @cls[*(1..98).to_a], @cls[99,100] ],  a.split_at(-2,0)
+    assert_equal [ @cls[],                      a ],  a.split_at(-100,0)
+    assert_equal [ @cls[],                      a ],  a.split_at(-101,0)
+
+    assert_equal [ @cls[],   @cls[*(2..100).to_a] ],  a.split_at(0)
+    assert_equal [ @cls[1],  @cls[*(3..100).to_a] ],  a.split_at(1)
+    assert_equal [ @cls[*(1..99).to_a],    @cls[] ],  a.split_at(99)
+    assert_equal [ a,                      @cls[] ],  a.split_at(100)
+    assert_equal [ @cls[*(1..99).to_a],    @cls[] ],  a.split_at(-1)
+    assert_equal [ @cls[*(1..98).to_a], @cls[100] ],  a.split_at(-2)
+    assert_equal [ @cls[],   @cls[*(2..100).to_a] ],  a.split_at(-100)
+    assert_equal [ @cls[],                      a ],  a.split_at(-101)
+
+    assert_equal [ @cls[],   @cls[*(2..100).to_a] ],  a.split_at(0,1)
+    assert_equal [ @cls[1],  @cls[*(3..100).to_a] ],  a.split_at(1,1)
+    assert_equal [ @cls[*(1..99).to_a],    @cls[] ],  a.split_at(99,1)
+    assert_equal [ a,                      @cls[] ],  a.split_at(100,1)
+    assert_equal [ @cls[*(1..99).to_a],    @cls[] ],  a.split_at(-1,1)
+    assert_equal [ @cls[*(1..98).to_a], @cls[100] ],  a.split_at(-2,1)
+    assert_equal [ @cls[],   @cls[*(2..100).to_a] ],  a.split_at(-100,1)
+    assert_equal [ @cls[],                      a ],  a.split_at(-101,1)
+
+    assert_equal [ @cls[],   @cls[*(3..100).to_a] ],  a.split_at(0,2)
+    assert_equal [ @cls[1],  @cls[*(4..100).to_a] ],  a.split_at(1,2)
+    assert_equal [ @cls[*(1..99).to_a],    @cls[] ],  a.split_at(99,2)
+    assert_equal [ a,                      @cls[] ],  a.split_at(100,2)
+    assert_equal [ @cls[*(1..99).to_a],    @cls[] ],  a.split_at(-1,2)
+    assert_equal [ @cls[*(1..98).to_a],    @cls[] ],  a.split_at(-2,2)
+    assert_equal [ @cls[],   @cls[*(3..100).to_a] ],  a.split_at(-100,2)
+    assert_equal [ @cls[],                      a ],  a.split_at(-101,2)
+
+    assert_equal [ @cls[],              @cls[100] ],  a.split_at(0,99)
+    assert_equal [ @cls[1],                @cls[] ],  a.split_at(1,99)
+    assert_equal [ @cls[*(1..99).to_a],    @cls[] ],  a.split_at(99,99)
+    assert_equal [ a,                      @cls[] ],  a.split_at(100,99)
+    assert_equal [ @cls[*(1..99).to_a],    @cls[] ],  a.split_at(-1,99)
+    assert_equal [ @cls[*(1..98).to_a],    @cls[] ],  a.split_at(-2,99)
+    assert_equal [ @cls[],              @cls[100] ],  a.split_at(-100,99)
+    assert_equal [ @cls[],                      a ],  a.split_at(-101,99)
+
+    assert_equal [ @cls[],                 @cls[] ],  a.split_at(0,100)
+    assert_equal [ @cls[1],                @cls[] ],  a.split_at(1,100)
+    assert_equal [ @cls[*(1..99).to_a],    @cls[] ],  a.split_at(99,100)
+    assert_equal [ a,                      @cls[] ],  a.split_at(100,100)
+    assert_equal [ @cls[*(1..99).to_a],    @cls[] ],  a.split_at(-1,100)
+    assert_equal [ @cls[*(1..98).to_a],    @cls[] ],  a.split_at(-2,100)
+    assert_equal [ @cls[],                 @cls[] ],  a.split_at(-100,100)
+    assert_equal [ @cls[],                      a ],  a.split_at(-101,100)
+
+    assert_equal [ @cls[*(1..9).to_a], @cls[*(13..100).to_a] ], a.split_at(9, 3)
+    assert_equal [ @cls[*(1..9).to_a], @cls[*(13..100).to_a] ], a.split_at(-91, 3)
+
+    # assert_equal [ @cls[],   @cls[*(1..100).to_a] ], a.split_at(0..0)
+    # assert_equal [ @cls[*(1..99).to_a],    @cls[] ], a.split_at(99..99)
+    # assert_equal [ @cls[*(1..100).to_a],   @cls[] ], a.split_at(100..100)
+    # assert_equal [ @cls[*(1..99).to_a],    @cls[] ], a.split_at(99..200)
+    # assert_equal [ @cls[*(1..99).to_a],    @cls[] ], a.split_at(-1..-1)
+    # assert_equal [ @cls[*(1..98).to_a], @cls[100] ], a.split_at(-2..-2)
+    #
+    # assert_equal [ @cls[*(1..9).to_a], @cls[*(13..100).to_a] ], a.split_at(9..11)
+    # assert_equal [ @cls[*(1..9).to_a], @cls[*(13..100).to_a] ], a.split_at(-91..-89)
+    
+    assert_equal [ @cls[*(1..10).to_a], @cls[*(11..100).to_a] ], a.split_at(10, -3)
+    # assert_equal [ @cls[*(1..10).to_a], @cls[*(11..100).to_a] ], a.split_at(10..7)
+  end
+
   def test_to_a
     a = @cls[ 1, 2, 3 ]
     a_id = a.__id__
< Prev | 1 - 2 | Next >