codethinked (kōdthĭngked) adj. To be consumed by or obsessed with code.

Learning Ruby via IronRuby and C# Part 2

Click here to view the entire IronRuby via C# series

In my last post about learning Ruby via IronRuby and C# we talked about variables. We covered local variables, global variables, and both instance and static variables in Ruby. In this post we are going do delve a bit more into Ruby classes and start looking at defining methods and passing around parameters.

But first, let me start off with one quick thing so that you can start to play around with Ruby a bit on your own. And that one thing is output. In C# we would do this:

Console.WriteLine("My test value");

And in Ruby, all we do is this:

puts "My test value"

Simple, now that you can spit things out to the console, lets get on with the meat of this post.

To start it off, I am going to define a quick C# class with a constructor and a method:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Person(string firstName, string lastName)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
    }

    public string FirstAndLastName()
    {
        return String.Format("{0} {1}", FirstName, LastName);
    }
}

Here you see that we have a Person class that has two properties and initializes those properties in the constructor. Then we have a method that returns a first and last name together using String.Format. You might be surprised just how similar this looks in Ruby:

class Person
    attr_accessor :first_name
    attr_accessor :last_name
    
    def initialize(first_name, last_name)
        self.first_name = first_name
        self.last_name = last_name
    end
    
    def first_and_last_name
        "#{@first_name} #{@last_name}"
    end
end

So, you can see that in Ruby we have our attribute accessors instead of the properties in C#. Then we have a specialized method called "initialize". This is essentially a Ruby constructor. It is what Ruby calls in order to initialize a new instance of a class. You don't actually have to call this method though, as you'll see in a second. Next you see that we have a method called "first_and_last_name" that returns our first and last name appended together. First notice that we don't specify a return type, this is because everything in Ruby returns a value. Either that value is nil (Ruby's null) or it is a valid value. Also notice that there is no return statement, Ruby takes any statement on a line by itself as a return. You can also see that we are using our variables directly in the string, this is a common theme in Ruby where they provide syntactic sugar for doing common tasks like this.

In order to consume these classes you would just use the "new" operator in C# like this:

var person = new Person("Ted", "Smith");

And In Ruby you would use the new method, like this:

person = Person.new("Ted", "Smith")

Another useful features in C# is the "params" keyword when passing variables to methods. Your method would look something like this:

public string Concat(params string[] items)
{
    string result = "";
    foreach (string item in items)
    {
        result += item;
    }
    return result;
}

In Ruby this is also available, by use of the asterisk.

def concat(*items)
    result = ""
    items.each do |item|
        result += item
    end
    result
end

The Ruby method does virtually the same thing, and is arguably easier to read. In the C# version we are using a "foreach" statement and looping through each item in our items array. In Ruby the same thing happens, we are given an array of items and we use the "each" method to perform the concatenation on each item. Each may not look like a method, but it is, and this is due to the fact that parentheses are generally optional in Ruby.

So, if parentheses are optional, then we could have created our Person object up above like this?

person = Person.new "Ted", "Smith"

And the answer is yes, you could. But what do I mean about them being "generally" optional? Well, there are cases, such as when you are nesting method calls that not having parentheses would lead to ambiguous situations. Take for example this method call:

person.concat(person.first_and_last_name, "test")

Now, look at that without the parentheses:

person.concat person.first_and_last_name "test"

Yep, ambiguous. In fact, Ruby tries to give the parameter to person.first_and_last_name, which takes no arguments, and so we get an error. So, if the parentheses are optiona, are the commas optional? Nope. In fact, if we do this:

person = Person.new "Ted" "Smith"

We get an error telling us that the person initialize class expects 2 arguments and we only passed one. One? Does it pass it as an array? Nope. In Ruby, the "+" sign in string concatenation is optional! In fact, there are a few other ways to concatenate strings, but I'll just tell you to stick with the plus operator.

So, we mentioned above that "each" is just a method on the "items" array. So, if "each" is a method, then what is its parameter? Well, the entire thing from "do" to "end" is the parameter. In Ruby these are called blocks, and you can define your own methods that take them. In Ruby there are two forms of blocks, above you see on type, but we can also define another type that, as a C# developer, may look a bit more familiar to you:

