deciphering poignant guide chapter 4 example

J

john_sips_tea

Hi,

I'm reading through the poignant guide, and am a bit stuck
at the end of chapter 4
( http://poignantguide.net/ruby/chapter-4.html ). It's
regarding the kitty_toys example:

#!/usr/bin/ruby

kitty_toys =
[:shape => 'sock', :fabric => 'cashmere'] +
[:shape => 'mouse', :fabric => 'calico'] +
[:shape => 'eggroll', :fabric => 'chenille']

kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
end

and I've actually got three sticking points with it:

1. For one thing, I don't understand the point of the ":shape"
syntax. I don't understand why the author doesn't just write
the string "shape" instead. What exactly is a "Symbol" object
for?

The poinant guide says it's just "words that look just like
variables". And "Symbols are lightweight strings." But that
doesn't help me much. The PickAxe 2nd ed, in chapter 3 says,
"The construct :artist is an expression that returns a Symbol
object corresponding to artist.", so, I can understand that
(i.e. that there's some Symbol class and we're getting an
instance of it by using that notation), but I'm still not
getting the point... why not just use strings? What does
having "Symbols" buy the programmer?

2. Next up, why is

kitty_toys =
[:shape => 'sock', :fabric => 'cashmere'] +
[:shape => 'mouse', :fabric => 'calico'] +
[:shape => 'eggroll', :fabric => 'chenille']

supposed to be shorthand for

kitty_toys = [
{:shape => 'sock', :fabric => 'cashmere'},
{:shape => 'mouse', :fabric => 'calico'},
{:shape => 'eggroll', :fabric => 'chenille'}
]

How does that work? (Hmm... what does adding Arrays
in Ruby mean anyway? In Python it concatenates.)
Why does this shorthand exist? Hmm,.. it doesn't seem
to be saving much finger typing...

3. Finally, at the end of that example given in the poignant
guide:

#!/usr/bin/ruby

# ... Create kitty_toys as shown above, then

kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
end

how does that "kitty_toys.sort_by" line work? I believe that
braces and "do ... end" are equivalent, so it looks to me like
there's some kind of "loop-in-a-loop" going on, as in:

# Warning, Python code follows:
for i in range(1, 8):
for j in range(1, 4):
print i, j

Is there a more verbose way of writing that kitty_toys snippet
to make it a bit more obvious what's going on? I mean, I guess
the sort_by method is probably looking for something to sort
kitty_toys on, and we're telling it to use what it finds in
toy[:shape] for each hash it iterates over, but then, is that
next "each" looping over items in a given hash, or ... gah. I'm
not getting it. :)

Thanks,
---John
 
W

William James

Hi,

I'm reading through the poignant guide, and am a bit stuck
at the end of chapter 4
( http://poignantguide.net/ruby/chapter-4.html ). It's
regarding the kitty_toys example:

#!/usr/bin/ruby

kitty_toys =
[:shape => 'sock', :fabric => 'cashmere'] +
[:shape => 'mouse', :fabric => 'calico'] +
[:shape => 'eggroll', :fabric => 'chenille']

kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
end

and I've actually got three sticking points with it:

1. For one thing, I don't understand the point of the ":shape"
syntax. I don't understand why the author doesn't just write
the string "shape" instead. What exactly is a "Symbol" object
for?

The poinant guide says it's just "words that look just like
variables". And "Symbols are lightweight strings." But that
doesn't help me much. The PickAxe 2nd ed, in chapter 3 says,
"The construct :artist is an expression that returns a Symbol
object corresponding to artist.", so, I can understand that
(i.e. that there's some Symbol class and we're getting an
instance of it by using that notation), but I'm still not
getting the point... why not just use strings? What does
having "Symbols" buy the programmer?

2. Next up, why is

kitty_toys =
[:shape => 'sock', :fabric => 'cashmere'] +
[:shape => 'mouse', :fabric => 'calico'] +
[:shape => 'eggroll', :fabric => 'chenille']

supposed to be shorthand for

kitty_toys = [
{:shape => 'sock', :fabric => 'cashmere'},
{:shape => 'mouse', :fabric => 'calico'},
{:shape => 'eggroll', :fabric => 'chenille'}
]

How does that work? (Hmm... what does adding Arrays
in Ruby mean anyway? In Python it concatenates.)
Why does this shorthand exist? Hmm,.. it doesn't seem
to be saving much finger typing...

3. Finally, at the end of that example given in the poignant
guide:

#!/usr/bin/ruby

# ... Create kitty_toys as shown above, then

kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
end

how does that "kitty_toys.sort_by" line work? I believe that
braces and "do ... end" are equivalent, so it looks to me like
there's some kind of "loop-in-a-loop" going on, as in:

No, kitty_toys.sort_by { |toy| toy[:shape] } simply produces an
array that's sorted by the shape of each toy. Then he
iterates through the array and prints each toy.
# Warning, Python code follows:
for i in range(1, 8):
for j in range(1, 4):
print i, j

Is there a more verbose way of writing that kitty_toys snippet
to make it a bit more obvious what's going on? I mean, I guess
the sort_by method is probably looking for something to sort
kitty_toys on, and we're telling it to use what it finds in
toy[:shape] for each hash it iterates over, but then, is that
next "each" looping over items in a given hash, or ... gah. I'm
not getting it. :)

