Tuesday, 11 November 2014

Ruby equivalents of Python code

Just as I upgraded my iMac to OS X 10.10 (Yosemite), I saw that it came with Swift. Not the Maruti-Suzuki sedan, but a new language that Apple has put out. Oh information overload age, I missed knowing about it all these days.

Swift seems to have taken the most convenient features from all languages - it supports classes and structures, has very cleverly and rightly called interface as protocol, supports generics and has a mechanism a la garbage collection, calling it automatic reference counting - but its main USP seems to be built-in parallel processing. No Threads and no forks. Concurrency development with a vengeance. So the expectation is that building applications on OS X and iOS will see an accelerated traction.

But this post is not about Swift, rather it's about the two old warhorses, Python and Ruby. I spent some time with Python code, and like before, came up with the equivalent Ruby code.

1. Let's start with a simple example.
Say we want the first three elements of an array. In Python, you would do [:3] on the array. In Ruby you will have to use [0..2]. The code snippets are given below.

>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> b = a[:3]
>>> b
[1, 2, 3]

> a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
> b = a[0..2]
=> [1, 2, 3]
> b
=> [1, 2, 3]

2. On to the next Python snippet.
rating = int(field.strip().strip('"'))
This line of code takes a string field, removes all whitespace characters from the beginning and, then removes double quote character(") from the beginning and end, and finally converts the string to an integer. You would have guessed this double-quoted string field comes from a CSV file.

In Python, strip is dual purpose; it removes any character you specify, and if you don't specify any character it removes white space. In Ruby, however, strip removes only white space, and if we want to remove other characters, we have to use delete. The equivalent Ruby code I came up with is:
rating = field.strip.delete('"').to_i

The Python and Ruby documentation for this example is:
The built-in int function converts a string of digits to an integer, and an optional second argument lets you specify the numeric base. The method strip() returns a copy of the string in which all chars have been stripped from the beginning and the end of the string (default whitespace characters).
strip - Returns a copy of str with leading and trailing whitespace removed.
delete - Returns a copy of str with all characters in the intersection of its arguments deleted.
to_i (base=10) - Returns the result of interpreting leading characters in str as an integer base (base 2, 8, 10, or 16). Extraneous characters past the end of a valid number are ignored. If there is not a valid number at the start of str, 0 is returned. This method never raises an exception.

3. Moving on to dictionary/hash - you want to find how many elements it has.
In Python, you would call the len function and in Ruby, you would use length. Let's say ratings is a dictionary (hash). The Python and Ruby code are:


To complete this example, here's the documentation on len / length
The built-in len function works on dictionaries too; it returns the number of items stored in the dictionary, or, equivalently, the length of its key list.
len(d) : number of stored entries
Returns the size or length of has as an integer

4. Let's continue with another snippet with the 'ratings' dictionary/hash.
ratings = list (ratings.items())
What this Python code does is, it converts ratings to a list of tuples (immutable arrays), where the tuple is made of the key and value in the original dictionary. The equivalent Ruby code is
ratings = ratings.to_a
This converts ratings to an array of arrays whose members are the key and value of the original hash.

An example and the documentation are given below:
>>> a = {"a" : 1, "b" : 2, "c" : 3}
>>> a = list(a.items())
>>> a
[('a', 1), ('c', 3), ('b', 2)]

> a = {"a" => 1, "b" => 2, "c" => 3}
=> {"a"=>1, "b"=>2, "c"=>3}
> a = a.to_a
=> [["a", 1], ["b", 2], ["c", 3]]

items() : all key + value tuples. The built-in list function (an object construction call) builds a new list out of the items in any sequence.
Enumerable defines a to_a method (and a synonym entries) that converts any enumerable collection into an array.
Any Enumerable object, such as Range or Hash can be converted to an array with to_a

5. To conclude this post, let's look at a slightly involved snippet:
ratings = [(convert(k), v) for (k, v) in ratings]

It looks like a backward for loop and perhaps is very unique to Python. This syntax is called list comprehension. As we go past the previous example, ratings is a list of 2-tuples. This statement calls the method convert on the first member of every tuple and stores the result back in ratings.

The equivalent Ruby code has to use map. Ruby does not have the list comprehension which looks like a backward for loop.
ratings.map {|element| element[0] = convert(element[0])}

The documentation follows -
List comprehension requires less coding on our part and is likely to run substantially faster. The list comprehension isn't exactly the same as the for loop statement version because it makes a new list object (which might matter if there are multiple references to the original list), but it's close enough for most applications.
Syntactically, its syntax is derived from a construct in set theory notation that applies an operation to each item in a set, but you don't have to know set theory to use this tool.

L = [x+10 for x in L]
List comprehensions are written in square brackets because they are ultimately a way to construct a new list. They begin with an arbitrary expression that we make up, which uses a loop variable that we make up (x + 10). That is followed by what you should now recognize as the header of a for loop, which names the loop variable, and an iterable object (for x in L). To run the expression, Python executes an iteration across L inside the interpreter, assigning x to each item in turn, and collects the results of running the items through the expression on the left side. The result list we get back is exactly what the list comprehension says -- a new list containing x + 10, for every x in L.

The times, each, map, and upto methods are all iterators, and they interact with the block of code that follows them. The complex control structure behind this is yield. The yield statement temporarily returns control from the iterator method that invoked the iterator. Specifically, control flow goes from the iterator to the block of code assoicated with the invocation of the iterator. When the end of the block is reached, the iterator method regains control and execution resumes at the first statement following the yield. In order to implement some kind of looping construct, an iterator method will typically invoke the yield statement multiple times.

Documentation sources:
-- Learning Python, by Mark Lutx. 5th Edition, O'Reilly 2013.
-- The Ruby Programming Language, by David Flanagan & Yukihiro Matsumoto. O'Reilly 2008.
-- http://www.tutorialspoint.com

No comments:

Post a Comment