items.each { |item| result += item }

See, now you can probably start to see a bit better how this translates to C#. Think of it as an action delegate, and lets define this extension method real quick:

public void Each<T>(this IEnumerable<T> list, Action<T> func)
{
    foreach (T item in list)
    {
        func(item);
    }
}

Okay, now we can use this on our array in C# like this:

items.Each(delegate(string item) { result += item; });

Hmmm, C# is maybe borrowing a bit here? In fact, we could even use the Aggregate function (also called Fold) and then the Lambda syntax would make it look even more similar. But don't let blocks confuse you with lambdas, because blocks are not lambdas. Don't fear though, because Ruby also has lambdas!

Well, I'm gonna stop there for the evening. In the next post I am going to discuss control structures in Ruby a bit (if statements, loops, etc...) and then probably get back to blocks a bit, since they are used quite heavily in Ruby.

I hope you enjoyed the post!

Comments

pingback

Pingback from alvinashcraft.com

Dew Drop - July 22, 2008 | Alvin Ashcraft's Morning Dew

alvinashcraft.com

July 22. 2008 05:00

Brian Lowry

I really enjoy this series. One of the things I've been meaning to do is learn some IronRuby and perhaps throw together a test RoR site - just to get a feel for it. Your post will definitely be a reference point when I make the time to do that.

Good work, Justin.

Brian Lowry

July 22. 2008 11:48

United States
Denny Ferrassoli

Great series, please keep them coming!

Denny Ferrassoli

July 22. 2008 13:43

United States
pingback

Pingback from remark.wordpress.com

Ruby Tuesday #15 : Ruby Round Up « Re.Mark

remark.wordpress.com

July 22. 2008 22:33

Kevin Hazzard, MVP

Nice, Justin. I've always learned new languages in the context of others. Seeing your examples contrasting with C#, my native toungue these days, makes the information fly into my brain. Can I haz more?

Kevin Hazzard, MVP

July 22. 2008 22:56

United States
Justin Etheredge

@Brian, Denny Thanks guys!
@Kevin More? Yes you can, Kevin, yes you can. Smile

Justin Etheredge

July 23. 2008 06:14

United States
trackback

Trackback from Sam Gentile  The World According to MSCOREE

New and Notable 256

trackback

Trackback from CodeThinked

Learning Ruby via IronRuby and C# Part 3

CodeThinked

July 23. 2008 21:09

pingback

Pingback from rubyinside.com

A Great Set of IronRuby Tutorials To Bring C# Developers Into The Ruby Fold

rubyinside.com

July 23. 2008 21:51

pingback

Pingback from rubyworld.wordpress.com

Compare IronRuby with C#, Smile And Learn As Well. «

rubyworld.wordpress.com

July 25. 2008 08:30

jacob swanner

inside the initialize method, you would typically set your instance variables using the @ syntax, such as "@first_name = first_name", instead of "self.first_name = first_name", but both are valid.  in case you're wondering, the @first_name variable is defined when you call "attr_accessor :first_name".

when it comes to iterators and arrays, there are many ways to do anything.  your concat method can be shortened to:
def concat(*items)
  items.inject(""){ |result, item| result += item }
end

jacob swanner

July 26. 2008 15:06

United States
Justin Etheredge

@jacob Thanks, I will note in the future that "self" is not often used. I just wanted to show the "self" versus "this" in C#.

And thanks for the shortened "concat" method. I was trying to keep the method as simple as possible for those who have not written Ruby before. I wonder why they chose to call it "inject" instead of the standard "fold". Just like in C# they called the method "aggregate", but at least to me that makes more sense than "inject".

Justin Etheredge

July 26. 2008 20:13

United States
trackback

Trackback from D.K.

[译林系列--Ruby] 通过IronRuby和C#学习Ruby[2]

D.K.

August 17. 2008 07:29

pingback

Pingback from quantumatrix.wordpress.com

A set of articles on Ruby « QuantuMatrix’s Weblog

quantumatrix.wordpress.com

October 13. 2009 16:05

Add Comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading