Array#first is not Array#[0]

I made a class that is a subclass of Array :

class MyClass < Array
  def (i)
    "Here you go: #{super(i)}"
  end
end

and I thought that Array#first and Array#last was just syntactic sugar
that would translate to Array#[0] and Array#[-1] respectively. But
it's not:

g = MyClass.new([1,2,3])

=> [1, 2, 3]

g[0]

=> "Here you go: 1"

g.first

=> 1

Now, isn't that really, really crazy? Am I supposed to define
MyClass#first and MyClass#last separately???

Best regards,
Fredrik

Fredrik wrote:

I made a class that is a subclass of Array :

class MyClass < Array
  def (i)
    "Here you go: #{super(i)}"
  end
end

and I thought that Array#first and Array#last was just syntactic sugar
that would translate to Array#[0] and Array#[-1] respectively. But
it's not:

> g = MyClass.new([1,2,3])
=> [1, 2, 3]
> g[0]
=> "Here you go: 1"
> g.first
=> 1

Now, isn't that really, really crazy? Am I supposed to define
MyClass#first and MyClass#last separately???

C:\scripts>ruby temp.rb
Here you go: 1
1

C:\scripts>cat temp.rb
class MyClass < Array
~ def (i)
~ "Here you go: #{super(i)}"
~ end
end

g = MyClass.new([1,2,3])

puts g[0]
puts g.first

C:\scripts>ruby -v
ruby 1.8.6 (2008-03-03 patchlevel 114) [i386-mswin32]

Hm....

- --
Phillip Gawlowski
Twitter: twitter.com/cynicalryan
Blog: http://justarubyist.blogspot.com

~ Calvin: Can you make a living playing silly games? His Dad:
Actually, you can be among the most overpaid people on the planet.

I see that Array#each wont do what I want it to do either. I guess I
have to do it like this:

class MyClass < Array
  def initialize(arg)
    super(arg.map{ |i| "Here you go: #{i}"})
  end
end

This would give me all the behavior I want, except that it's a
wasteful use of memory if I have a really big array and want to
include the same text in all my array elements. I just want to have
some fixed text attached to ANY operations I do with array elements.
To achieve that, do I really need to do it like this?

/Fredrik

"David A. Black" <dblack@rubypal.com> writes:

···

On Tue, 29 Apr 2008, Fredrik wrote:

What I am still thinking about though, is this:
Why on earth is Array#first not calling Array#[0] ???

The real question is: why should it? They're completely separate
methods.

I can't think of a good reason to ever have: Array#first != Array#(0)

# include the same text in all my array elements. I just want to have
# some fixed text attached to ANY operations I do with array elements.

there are other better ways, but i just extended your initial thought/code,

class MyArray < Array
   Array.methods.each do |m|
     define_method(m) do
       "Here you go: #{super}"
     end
   end
end
#=>...

g=MyArray.new [1,2,3]
#=> Here you go: [1, 2, 3]
g[1]
#=> "Here you go: 2"
g[0]
#=> "Here you go: 1"
g.first
#=> 1
g.last
#=> 3
g[-1]
#=> "Here you go: 3"

kind regards -botp

···

from: Fredrik [mailto:fredjoha@gmail.com]

Brian Adkins <lojicdotcom@gmail.com> writes:

"David A. Black" <dblack@rubypal.com> writes:

What I am still thinking about though, is this:
Why on earth is Array#first not calling Array#[0] ???

The real question is: why should it? They're completely separate
methods.

I can't think of a good reason to ever have: Array#first != Array#(0)

Sorry about that. I'm having a bit of trouble with the Gnus news reader,
so this got attached to the wrong message - it should've followed
David's of course.

···

On Tue, 29 Apr 2008, Fredrik wrote:

Hi --

···

On Wed, 30 Apr 2008, Brian Adkins wrote:

"David A. Black" <dblack@rubypal.com> writes:

On Tue, 29 Apr 2008, Fredrik wrote:

What I am still thinking about though, is this:
Why on earth is Array#first not calling Array#[0] ???

The real question is: why should it? They're completely separate
methods.

I can't think of a good reason to ever have: Array#first != Array#(0)

That's not the question, though; the question is why #first doesn't
actually call #.

David

--
Rails training from David A. Black and Ruby Power and Light:
   INTRO TO RAILS June 9-12 Berlin
   ADVANCING WITH RAILS June 16-19 Berlin
   INTRO TO RAILS June 24-27 London (Skills Matter)
See http://www.rubypal.com for details and updates!

Hi,

"David A. Black" <dblack@rubypal.com> writes:
> The real question is: why should it? They're completely separate
> methods.

