Deciphering poignant guide chapter 4 example


#1

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?

  1. 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…

  1. 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. :slight_smile:

Thanks,
—John


#2

removed_email_address@domain.invalid wrote:

[:shape => 'sock', :fabric => 'cashmere'] +

syntax. I don’t understand why the author doesn’t just write
getting the point… why not just use strings? What does

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.

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. :slight_smile:

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] }”
}


#3

Hi,

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.

removed_email_address@domain.invalid a écrit :

‘chenille’]
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
informations in, for example, a hash table. The idea is to get an
efficient implementation while keeping the code readable by human
beings.

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. Also you will learn that short Python’s
string are coded differently so that a simple integer lookup is used for
method lookup. 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.

‘mouse’, :fabric => ‘calico’}, {:shape => ‘eggroll’, :fabric =>

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. :slight_smile:
No, as said before, the sort_by returns an array … which is further
processed, so it more subsequent loops.
You can expand it like that :

sorted_kitty = 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} }


#4

Hi –

On Sun, 26 Mar 2006, Simen wrote:

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.

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 (removed_email_address@domain.invalid)
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


#5

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.


#6

unknown wrote:

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


#7

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. :slight_smile:

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. :slight_smile:

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. :slight_smile:


#8

On Mar 26, 2006, at 5:43 PM, removed_email_address@domain.invalid wrote:

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.


#9

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.


#10

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!


#11

removed_email_address@domain.invalid a écrit :
[…]

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…

Ok, so, internally, when you write :

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”.
Unlike C++ and Java, the method resolution is done entirely at runtime,
so you have to use the name of the method to find it ! Remember that
any single object may or may not have the method defined, whatever its
class is !!!

If you prefer, these two statement are exactly equivalent :

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

The same equivalence in Python:

obj.fct <=>obj.getattr(“fct”)

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

Hope that helped !

Pierre


#12

On Mar 26, 2006, at 5:43 PM, removed_email_address@domain.invalid wrote:

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.

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


#13

William J. wrote:

kitty_toys =

  1. For one thing, I don’t understand the point of the “:shape”
    instance of it by using that notation), but I’m still not
    supposed to be shorthand for
    to be saving much finger typing…
    end

Warning, Python code follows:

not getting it. :slight_smile:
]

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’}


#14

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. :slight_smile:


#15

Ok, I added a wiki page:
http://www.rubygarden.org/ruby?UnderstandingSymbols

It could probably use some corrections, but please try not to tread to
heavily upon the delicately hand-crafted exposition. :wink:

—John