Thanks,
---John

Here's another way:

kitty_toys =
[ { :shape, 'sock', :fabric, 'cashmere'},
{ :shape, 'mouse', :fabric, 'calico'},
{ :shape, 'eggroll', :fabric, 'chenille'}
]

puts kitty_toys.sort_by { |toy| toy[:shape] }.map { |toy|
"Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
}
 
P

Pierre Barbier de Reuille

Hi,

First, let me warn you that from what you describe, you have a Python=20
viewpoint on the data structures and that it is quite different from=20
many many languages.

(e-mail address removed) a =E9crit :
Hi,

I'm reading through the poignant guide, and am a bit stuck at the end
of chapter 4 ( http://poignantguide.net/ruby/chapter-4.html ). It's
regarding the kitty_toys example:

#!/usr/bin/ruby

kitty_toys =3D [:shape =3D> 'sock', :fabric =3D> 'cashmere'] + [:shape= =3D>
'mouse', :fabric =3D> 'calico'] + [:shape =3D> 'eggroll', :fabric =3D>
'chenille']

kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy| puts "Blixy
has a #{ toy[:shape] } made of #{ toy[:fabric] }" end

and I've actually got three sticking points with it:

1. For one thing, I don't understand the point of the ":shape"
syntax. I don't understand why the author doesn't just write the
string "shape" instead. What exactly is a "Symbol" object for?

The poinant guide says it's just "words that look just like
variables". And "Symbols are lightweight strings." But that doesn't
help me much. The PickAxe 2nd ed, in chapter 3 says, "The construct
:artist is an expression that returns a Symbol object corresponding
to artist.", so, I can understand that (i.e. that there's some Symbol
class and we're getting an instance of it by using that notation),
but I'm still not getting the point... why not just use strings? What
does having "Symbols" buy the programmer?
You can see symbols as read-only strings used to very quickly find=20
informations in, for example, a hash table. The idea is to get an=20
efficient implementation while keeping the code readable by human beings.

As you seem to come from the Python's world, you probably know that=20
Python's strings are immutable. And if you read a little bit about why=20
strings in Python are immutable, you will see it's because they wanted=20
to optimize the method lookup. Also you will learn that short Python's=20
string are coded differently so that a simple integer lookup is used for=20
method lookup. In the end, Python's string are what's called in Ruby=20
Symbol and Ruby's strings have no equivalent in Python, as Python has no=20
mutable string class.
2. Next up, why is

kitty_toys =3D [:shape =3D> 'sock', :fabric =3D> 'cashmere'] + [:shape= =3D>
'mouse', :fabric =3D> 'calico'] + [:shape =3D> 'eggroll', :fabric =3D>
'chenille']

supposed to be shorthand for

kitty_toys =3D [ {:shape =3D> 'sock', :fabric =3D> 'cashmere'}, {:shap= e =3D>
'mouse', :fabric =3D> 'calico'}, {:shape =3D> 'eggroll', :fabric =3D>
'chenille'} ]

How does that work? (Hmm... what does adding Arrays in Ruby mean
anyway? In Python it concatenates.) Why does this shorthand exist?
Hmm,.. it doesn't seem to be saving much finger typing... 3. Finally,
at the end of that example given in the poignant guide:

#!/usr/bin/ruby

# ... Create kitty_toys as shown above, then

kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy| puts "Blixy
has a #{ toy[:shape] } made of #{ toy[:fabric] }" end

how does that "kitty_toys.sort_by" line work? I believe that braces
and "do ... end" are equivalent, so it looks to me like there's some
kind of "loop-in-a-loop" going on, as in:

# Warning, Python code follows: for i in range(1, 8): for j in
range(1, 4): print i, j

Is there a more verbose way of writing that kitty_toys snippet to
make it a bit more obvious what's going on? I mean, I guess the
sort_by method is probably looking for something to sort kitty_toys
on, and we're telling it to use what it finds in toy[:shape] for each
hash it iterates over, but then, is that next "each" looping over
items in a given hash, or ... gah. I'm not getting it. :)
No, as said before, the sort_by returns an array ... which is further=20
processed, so it more subsequent loops.
You can expand it like that :

sorted_kitty =3D kitty_toys.sort_by { |toy| toy[:shape] }
sorted_kitty.each do |toy|
[...]
end

The ruby equivalent of your python code would be :

(1...8).each { |i| (1...4).each { |j| puts i,j} }
 
S

Simen

unknown said:
The poinant guide says it's just "words that look just like
variables". And "Symbols are lightweight strings." But that
doesn't help me much. The PickAxe 2nd ed, in chapter 3 says,
"The construct :artist is an expression that returns a Symbol
object corresponding to artist.", so, I can understand that
(i.e. that there's some Symbol class and we're getting an
instance of it by using that notation), but I'm still not
getting the point... why not just use strings? What does
having "Symbols" buy the programmer?

Symbols are not merely lightweight strings, they represent names in the
Ruby interpreter. Check this sample out to see the difference:

$ irb
irb(main):001:0> "Hello".object_id
=> -605430718
irb(main):002:0> "Hello".object_id
=> -605437098
irb(main):003:0> :hello.object_id
=> 3985678
irb(main):004:0> :hello.object_id
=> 3985678
irb(main):005:0> :hello.object_id == :hello.object_id
=> true
irb(main):006:0> "hello".object_id == "hello".object_id
=> false
irb(main):007:0>

Symbols with the same values are the same objects, that is not the case
with strings. Symbols represents identifiers, strings values. For
example

def method_missing( id, *args )
puts id.class
end
asdf # undefined method

Would output "Symbol", since the method identifier is indeed an
identifier. Symbols are simply names of stuff. Use them when you need to
name something whose value may change, but not whose semantics (ie
ary[:greeting] may be "Hello world" or "Hello johnny" but it's allways a
greeting. You shouldn't use Symbols for stuff you're going to output to
some io, for example, there's no point in doing

puts :"Hello world"

instead of

puts "Hello world"

(Maybe some Ruby gurus could explain this better than me).
 
D

dblack

Hi --

Symbols with the same values are the same objects, that is not the case
with strings. Symbols represents identifiers, strings values. For
example

def method_missing( id, *args )
puts id.class
end
asdf # undefined method

Would output "Symbol", since the method identifier is indeed an
identifier.

There's no constraint or direct cause-and-effect operating on Ruby
here, though. For example, when you do:

Array.instance_methods

you get a list of strings, corresponding to method names. These
strings identify the methods, as much as :asdf does in your example.
Symbols are simply names of stuff. Use them when you need to
name something whose value may change, but not whose semantics (ie
ary[:greeting] may be "Hello world" or "Hello johnny" but it's allways a
greeting. You shouldn't use Symbols for stuff you're going to output to
some io, for example, there's no point in doing

puts :"Hello world"

instead of

puts "Hello world"

I'd add that a lot of Ruby methods take strings or symbols: attr and
friends, send, define_method, etc. So there's also no point doing:

send(meth_name.to_sym)

which one sees occasionally.


David

--
David A. Black ([email protected])
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! http://www.manning.com/books/black
 
J

john_sips_tea

Here's another way:
kitty_toys =
[ { :shape, 'sock', :fabric, 'cashmere'},
{ :shape, 'mouse', :fabric, 'calico'},
{ :shape, 'eggroll', :fabric, 'chenille'}
]

Ahh. Replacing the "fat commas" with commas. Thanks.
 
J

john_sips_tea

First, let me warn you that from what you describe, you have
a Python viewpoint on the data structures and that it is quite
different from many many languages.

Well, hopefully you folks can help straighten me out then. :)

I've actually programmed in C, C++, Java, Perl, and Python,
but my trouble is that as soon as I learn the next language,
my brain tries to file previous language knowledge into
my grey matter's round filing cabinet. :)
As you seem to come from the Python's world, you probably
know that Python's strings are immutable. And if you read a
little bit about why strings in Python are immutable, you will
see it's because they wanted to optimize the method lookup.

I'll have to think about that. I don't see the connection between
strings and method lookup. In Python code, you write a name(),
and python works its way up the inheritance tree looking for
the function definition. You can't call a method like:

def my_func():
print "hi"
foo = "my_func"
foo() # Trying to call my_func, but it fails.

so I don't see the string/method-call connection you're
referring to...
In the end, Python's string are what's called in Ruby Symbol
and Ruby's strings have no equivalent in Python, as Python
has no mutable string class.

Ah! Thanks.
You can expand it like that :

sorted_kitty = kitty_toys.sort_by { |toy| toy[:shape] }
sorted_kitty.each do |toy|
[...]
end

Got it.
The ruby equivalent of your python code would be :

(1...8).each { |i| (1...4).each { |j| puts i,j} }

Ah. Sweet. Thanks for throwing that in. :)
 
L

Logan Capaldo

--Apple-Mail-5--249190947
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
delsp=yes;
format=flowed


I'll have to think about that. I don't see the connection between
strings and method lookup. In Python code, you write a name(),
and python works its way up the inheritance tree looking for
the function definition. You can't call a method like:

def my_func():
print "hi"
foo = "my_func"
foo() # Trying to call my_func, but it fails.

so I don't see the string/method-call connection you're
referring to...

As a little aside, python lets you do:

def z(x):
print x

a = z

a("Hello")
Hello

yes?

well ruby as you know, makes parens optional in method calls, what
this means is you can't directly assign methods around. So in ruby
you can do this:
send("z", "Hello")

or this
send:)z, "Hello")

In the first case, z has to be hashed to its numeric symbol code at
run time. This means somewhere inside the send method, its doing
"z".to_sym. When you use :z, the hash is computed at compile time,
and you're basically passing send a number, let's say 9.

send(9, "Hello")

In ruby Symbols don't have the extra level of indirection that
strings do. "z" is a pointer to the string object that contains the
character 'z'. :z is the number 9. (well not really, but it is *a*
number).

This is a reason for using symbols as hash keys, if you never display
the keys for instance. Its a number so equality comparsions become
trivial and hashing operations also become cheaper (I imagine so
anyway).
Using symbols basically saves you one level of runtime lookup.



--Apple-Mail-5--249190947--
 
J

john_sips_tea

Symbols are not merely lightweight strings, they represent
names in the Ruby interpreter.

Ok, just like any other object:

foo = Foo.new # An instance of class Foo.
bar = Bar.new # An instance of class Bar.
baz = :famous_pizza # An instance of class Symbol.
Check this sample out to see the difference: [snip]
Symbols with the same values are the same objects, that
is not the case with strings.

Ah. Thanks.
 
P

Pierre Barbier de Reuille

(e-mail address removed) a =E9crit :
[...]
=20

I'll have to think about that. I don't see the connection between
strings and method lookup. In Python code, you write a name(),
and python works its way up the inheritance tree looking for
the function definition. You can't call a method like:

