OK, here’s a simple task. A function which returns an array of f(0) - f(x), where f(n) is the Fibonacci number of n. A simple solution should expose some of the language differences when using lists.
Let’s look at PHP first.
<?php
function fib_upto($x, $position = 0, $history=array())
{
if ($x < 0) {
throw new Exception(”Input out of range”);
}
if ($position <= 1) {
$history[$position] = $position;
} else {
$history[$position] = $history[$position - 1] + $history[$position - 2];
}
if ($position < $x) {
return fib_upto($x, $position + 1, $history);
} else {
return $history;
}
}
echo implode(fib_upto(20), “,”);
?>
A ruby equivalent:
def fib_upto x, position = 0, history=[]
raise “Input out of range” if x < 0
if position <= 1
history[position] = position
else
history[position] = history[position - 1] + history[position - 2]
end
if position < x
fib_upto(x, position + 1, history)
else
history
end
end
print fib_upto(20).join(”,”)
And now some Python:
def fib_upto(x, position = 0, history=[]):
if x < 0: raise Exception(”Input out of range”)
if position <= 1:
history.append(position)
else:
history.append(history[position - 1] + history[position - 2])
if position < x:
return fib_upto(x, position + 1, history)
else:
return history
print “,”.join(map(str, fib_upto(20)))
As you can see, the Python script wins in terms of number of lines of code. The famous semantic whitespace makes braces and ‘end’ directives redundant, saving some lines of code. It also forces good coding style on developers, which is a Good Thing.
PHP is littered with unnecessary characters - ‘<?php’, ‘?>’, ‘$’, ‘{’ and ‘}’ in this case. They seem to get in the way of coding without really adding anything in the way of readability. PHP also forces you to use brackets around ‘if’ clauses.
Ruby appears to be the cleanest-looking code here. It may need ‘end’ commands to terminate blocks, but they start without the curly brace of PHP or the colon of Python. The ‘raise’ at the beginning of the function shows Ruby’s trick of appending a conditional to the end of a line, which is both readable and terse. Python can do this task in one line, but requires a colon to do so.
A subtle language difference regarding lists is shown by the Python command:
history.append(position)’
Attempting to assign a value to a member of a list which hasn’t been previously defined causes an error. Try changing the line to read ‘history[position] = position’ and see what happens.
Both PHP and Ruby can handle this, meaning lists are more flexible - but it could be argued that a Hash, or Dictionary in Python speak, would be better suited to that kind of use.
While PHP and Python require explicit ‘return’ commands, Ruby just returns the object resolved by the last line of executed code. A return can be used optionally (to pop out of a function for example) but in most cases it’s not required. This can lead to some very tight code if used correctly and encourages methods which can be chained together. It seems like a small thing, but I actually miss it a great deal when I work in PHP.
Coming to the last line, we’re joining the elements of the array together to make a string. PHP makes this easy (if quirky) with the ‘impolde’ command. Ruby handles it neatly and intuitively with a join method on the array. Python, on the other hand, has its join method on the string. This seems like a strange place to put it, as you’re in effect operating on the delimiter.
You’ll notice another layer of complexity here, added by a mapping function. Here we’re passing in ’str’ as a function to convert each element of the array into a string before operating on it with join.
It appears that join only works on arrays of strings in Python. Presumably, Ruby calls the ‘.to_s’ on each element behind the scenes to avoid this problem.
The mapping could be replaced with the following, more ‘modern’ Python constructs:
print “,”.join([str(x) for x in fib_upto(20)]) # using str(x) in a list comprehension.
or
print “,”.join(str(x) for x in fib_upto(20)) # using generator comprehension, avoiding an intermediate list.
(Thanks go to SpiralX, my Python mentor, for these code fragments.)
Generators look like a very handy feature - readable and powerful. It looks like it’ll be fun learning to use them.
That’s all for now. Next time I’ll try generating Sierpinski and Pascal triangles, so we can see how the different languages handle loops.