Array#first is not Array#[0]

F

Fredrik

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:

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

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

Best regards,
Fredrik
 
P

Phillip Gawlowski

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

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:
|
| irb> g = MyClass.new([1,2,3])
| => [1, 2, 3]
| irb> g[0]
| => "Here you go: 1"
| irb> 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.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.8 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkgW1xUACgkQbtAgaoJTgL8ozACfeThkZk1S/FDCSSYeSBEhog6V
PA4AoIQJjVJlB092JSYBPAXA4jF7UYfE
=c2AX
-----END PGP SIGNATURE-----
 
F

Fredrik

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
 
P

Peña, Botp

from: Fredrik [mailto:[email protected]]=20
# 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
#=3D>...

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

kind regards -botp
 
F

Fredrik

from: Fredrik [mailto:[email protected]]
# 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

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.
 
P

Peña, Botp

From: Pe=F1a, Botp [mailto:[email protected]]=20
#..
# Array.methods.each do |m|
^^^^^^^^^^^^^

make that=20

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
#=3D>....

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

kind regards -botp
 
S

Sandro Paganotti

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=3DMyArray.new [1,2,3]
=3D> Here you go: [1, 2, 3]
irb(main):034:0> g.first
=3D> "Here you go: 1"
irb(main):035:0>


from: Fredrik [mailto:[email protected]]
# 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/co= de,

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

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

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

kind regards -botp



--=20
Go outside! The graphics are amazing!
 
F

Fredrik

Thanks! But how about this then:

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

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

Peña, Botp

From: Fredrik [mailto:[email protected]]=20
# irb> g.size
# =3D> "Here you go: 3"
#=20
# That's a strange size :)
# So this solution solves some problems but creates new ones instead...

you wanted to put text on all ops

so even=20

irb> g.last
=3D> "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
 
P

Peña, Botp

From: Pe=F1a, Botp [mailto:[email protected]]=20
# From: Fredrik [mailto:[email protected]]=20
# # irb> g.size
# # =3D> "Here you go: 3"
# # That's a strange size :)
# # So this solution solves some problems but creates new ones=20
# instead...
# you wanted to put text on all ops
# so even=20
# irb> g.last
# =3D> "Here you go: 3"
# is already strange, right?
# my suggestion is if you just want to view some output w text,=20
# 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=3Dsuper
puts "Here you go: #{r}"
r
end
end
end
#=3D> ...

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

is that ok?

kind regards -botp
 
F

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.

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

Fredrik
 
P

Peña, Botp

From: Fredrik [mailto:[email protected]]=20
# 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 =3D super
puts "Here you go: #{r}"
r
end
end
end
#=3D> ["[]", "first", "last"]
a=3DArray2.new [1,2,3]
#=3D> [1, 2, 3]
a[0]
Here you go: 1
#=3D> 1
a.first
Here you go: 1
#=3D> 1
a.size
#=3D> 3


=20
# 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
 
D

David A. Black

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 :) 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

--
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!
 
B

Brian Adkins

David A. Black said:
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)
 
B

Brian Adkins

Brian Adkins said:
David A. Black said:
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.
 
D

David A. Black

Hi --

David A. Black said:
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!
 
A

Arlen Cuss

[Note: parts of this message were removed to make it a legal post.]

Hi,

David A. Black said:
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
 
F

Fredrik

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
 
A

Albert Schlef

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top