I can't think of a good reason to ever have: Array#first != Array#(0)

They're still separate methods. The point is, internally, Ruby saves another
dynamic method dispatch by hard-coding Array#first to just grab the first
item from the internal data structure -- just like calling Array#(0)
would, but it's faster. It might even make a noticeable difference in some
application that calls Array#first and #last a lot.. (I don't propose such
cases do exist, but every small part counts)

array.c:
static VALUE
rb_ary_first(int argc, VALUE *argv, VALUE ary)
{
if (argc == 0) {
if (RARRAY_LEN(ary) == 0) return Qnil;
return RARRAY_PTR(ary)[0];
}
else {
return ary_shared_first(argc, argv, ary, Qfalse);
}
}

Here seen retrieving the first item manually.

Arlen

···

On Wed, Apr 30, 2008 at 4:50 AM, Brian Adkins <lojicdotcom@gmail.com> wrote:

#..
# Array.methods.each do |m|
       ^^^^^^^^^^^^^

make that

       Array.instance_methods.each do |m|

sorry, my bad.

so,

class MyArray < Array
   Array.instance_methods.each do |m|
     define_method(m) do
       "Here you go: #{super}"
     end
   end
end
#=>....

g=MyArray.new [1,2,3]
#=> Here you go: [1, 2, 3]
g[0]
#=> "Here you go: 1"
g.first
#=> "Here you go: 1"
g[-1]
#=> "Here you go: 3"
g.last
#=> "Here you go: 3"
g[2]
#=> "Here you go: 3"

kind regards -botp

···

From: Peña, Botp [mailto:botp@delmonte-phil.com]

But that doesn't work (though I don't understand why). I want it to
always say "Here you go: ...", no matter how I retrieve an element out
of the array.

···

On Apr 29, 5:36 pm, Peña, Botp <b...@delmonte-phil.com> wrote:

from: Fredrik [mailto:fredj...@gmail.com]
# include the same text in all my array elements. I just want to have
# some fixed text attached to ANY operations I do with array elements.

there are other better ways, but i just extended your initial thought/code,

class MyArray < Array
   Array.methods.each do |m|
     define_method(m) do
       "Here you go: #{super}"
     end
   end
end
#=>...

g=MyArray.new [1,2,3]
#=> Here you go: [1, 2, 3]
g[1]
#=> "Here you go: 2"
g[0]
#=> "Here you go: 1"
g.first
#=> 1
g.last
#=> 3
g[-1]
#=> "Here you go: 3"

kind regards -botp

Maybe is better using instance_methods instead

class MyArray < Array
   Array.instance_methods.each do |m|
    define_method(m) do
      "Here you go: #{super}"
    end
  end
end

irb(main):033:0> g=MyArray.new [1,2,3]
=> Here you go: [1, 2, 3]
irb(main):034:0> g.first
=> "Here you go: 1"
irb(main):035:0>

···

On Tue, Apr 29, 2008 at 8:36 AM, Peña, Botp <botp@delmonte-phil.com> wrote:

from: Fredrik [mailto:fredjoha@gmail.com]
# include the same text in all my array elements. I just want to have

# some fixed text attached to ANY operations I do with array elements.

there are other better ways, but i just extended your initial thought/code,

class MyArray < Array
   Array.methods.each do |m|
     define_method(m) do
       "Here you go: #{super}"
     end
   end
end
#=>...

g=MyArray.new [1,2,3]
#=> Here you go: [1, 2, 3]
g[1]
#=> "Here you go: 2"

g[0]
#=> "Here you go: 1"
g.first
#=> 1
g.last
#=> 3
g[-1]
#=> "Here you go: 3"

kind regards -botp

--
Go outside! The graphics are amazing!

Ok, I got it. So Array#first is a matter of optimization. I thought it
was syntactic sugar that the Ruby interpreter translates to Array#[]
(0).
But I understand now that Array#first is not there for my coding
convenience but rather for computational speed.

Thanks for clearing it up!
Fredrik

Thanks! But how about this then:

g.size

=> "Here you go: 3"

That's a strange size :slight_smile:
So this solution solves some problems but creates new ones instead...

Fredrik wrote;

But I understand now that Array#first is not there for my coding
convenience but rather for computational speed.

