Perhaps I'm way off here but the languages seem to me to show some of their cultural heritage. Python has a blunt & minimalistic Dutchness about it while Ruby has a Japanese elegance and style.
Ruby is more fun,but python is more mature. In the end - whatever gets the job done right?
There are a number of things that made me think Ruby was made by a less astute designer. The 'end' everywhere feels bad in a world where I know both "}" and a newline can accomplish the same thing but with less typing and screen clutter. Also do not like Ruby's dropping of ()'s from fn call statements, because it blurs the boundary with non-calls and non-expressions and control flow keywords. I also feel Ruby encourages a certain kind of DSL creation which makes the code harder to read and maintain. I also don't think every app or library needs to export a DSL into client space. It's better if the interface it publishes still looks and feels like the prog language used.
That said, I acknowledge Ruby has good qualities. Python is not perfect, but to me it gives the better designed feeling. Actually the Lisps seem to be the most elegant and powerful. Love their regularity and power but not (yet) all the parentheth everywhereth.
People sometimes do go overboard with DSLs in Ruby too but the big problem I have with Python code is that it all looks the same. The shape of Ruby code is usually a good indicator of what it's doing, partly thanks to DSL. Python seems to have gone just far enough in making all code look the same to defeat quick visual intuition. Why not take it a step further and get a completely regular, homoiconic syntax and all the Lisp-y benefits it brings?
To me Ruby hit the sweet spot exactly between Lisp and Perl. If I had to code in Python I wouldn't hate it though. There are far worse languages.
I really enjoy the pretty simply object model, the message passing OO, and blocks.
An interesting point that I read somewhere the other day, from someone who codes in a lot of both: Ruby (to him) is kind of ugly, and Python is very pretty. Yet, the exact parts that make Ruby ugly allow it to have _awesome_ libraries. Python, with its rigidity, means that the core language is great, but the libraries end up being worse because of it. He uses RSpec as an example, specifically. So for him, Ruby ends up being better to use, even though he likes the Python language better.
Apropos: Does Ruby optimize tail calls?
Ruby: a = %w( apple pear orange )
Python: a = [ "apple", "pear", "orange" ]
Ruby: a=%q/O'Malley says "Hello"/
Python: a = "O'Malley says \"Hello\""
Python lets you use "'" or '"' as the delimiter, which solves 80% of these cases - but why not generalize the concept, as Perl and Ruby did?In Ruby, the last expression in a function is the return value; in Python you must use the return keyword, even though there's no ambiguity.
a = '''O'Malley says "Hello"''' a = "apple pear orange".split()For example, if there's a function composed entirely of print statements, the last expression evaluated will be the expression in the last print statement executed. But, that means you have to know that 'print' is a statement and not an expression, and that the right-hand side of the print statement is in fact an expression.
In ruby, actions like prints and assignments are side-effects and always evaluated as expressions. So if puts is the last expression in a function you'll get the return value of puts (which is usually nil I believe). That's pretty simple.
Ruby: a.map{|i| i+1}.reject{|i| i%3 == 0}.map{|i| i*i}
Python: [i*i for i in filter(lambda i: i%3 != 0, [i+1 for i in a])]
Please ignore the fact that the whole operation can be simplified mathematically - nontrivial map-grep-map operations do occur.I find the Ruby version clearer because it proceeds from left to right like a shell pipeline.
This is easier to read and understand, only goes through the list twice, and loses nothing in terms of power:
[j*j for j in [i+1 for i in a] if j%3 != 0]
(And for any given operation, there's very possibly a cleaner way to abstract out the inner list comprehension, which would again make it a lot nicer.)In general, I don't see much of a reason to use filter/map/etc in Python: weak lambdas mean they're not terribly powerful. List/sequence comprehensions can do everything they can do with cleaner syntax and/or fewer operations.
This, I think, is actually at the core of this whole discussion. The Ruby/bash approach makes sense if you're used to working with sequences like Ruby/bash do. The Python approach is more natural to me though, because I've written a lot of Python. (And having spent the last year writing a lot of Ruby, I still find the Python approach cleaner/easier to understand at a glance.)
[(i+1)**2 for i in a if i%3!=2]
Goes through the list once, and reads like set notation!
(and does i+1 once, which is what I assume you were going for with the separate [i+1 for i in a])In this example, yes, it's easy to solve the problem with only one iteration through the list. In a more complicated example (especially when the first map step is expensive and you really only want to do it once) this kind of solution may not work.
This is an excellent point. Ruby code can be some of the cleanest, most readable functional code there is. I have these kinds of chained pipelines in my Ruby code all over the place and they're much easier for me to follow than similar expressions in Lisp or Haskell and than Python's comprehensions.
map { $_*$_ } grep { $_%3 != 0 } map { $_+1 } @a
Interestingly, in this particular case Perl seems to have way less syntax than ruby or python, which I find rather ironic. It does have to be read backwards though, because of the syntax of map and grep... @a.map({ $_ + 1 }).grep({ $_ % 3 != 0 }).map({ $_ * $_ })
Alternately, using the feed operator ==>: @a ==> map { $_ + 1 } ==> grep { $_ % 3 != 0 } ==> map { $_ * $_ }
And, if your code being readable to people who've never used Perl 6 and know nothing about it isn't a concern, you can use the much more concise Whatever star notation for closures: @a ==> map * + 1 ==> grep * % 3 != 0 ==> map { $_ * $_ }
Using Whatever for the last one would neither work nor be readable(although you could do "* 2", but that's still suboptimal for readability), since the last "" in " * " would be interpreted as a second parameter.If the preceding notations for anonymous functions have been too implicit for you, then you can use pointy blocks(or just plain anonymous function declarations).
@a ==> map -> $i { $i + 1 } ==> grep -> $i { $i % 3 != 0 } ==> map -> $i { $i * $i }
Or, you could use hyper operators instead of the maps: ((@a >>+>> 1).grep: {$_ % 3 != 0}) >>**>> 2
The hyper-operators extend the scalar operators "+" and "*" to lists. Since the ">>" point to their operands on the right side, that operand will be extended to be as long as the other side, allowing you to use a single scalar as the right-side element.