Ruby for C# Geeks

ttention C#
developers: C# is good for a great many things, but it’s not the best
language for everything. In fact, there is no such thing as "the best
language." It all depends on what you’re trying to do, and your personal
preference and familiarity with the languages you have available.
Although the Church-Turing Thesis
suggests that anything you can do in one complete language, you can
also do in another, the truth is not all languages are created equal.
You can accomplish simple tasks with complex code in some languages and
complex tasks with simple code in others. C# sometimes falls into both
categories, but more often than not, it requires a bit more than its
fair share of effort. Strangely enough, a language that allows you to
accomplish simple tasks with simple code is rare. Enter Ruby, an
interpreted, dynamically typed language that enables just that.

Many would say that the main difference between Ruby and C# is that Ruby
is a dynamic language whereas C# isn’t. However, referring to C# as a
static language really isn’t right because you wouldn’t apply that term
to an entire language as you would to one of the dynamic variety. Ruby
really differs from C# in that its code is not actually compiled into an
intermediate executable form before it is run. Instead, Ruby has at its
heart a text-driven interpreter. This means that the expressions and
statements in a Ruby program are evaluated as the interpreter passes
over them. In C#, you must first compile the code to an .exe or .dll file to be able to run it.

In requiring compilation, C# encapsulates an opportunity to check syntax
and optimize the runtime efficiency of the code before it’s ever run.
On the other hand, all of the declaration and specification that goes
into setting up your code with all of the necessary information to allow
the compiler to perform these tricks will slow you down when these
features aren’t necessary or desired. You might use a language like
Ruby, with looser guidelines, to test your algorithmic theories or
rapidly prototype an application. Sometimes you just need to format a
couple of text files, and C# isn’t exactly friendly in cases where you
just want to automate something as simple as a command line.

This article offers a brief introduction to the Ruby language from the
perspective of a C# developer. You’ll learn the differences between the
languages’ features through line-by-line examinations of identical
programs built in each one.

Same Results, Simpler Code
Every programming language tutorial I’ve ever read has what is (often
sardonically) known as the "obligatory Hello World! example." I’m not
very fond of that terminology, so I’ve spruced up the sample application
for this article to the "slightly interesting Hello Foo! example." The
snippets to follow show two programs (one in C#, the other in Ruby) that
produce exactly the same output for my Hello Foo! example.
advertisement

intm/webdev/www.devx.com/ros/2325/34470: accessunit



First, here’s the C# version:


// Hello Foo! in C#

using System ;

namespace TestCSharp
{
class MainClass
{

public static void Main(string[] args)
{
Console.WriteLine("Hello Foo!"); //say hello
}
}
}

As you can see, C# requires you to specify a lot of structure and
actually tell the compiler that you’re going to write a class.
Specifically, here you tell it you’re going to write a class with a
well-known method called Main that a console
application will use as the first method to call (known as an entry
point) when the program executes. The required verbosity is somewhat
mitigated by Visual Studio or any other syntax-helping editor that
allows for templates and IntelliSense; however, the level of code
complexity is still there regardless of whether or not you have to
actually type every letter.

First, compile the C# program (If you’re using Visual Studio or MonoDevelop, you can simply press F5.), and then you can run it:


# Hello Foo! in Ruby

puts "Hello Foo!" # say hello

Running this little snippet requires that you simply invoke the
interpreter and then provide the name of the script file (hellofoo.rb):


C:>ruby hellofoo.rb
Hello Foo!

C:>

Since the Ruby interpreter assumes all of the structural information is
there, you don’t have to write it like you would in C#. The interpreter
assumes that the first bunch of code you write without an enclosing
class or module declaration is analogous to it appearing within the Main method of the entry point class. Very handy.

Note the differences in syntax between the languages as well:

  • Semi-colons aren’t used to delimit the end of a statement in Ruby.
  • Ruby’s built-in puts method is actually like C#’s Console.Writeline in that it will display the result of the .to_s method of the parameter object, which in C# you would write as .ToString().
  • Parentheses are optional for method calls in Ruby. Most of the time,
    you simply omit them—particularly when you don’t pass any parameters.
  • The // style of commenting in C# is replaced by the # notation in Ruby. Authors Note:
    Because its syntax is regarded as self-explanatory, a common belief is
    that Ruby rarely requires comments. I’m a little skeptical of this idea,
    but I will admit that it’s often notably easier to understand Ruby code
    as a human reader than it is the C# equivalent.

Dynamically Typed Means Faster Coding
Not only is Ruby an interpreted (i.e., dynamically evaluated) language ,
it is also dynamically typed. So the variables in Ruby do not require
the specification of their types before you use them. The interpreter
will infer variable types as they are assigned values. To add another
twist, you don’t have to declare variables at all! This is a common
feature of many interpreted languages, and even a few compiled
languages. To see what I’m talking about, consider the following Ruby
snippet:


#Ruby

abc = 1 #abc is a Fixnum (integer)
puts abc

abc = "Rubber Baby Buggy Bumpers" # now it's a string!
puts abc

You can see that you have to declare neither the variables nor their
types in Ruby, and you can change the type by assigning it a different
value—right in the middle of the running program. Very dynamic! If you
tried to do something like that in C#, the program wouldn’t even
compile. C# requires statically defining types for variables, which
allows the compiler to catch any errors that may arise when types don’t
match the ways in which they are used. However, it’s faster and easier
to throw together a script that doesn’t go to all of this trouble in
Ruby. Both approaches may have a time and a place, but you can choose
the one that best suits the problem you’re trying to solve.

The Ruby vs. C# Feature Rundown
The following is a simple example program in Ruby that demonstrates a
variety of the features often seen in C# programs. (It’s another
"classic" example program used in many programming tutorials.) It is
chock full of new stuff I haven’t discussed yet, but I’ll explain the
entire program snippet by snippet afterwards. If it’s still opaque after
that, fear not because I provide a structurally similar C# version so you can compare lines for yourself:


#gessnum.rb
class Game
def initialize(maxNum)
@num = rand(maxNum)
puts ["nnMagic Number Game!n",
"------------------nn",
"I'm thinking of a magic number between 0 and #{maxNum}n",
"Would you like to guess my number? (y/n) [n]:"]
playNow = gets or "n"
if playNow.chop == "y"
play
else
puts "OK, Bye!"
end

end

def play
loop do # infinite loop!

puts "nNumber?!?"
attempt = gets or break

case attempt.to_i <=> @num
when 1 # attempt > @num
puts "Guess Lower!"
when -1 # attempt < @num
puts "Think Bigger, Mac."
else # only have == left... so...
puts "Spot on! That's it! Bye!"
break # hop out of our loop!
end
end
end
end

Game.new(100)
advertisement

intm/webdev/www.devx.com/ros/2325/34470: accessunit



Here’s a sample run:


C:projruby>ruby sample.rb


Magic Number Game!
------------------

I'm thinking of a magic number between 0 and 100
Would you like to guess my number? (y/n) [n]:
y

Number?!?
50
Guess Lower!

Number?!?
25
Think Bigger, Mac.

Number?!?
37
Spot on! That's it! Bye!

At the very beginning of the code, I define a class. Notice how it’s
just the word "class" followed by the class name—no curly anything, and I
haven’t set a namespace.

The Game class contains two methods: initialize, the constructor, and play, the main body of the game. Methods are designated simply by the def keyword, and are terminated by the end keyword. In Ruby, all constructors are called initialize, and they are called when an object is instantiated (more on this in a minute).

The following snippet designated a variable called @num in the constructor. The variable was set to a random number between 0 and maxNum:


@num = rand(maxNum)

Any variable inside a class that starts with a @ sign is known as an instance variable
(like a field in C#). Just like in C#, the default for an instance
variable is to be private, meaning that folks outside the class cannot
see it or read its value. Why would I bother putting it in a field
instead of a local variable? Well, for the same reasons that I’d do so
in C#; I need to access it from other methods, namely the play method.

This command printed an array as a string:


puts ["nnMagic Number Game!n",
"------------------nn",
"I'm thinking of a magic number between 0 and #{maxNum}n",
"Would you like to guess my number? (y/n) [n]:"]

It’s much easier to just declare an array of strings and issue a single puts command for it to make a puts for each one.

The #{maxNum} portion is a Ruby trick known as string interpolation. The value of maxNum will be substituted for the occurrence of #{maxNum} in the string. This is roughly analogous to the String.Format() idiom in C#.

I set the value of a local variable ‘playNow’ to the result of the gets function, which reads a string from the input stream:


playNow = gets
if playNow.chop == "y"
play
else
puts "OK, Bye!"
end

I had to compare playNow to "y" (for yes) to make sure the user actually wants to play the game. But wait, you say, what’s that .chop business? That extension will drop the last character from the value, which would be the newline character, because gets records the newline generated by the enter key when it reads the input stream. So, if the program gets a "y" it invokes the play method, otherwise it kindly says goodbye.

Normally, trying to run code from within the constructor may not be such
a great idea or most objects, but for the sake of this paltry example
game, it’s no big deal:


def play
loop do # infinite loop!

puts "nNumber?!?"
attempt = gets or break

case attempt.to_i <=> @num
when 1 # attempt > @num
puts "Guess Lower!"
when -1 # attempt < @num
puts "Think Bigger, Mac."
else # only have == left... so...
puts "Spot on! That's it! Bye!"
break # hop out of our loop!
end
end

end

The play method is an infinite loop, meaning
that it will continue to execute until the process is terminated or
something inside the loop issues a break statement. I prompt the user for a number and store it in the local variable attempt.

The next bit is a little strange for a C# fan, but it actually is not all that different from a switch statement. The expression it is case-ing on (switching on) is attempt.to_i <=> @num. The first part, attempt.to_I,
converts the string value attempt to an integer. (The Ruby class of
objects holding small integer values is actually called Fixnum.) It’s a
built-in method of the %(String) class, which itself is a built-in type.
The <=> operator is analogous to the C# idiom of CompareTo(). If the values are equal, <=> returns the integer 0. If the left is less than the right, an integer value of -1 is returned, and a 1
is returned if the left side of the expression is greater than the
right. Basically, this is a switch for the three possible values, but
instead of the C# way (switch… case), it’s the Ruby way (case…when):

The very last line is the actual body of the main program:


Game.new(100)

The only thing that happens here is that an instance of the Game class is created by calling the .new method with the parameter of 100. In Ruby, .new is a special method that will invoke the initialize method, the constructor of the object. Notice the object isn’t assigned. Nobody needs to see the object, so it’s not stored.

The Ruby Mixin Feature
One trick I haven’t covered is the ability of Ruby to change an existing class that has already been defined. This is called a mixin because it allows you to mix in
your code with code that already exists. You can even create mixins
that alter the built-in types in Ruby, effectively altering the way the
entire language operates. To add even more variability, mixins allow you
to import classes, methods, or extend classes with features of a Module
(like a static class in C# 2.0) by modifying either the classes themselves or just particular instances of the classes!
advertisement

intm/webdev/www.devx.com/ros/2325/34470: accessunit



You also can re-open an existing class definition and inject your own
methods in, or override ones that are already there. For example, I
could redefine the .to_s method of the Fixnum
class, which is the class that all integers take on by default, to
return something like "I, for one, welcome our robot overlords" every
time. (The wisdom of doing something like this is of course
questionable.) For the sake of demonstrating how very open the entire
structure of Ruby is, here’s how you can do this:


Fixnum.class_eval do
def to_s
"I, for one, welcome our robot overlords."
end
end

q = 123
puts q.to_s

Of course, don’t try this at home, or at least don’t do it in production code.

For Further Study
The Ruby language offers many more features and goodies than I could
cover in one article, but absorbing them a few at a time from here on
will be no harder than what this tutorial has shown. For a quick
perusal of how Ruby generally compares to C# and Java, see Table 1
below.

Language Feature Ruby C# Java
Object Oriented Yes Yes Yes
Compiled/Interpreted Interpreted Compiled Both (usually compiled)
Variable Typing Dynamic Static Static
Native Mixins Yes No No (some add-on libraries offer limited support)
Closures Yes In version 2.0 + No
Reflection Yes Yes Yes
Multi-Threading Yes Yes Yes
Regular Expressions Native Standard library support Standard library support
Static and Instance Methods Yes Yes Yes
Type Inference Yes No No
Strong Typing Yes* Yes Yes
* Ruby is still
strongly typed, but the type can be inferred and easily changed at
run-time. Note that this is not the same as statically typed.
Table 1. Feature Comparison Chart of Ruby, C#, and Java

One of the particularly nice things about Ruby is the voluminous mass of
documentation generated by the ever-expanding Ruby user community. If
you would like to learn some more about Ruby, you should check out the
industry standard tongue-in-cheek guide known as Why’s (Poignant) Guide to Ruby. If humor isn’t your ideal way to learn a language, then you might want to see the more formal Programming Ruby,
which is actually quite authoritative on the subject. No matter what
your preferences are, you can get wherever you want to go with Ruby from
the official Ruby Language site.

Previous Page: The Ruby vs. C# Feature Rundown  
advertisement

intm/webdev/www.devx.com/ros/2325/34470: 336×280



Dave Dolan works as a Systems Administrator for Concurrent
Technologies Corporation where he regularly builds .NET based Web
applications to integrate and automate enterprise systems. He is
currently studying the associative memory model of computing, and
developing solutions utilizing associative database technology. For more
of his writing, visit http://davedolan.com.

This entry was posted in 未分类. Bookmark the permalink.

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s