I'm using some other scripting language (PHP), and when it becomes known
that method A to do something is faster than method B to do something,
everybody turns to use method A no matter how ugly it looks. (When I say
"method" I don't mean the object orient meaning. I simply mean "way".)

Now,

If #first was slower than #[0], _nobody_ would have used it!
Having #first is nice. It has merit (clarity). So the only way to have
it and have people use it is not to have it slower than #[0].

So on the contrary: #first is there for your convenient, not for
computational speed.

···

--
Posted via http://www.ruby-forum.com/\.

# irb> g.size
# => "Here you go: 3"

···

From: Fredrik [mailto:fredjoha@gmail.com]
#
# That's a strange size :slight_smile:
# So this solution solves some problems but creates new ones instead...

you wanted to put text on all ops

so even

g.last

=> "Here you go: 3"

is already strange, right?

my suggestion is if you just want to view some output w text, just wrapped it in a text,

like so,

puts "Here you go: #{g.last}"

kind regards -botp

# From: Fredrik [mailto:fredjoha@gmail.com]
# # irb> g.size
# # => "Here you go: 3"
# # That's a strange size :slight_smile:
# # So this solution solves some problems but creates new ones
# instead...
# you wanted to put text on all ops
# so even
# irb> g.last
# => "Here you go: 3"
# is already strange, right?
# my suggestion is if you just want to view some output w text,
# just wrapped it in a text,
# like so,
# puts "Here you go: #{g.last}"

ok, how about combining what we want, ie, put the text yet still returning the object, so

class MyArray < Array
   Array.instance_methods.each do |m|
     define_method(m) do
       r=super
       puts "Here you go: #{r}"
       r
     end
   end
end
#=> ...

g=MyArray.new [1,2,3]
Here you go: [1, 2, 3]
#=> [1, 2, 3]
g[0]
Here you go: 1
#=> 1
g.first
Here you go: 1
#=> 1
g.last
Here you go: 3
#=> 3
g.size
Here you go: 3
#=> 3
puts "size is: #{g.size}"
Here you go: 3
size is: 3
#=> nil

is that ok?

kind regards -botp

···

From: Peña, Botp [mailto:botp@delmonte-phil.com]

I'm sorry, I think I am too vague about what I want to achieve. What I
want is to have is an "array" that behaves exactly like an array
EXCEPT that each element has some text attached to it. Array#size is
not an element but Array#last is an element for example.

Maybe it's not doable in any other way than copying the text into each
element since I would like this text to follow into Array#each,
Array#map and all those methods.

What I am still thinking about though, is this:
Why on earth is Array#first not calling Array#[0] ???

Fredrik

# I'm sorry, I think I am too vague about what I want to achieve. What I
# want is to have is an "array" that behaves exactly like an array
# EXCEPT that each element has some text attached to it. Array#size is
# not an element but Array#last is an element for example.
# Maybe it's not doable in any other way than copying the text into each
# element since I would like this text to follow into Array#each,
# Array#map and all those methods.

ok, then just modify those you want

eg,

class Array2 < Array
   %w([] first last).each do |m|
     define_method(m) do
        r = super
        puts "Here you go: #{r}"
        r
     end
   end
end
#=> ["[]", "first", "last"]
a=Array2.new [1,2,3]
#=> [1, 2, 3]
a[0]
Here you go: 1
#=> 1
a.first
Here you go: 1
#=> 1
a.size
#=> 3

# What I am still thinking about though, is this:
# Why on earth is Array#first not calling Array#[0] ???

that would be calling two methods, and would be quite expensive, right?

kind regards -botp

···

From: Fredrik [mailto:fredjoha@gmail.com]

Hi --

I'm sorry, I think I am too vague about what I want to achieve. What I
want is to have is an "array" that behaves exactly like an array
EXCEPT that each element has some text attached to it. Array#size is
not an element but Array#last is an element for example.

Maybe it's not doable in any other way than copying the text into each
element since I would like this text to follow into Array#each,
Array#map and all those methods.

What I am still thinking about though, is this:
Why on earth is Array#first not calling Array#[0] ???

The real question is: why should it? They're completely separate
methods.

Mind you, if you dig through the ruby-lang archives, you'll find Ben
Tilly and Mathieu Bouchard patiently explaining to me the answer to
the same question :slight_smile: My initial assumption was that all get and set
operations would go through and =. They don't, though. It would
make it very easy to do Perl-style "tie" operations if they did, but
it would also be less efficient; in other words, not going through
and = is an optimization.

I'm perennially working on a "tie" module, and never seem to get it
how I want it... but I'll have another look at it and see how close
it's getting.

David

···

On Tue, 29 Apr 2008, Fredrik wrote:

--
Rails training from David A. Black and Ruby Power and Light:
   INTRO TO RAILS June 9-12 Berlin
   ADVANCING WITH RAILS June 16-19 Berlin
   INTRO TO RAILS June 24-27 London (Skills Matter)
See http://www.rubypal.com for details and updates!