def my_func():
print "hi"
foo =3D "my_func"
foo() # Trying to call my_func, but it fails.

so I don't see the string/method-call connection you're
referring to...

=20
Ok, so, internally, when you write :

obj.fct()

the language first has to find out if and where "fct" is in "obj". To do=20
so, ruby will see "fct" as a Symbol and look for that symbol in "obj",=20
and Python will see "fct" as a string and look for it in "obj".
Unlike C++ and Java, the method resolution is done entirely at runtime,=20
so you have to use the *name* of the method to find it ! Remember that=20
any single object may or may not have the method defined, whatever its=20
class is !!!

If you prefer, these two statement are exactly equivalent :

obj.fct <=3D> obj.send:)fct)

The same equivalence in Python:

obj.fct <=3D>obj.getattr("fct")

Thus, in the dynamic languages, you need to keep a symbolic=20
representation of the methods, whether as a symbol or as a string=20
(symbol is more efficient, that's why Python's string are in fact=20
symbols ...).
[...]


Hope that helped !

Pierre
 
J

john_sips_tea

well ruby as you know, makes parens optional in method
calls, what this means is you can't directly assign
methods around.

Ah! Hadn't thought of that!

Sounds like Symbols are used extensively inside Ruby. Almost
like some kind of smart pointer.

Will look up the docs on "send", and re-read the docs on Symbol.
Thanks!
 
J

john_sips_tea

obj.fct()
the language first has to find out if and where "fct" is in "obj".
To do so, ruby will see "fct" as a Symbol and look for that
symbol in "obj", and Python will see "fct" as a string and look
for it in "obj".

Ah. Thank you for pointing that out! I see what you mean.
The runtime has to find the method *somehow*.
If you prefer, these two statement are exactly equivalent :

obj.fct <=> obj.send:)fct)

The same equivalence in Python:

obj.fct <=>obj.getattr("fct")

Ahh. Very informative. I'm gonna have to try and put some
of this up on the Ruby wiki.
Hope that helped !

Pierre

Very much so. Thanks again Pierre. :)
 
E

Eric Kidd

I'll have to think about that. I don't see the connection between
strings and method lookup.

Symbols and strings have various tradeoffs:

1) Comparing two strings may require looking at every character in
each string. This means some kind of loop, which takes time. On the
plus side, you can change the contents of a Ruby string.

2) Comparing two symbols takes only one instruction, because symbols
are stored as pointers to objects. Every time you write ":foo", you
get a pointer to the exact same ":foo" object. But since this object
is shared (and represents a constant), you're not allowed to change it.

The Ruby interpreter makes extensive use of symbols internally, for
performance reasons.

When should you use symbols? Mostly when you need some way to talk
about the (computer's) "names" for things, and compare those names
very efficiently. This generally occurs when implementing computer
languages, or when trying to parse natural languages.

How would you implement symbols? Usually with a hash table:

# Pseudocode. This function would be called by the parser.
def get_symbol_for_string(str)
if hash contains object for str:
return the object
else
make a new object, store it in the hash, and return it

Cheers,
Eric
 
W

William James

William said:
Hi,

I'm reading through the poignant guide, and am a bit stuck
at the end of chapter 4
( http://poignantguide.net/ruby/chapter-4.html ). It's
regarding the kitty_toys example:

#!/usr/bin/ruby

kitty_toys =
[:shape => 'sock', :fabric => 'cashmere'] +
[:shape => 'mouse', :fabric => 'calico'] +
[:shape => 'eggroll', :fabric => 'chenille']

kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
end

and I've actually got three sticking points with it:

1. For one thing, I don't understand the point of the ":shape"
syntax. I don't understand why the author doesn't just write
the string "shape" instead. What exactly is a "Symbol" object
for?

The poinant guide says it's just "words that look just like
variables". And "Symbols are lightweight strings." But that
doesn't help me much. The PickAxe 2nd ed, in chapter 3 says,
"The construct :artist is an expression that returns a Symbol
object corresponding to artist.", so, I can understand that
(i.e. that there's some Symbol class and we're getting an
instance of it by using that notation), but I'm still not
getting the point... why not just use strings? What does
having "Symbols" buy the programmer?

2. Next up, why is

kitty_toys =
[:shape => 'sock', :fabric => 'cashmere'] +
[:shape => 'mouse', :fabric => 'calico'] +
[:shape => 'eggroll', :fabric => 'chenille']

supposed to be shorthand for

kitty_toys = [
{:shape => 'sock', :fabric => 'cashmere'},
{:shape => 'mouse', :fabric => 'calico'},
{:shape => 'eggroll', :fabric => 'chenille'}
]

How does that work? (Hmm... what does adding Arrays
in Ruby mean anyway? In Python it concatenates.)
Why does this shorthand exist? Hmm,.. it doesn't seem
to be saving much finger typing...

3. Finally, at the end of that example given in the poignant
guide:

#!/usr/bin/ruby

# ... Create kitty_toys as shown above, then

kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
end

how does that "kitty_toys.sort_by" line work? I believe that
braces and "do ... end" are equivalent, so it looks to me like
there's some kind of "loop-in-a-loop" going on, as in:

No, kitty_toys.sort_by { |toy| toy[:shape] } simply produces an
array that's sorted by the shape of each toy. Then he
iterates through the array and prints each toy.
# Warning, Python code follows:
for i in range(1, 8):
for j in range(1, 4):
print i, j

Is there a more verbose way of writing that kitty_toys snippet
to make it a bit more obvious what's going on? I mean, I guess
the sort_by method is probably looking for something to sort
kitty_toys on, and we're telling it to use what it finds in
toy[:shape] for each hash it iterates over, but then, is that
next "each" looping over items in a given hash, or ... gah. I'm
not getting it. :)

Thanks,
---John

Here's another way:

kitty_toys =
[ { :shape, 'sock', :fabric, 'cashmere'},
{ :shape, 'mouse', :fabric, 'calico'},
{ :shape, 'eggroll', :fabric, 'chenille'}
]

puts kitty_toys.sort_by { |toy| toy[:shape] }.map { |toy|
"Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
}

kitty_toys =
{ :shape, 'sock', :fabric, 'cashmere'},
{ :shape, 'mouse', :fabric, 'calico'},
{ :shape, 'eggroll', :fabric, 'chenille'}
 

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

Forum statistics

Threads
474,001
Messages
2,570,254
Members
46,850
Latest member
VMRKlaus8

Latest Threads

Top