[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

deciphering poignant guide chapter 4 example

john_sips_tea

3/26/2006 7:15:00 AM

Hi,

I'm reading through the poignant guide, and am a bit stuck
at the end of chapter 4
( http://poignantguide.net/ruby/chap... ). 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

14 Answers

William James

3/26/2006 11:04:00 AM

0

john_sips_tea@yahoo.com wrote:
> Hi,
>
> I'm reading through the poignant guide, and am a bit stuck
> at the end of chapter 4
> ( http://poignantguide.net/ruby/chap... ). 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] }"
}

Pierre Barbier de Reuille

3/26/2006 12:26:00 PM

0

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.

john_sips_tea@yahoo.com a écrit :
> Hi,
>
> I'm reading through the poignant guide, and am a bit stuck at the end
> of chapter 4 ( http://poignantguide.net/ruby/chap... ). 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?
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.
>
> 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. :)
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} }
>
> Thanks, ---John
>




Simen

3/26/2006 12:34:00 PM

0

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

--
Posted via http://www.ruby-....


dblack

3/26/2006 1:59:00 PM

0

john_sips_tea

3/26/2006 10:27:00 PM

0

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

john_sips_tea

3/26/2006 10:41:00 PM

0

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

Logan Capaldo

3/26/2006 11:05:00 PM

0


On Mar 26, 2006, at 5:43 PM, john_sips_tea@yahoo.com wrote:

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


john_sips_tea

3/26/2006 11:42:00 PM

0

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

Pierre Barbier de Reuille

3/27/2006 8:49:00 AM

0

john_sips_tea@yahoo.com a écrit :
[...]
>
>> 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...
>
>
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


john_sips_tea

3/27/2006 4:19:00 PM

0

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