Hi,

Ali Koubeissi wrote:
> Hey, I've started with Ruby two days ago, and I have some questions.
> 
> 1- What's the use of Symbols? I've read that symbols are (Symbols are
> lightweight strings. Usually, symbols are used in situations where you
> need a string but you won???t be printing it to the screen.), however I
> cannot understand how to use them, and so I tried to read some examples
> and I got this:
> 
> kitty_toys =
>    [:shape => 'sock', :fabric => 'cashmere'] +
>    [:shape => 'mouse', :fabric => 'calico'] +
>    [:shape => 'eggroll', :fabric => 'chenille']
> kitty_toys.sort_by { |toy| toy[:fabric] }
> 
> Why is he using symbols? I just couldn't understand ;(.

It might save memory and make the program run faster.

I'm also new to Ruby, so I'll post my understanding of symbols so 
far--at the very least it should provide the experts with some easy 
weekend target practice.  If you search on google, you'll find that 
explaining symbols has been hotly debated in the past, so I am under no 
illusion that this is the correct or only interpretation.

> 1- What's the use of Symbols? I've read that symbols are (Symbols are
> lightweight strings. Usually, symbols are used in situations where you
> need a string but you won???t be printing it to the screen.), however I
> cannot understand how to use them, and so I tried to read some examples
> and I got this:
> 
> kitty_toys =
>    [:shape => 'sock', :fabric => 'cashmere'] +
>    [:shape => 'mouse', :fabric => 'calico'] +
>    [:shape => 'eggroll', :fabric => 'chenille']
> kitty_toys.sort_by { |toy| toy[:fabric] }
> 
> Why is he using symbols? I just couldn't understand ;(.

In java, if you write:

String s1 = "hello world";
String s2 = "hello";
String s3 = "world";
s2 += s3;

if(s1 == s2)
{
  System.out.println("yes");
}
else
{
  System.out.println("no");
}

the output will be 'no'.  Why?  Because in java, the == operator tests 
whether two objects are the same object--not whether their values are 
identical. However, if you write this:

String s1 = "hello";
String s2 = "hello";

if(s1 == s2)    //checks whether s1 and s2 refer to the same string 
object
{
  System.out.println("yes");
}

the output will be 'yes'.  Java does not create a second string object 
when you assign a string literal that already exists in memory to 
another String variable.  Instead, both String variables will refer to 
the same String object.  That saves memory and can speed up execution.

On the other hand, Ruby creates new string objects for every string 
literal that you assign to a variable:

s1 = "hello"
s2 = "hello"

puts s1.object_id
puts s2.object_id

--output:--
76810
76800

Note that in ruby you have to look at the object id's to determine 
whether two objects are the same, because in ruby the == operator 
compares the values of two string objects--not the string objects 
themselves.  That's different than the way Java's == operator works. 
The == operator in ruby is equivalent to the equals() method in java.

Since ruby does the opposite with duplicate strings as Java, the 
opposite conclusion might be true: ruby's string handling hogs memory 
and slows down execution.

Enter symbols:

s1 = :hello
s2 = :hello
s3 = :hello

puts s1.object_id
puts s2.object_id
puts s3.object_id

--output:--
2545934
2545934
2545934

puts s1.to_s
puts s2.to_s
puts s3.to_s

--output:--
hello
hello
hello

I think this is what it looks like:


    "hello"
       ^
       |
       |
   symbol_obj
   id:2545934
   ^   ^   ^
   |   |   |
   s1  s2  s3


I found the description here helpful:

http://moonbase.rydia.net/mental/blog/programming/ruby-symbols-explained

-----
For every unique string value, there is a unique symbol object.

Testing two symbol values for equality (or non-equality) is faster than 
testing two string values for equality, because Ruby only needs to do a 
single test[ruby compares the symbol id's]. Checking two strings for 
equality is more complicated; every individual character in the string 
has to be checked until a difference is found.

As noted above, each unique string value has an associated symbol. This 
means that checking whether two symbols have the same string value or 
not is as simple as checking whether they are the same object or not.

One comparison:

:Worcestershire == :Worcestershire
Easy peasy. They???re the same object, so they???re equal.

Sixteen comparisons:

"Worcestershire" == "Worcestershire"

With strings, Ruby has to dig into the objects to check their contents. 
Since in this case they???re different string objects with the same 
length, it???s got to check all fourteen characters in each string to make 
sure that they really are equal.
-----

Finally, remember that symbols are not string objects, so string methods 
do not work on symbols:

s1 = "hello"
s1.upcase!
puts s1

--output:--
HELLO

s2 = :hello
s2.upcase!
puts s2

--output:--
undefined method `upcase!' for :hello:Symbol (NoMethodError)

However, you can always get the string that a symbol refers to and call 
string functions on the string:

s2 = :hello
s3 = s2.to_s
s3.upcase!

puts s3

--output:--
HELLO

For a final twist, there is method called send(), which is a method of 
Object.  send() takes an argument that is a symbol.  The symbol argument 
should refer to a string, and that string should be the name of a 
method.  send() then executes the method:

def show(x)
    puts x
end

send(:show, 10)

The method name 'send' comports with the ruby terminology that you are 
sending messages to objects when you write something like:

my_string.upcase!

In ruby, that code is described as sending the upcase! message to the 
the object my_string, which causes the upcase! method in the string 
class to execute.

All languages I've studied have a way to call a method when you have the 
method name as a string, e.g. the user entered a method name and you 
need to call that method.  In Java, you do that with reflection and a 
few contortions.  Because a symbol refers to a string, it's not much of 
a stretch to think that you should be able to execute a function when 
you have a symbol: the symbol can be used to get the string.  In fact, 
it appears that send() will also accept a string as an argument, 
although that isn't documented:

def show(x)
    puts x
end

send("show", 10)


Knowing when to use symbols to optimize your code will hopefully come 
with experience.  See these articles as well,

The Ruby_Newbie Guide to Symbols:
http://www.troubleshooters.com/codecorn/ruby/symbols.htm

13 ways to look at a Ruby symbol:
http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol


> 2- Why does a block has to be on the same line when passing argument?
> Like in the previous example: kitty_toys.sort_by { |toy| toy[:fabric] }.
> If I tried to move the block down, it won't work. 

The file that contains your program is just a text file.  ruby has to 
interpret the text in the file and make sense of it.  In order for ruby 
to make sense of the text, you have to follow certain syntax rules. 
Apparently, a syntax rule in ruby is that a brace enclosed block has to 
be on the same line as the method call so that ruby knows it is 
associated with the method call.  Of course, you can always use the "do" 
form for lengthy blocks:


kitty_toys.sort_by do |toy|
     toy[:fabric]

     #200 lines of other code here

end

> Also, if someone could
> explain how does the argument get passed to the block. I mean, based on
> what?

You can think of a block as a second method.  The first method specified 
before the block, e.g. sort_by in your example, calls the second 
method(the block).  However, only a method that is written to call a 
second method can have a block.  Methods that call a second method(the 
block) need to be defined with a yield statement(or the equivalent). 
Here is an example of a method I made up that can call another method 
that is a block:

def func
    arr = [3, 6, 9, 12]
    count = 0

    while count <= 3
        yield arr[count]
        count += 1
    end

end

In fact, calling that method without a block will produce an error.  A 
yield statement is similar to a normal method call: the yield statement 
sends the value specified after 'yield' to the block, e.g arr[count] in 
func.  The yielded value then gets assigned to the block parameter 
variable.  Subsequently, the block can do whatever it wants with the 
value.  Execution in func halts at the point of the yield statement 
until the block finishes executing, at which point execution continues 
in the func.  Since func has a repeating while loop, the block is called 
repeatedly with different values.  Here is an example of calling func 
with a block:


def func
    arr = [3, 6, 9, 12]
    count = 0

    while count <= 3
        yield arr[count]
        count += 1
    end

end


func {|i| puts i * 2}

--output:--
6
12
18
24



Here is another example:

def func
    yield
    yield
    yield
end


func {puts "hello"}

Since func doesn't yield any values(there are no values specified after 
the word 'yield'), the block doesn't need to be defined with a parameter 
variable to catch the values.  In fact, you can't define a block with a 
parameter variable if no values are going to be sent to the 
block--you'll get an error that says the block was expecting one 
argument and got zero.

If it helps you to understand, you can also do the equivalent(nearly) 
using a Proc object.  A Proc object represents a method.  Proc objects 
allow you to explicitly send methods as arguments to other methods, 
rather than doing so implicitly with a block:

def func(proc_obj)
    arr = [3, 6, 9, 12]
    count = 0

    while count <= 3
        proc_obj.call(arr[count])
        count += 1
    end
end

p = Proc.new {|i| puts i * 2}
func(p)

--output:--
6
12
18
24


> 3- How does a Ruby file compile? Like in Java, your code gets compiled
> into bytecode, and then delivered to the JVM. Are there any similarities
> in Ruby?

Uhmmm...I don't think that's quite the way it works in java.  In java, 
you compile your program into byte code *before* executing it.  Byte 
code consists of instructions for the java jvm.  After compiling, you 
execute your program, which sends the byte code instructions to your 
platform specific jvm installed on your computer.  The jvm then converts 
the byte code into machine instructions that your specific os can 
understand.

I'm not quite sure about the nitty gritty details in Ruby, but when you 
execute a Ruby program, full compilation is done before execution can 
begin.  From what I've read, Ruby is much slower than Java, and it's 
also slower than just about any other comparable language, e.g Perl, 
Python, PHP, etc. It's significantly slower than Java because it has to 
do a full compilation before executing the code, and Ruby is a 
dynamically typed language(i.e. variables don't have types and can be 
assigned any type).  Dynamically typed languages cannot be optimized as 
much as statically typed languages like Java.


> I'm currently reading Programming Ruby 2nd Edition, 

Me too.  :)
-- 
Posted via http://www.ruby-forum.com/.