3 ms·
Given a = [1, 2, 3, 4, 5]: 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]
by asher 16y ago
Given a = [1, 2, 3, 4, 5]:
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.
- samdk 16y agoI see your point, but I think your Python's not terribly idiomatic and that's a big part of the problem. 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.)
- LordLandon_ 16y ago[(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])
- samdk 16y agoI kept the i+1 separate because the parent noted that this was a trivial example and wanted to make a point about the more general map-filter-map operation. I wanted to make the point that you can do the map-filter-map in Python more succinctly and more efficiently without sacrificing any power or flexibility. 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.
- cageface 16y agoI find the Ruby version clearer because it proceeds from left to right like a shell pipeline. 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.
- __david__ 16y agoOh, I want to play! Here's Perl: 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...
- ekiru 16y agoIn Perl 6: @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.
- Jach 16y agoI think this is a great example why Perl 6 highly intrigues me but at the same time scares the crap out of me. Thanks for sharing.