5 ms·
Colin, would a reasonable implicit assumption be that if the problem being solved isn't a problem for me, then I should stick with the language in question? I
by shrikant 15y ago
Colin, would a reasonable implicit assumption be that if the problem being solved isn't a problem for me, then I should stick with the language in question?
I mean, for e.g., if I don't care that Python's not OO enough, I don't need to know Ruby, right? Right?!
p.s: in case it's not apparent, I will be using these graphs to justify some laziness on my part :)
- kragen 15y agoThere might be other reasons that are more important to you. e.g. if you've been using Modula-3, its syntax might be a reason to switch to Python, but in my mind, things like the iterator protocol and NumPy are better reasons. (Also, you've been asleep under a rock for fifteen years.) In my case, I've been surprised at how much less syntactic overhead Ruby imposes than Python (despite the explicit "end"!), and how much it matters. Compare: def mouse_clicked x = mouse_x / @cellsize y = mouse_y / @cellsize if @cells[x][y] == :empty set x, y, :wire elsif mouse_button == LEFT set x, y, :empty else set x, y, :electron_head end end def mouse_clicked(self): x = processing.mouse_x() / self.cellsize y = processing.mouse_y() / self.cellsize if self.cells[x][y] is Empty: self.set(x, y, Wire) elif processing.mouse_button() == processing.LEFT: self.set(x, y, Empty) else: self.set(x, y, ElectronHead) (Here ElectronHead and the like are assumed to be top-level empty classes in the module; the Ruby equivalent is a Lisp-style atom that doesn't need to be declared.) To me, the Ruby version has a much higher signal-to-noise ratio. Another thing: JRuby seems to be a lot more mainstream among Rubyists than Jython is among Pythonistas, which I think means it's a bit more solid.
- karmalizer 15y agoI don't understand why you think the Ruby code has better SNR. The python code is two lines shorter. In python, you have to type the (), but that's not really "noise". I do hat the : at the end of python control statements. I always forget them, and it seems like the parser could do it's job just as well without them.
- kragen 15y ago> I don't understand why you think the Ruby code has better SNR. Because they say the same thing, but the Python code is much longer, if you count tokens instead of lines. It has about 28-30 more tokens than the Ruby code does, about three per line. Those tokens don't convey any information. They're just redundancy, i.e. noise. And there are a lot of them; these redundant tokens are something like a third of the code. Now, redundancy can be useful (see e.g. http://www.paulgraham.com/redund.html http://www.paulgraham.com/redund.html) but it is costly, so you should keep it to the minimum that achieves what you want the redundancy to achieve.
- Peaker 15y agoHaskell removes not only the () that Ruby does, but also the commas. It also uses a simple precedence trick to remove almost all () in general (not just for functional calls). I wanted to translate the code to Haskell directly, but the idioms don't map that well (self.cellSize would becomes something else entirely, and the comparison would not be fair).
- kragen 15y agoI'm curious what it would look like.
- reinhardt 15y agoI'm curious if you would still think of token count as a useful metric if the Ruby version turns out to be twice the size of the equivalent code in J or K due to "redundant" "noise" tokens. Programs should be written for people to read, and only incidentally for machines to execute.
- kragen 15y agoI do think it's a useful metric, and I am often tempted by the low token count of Forth and APL dialects like J and K, but much of the APL reduction in code size comes not from token-count reduction, but from token-length reduction, which is much less praiseworthy in my book. Here's Life in Dyalog APL, from http://dfns.dyalog.com/c_life.htm http://dfns.dyalog.com/c_life.htm and http://news.ycombinator.com/item?id=1041500 http://news.ycombinator.com/item?id=1041500: life←{ ⍝ John Conway's "Game of Life". ↑1 ⍵∨.^3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵ ⍝ Expression for next generation. } Unfortunately, my APL is pretty rusty, and Dyalog has added some major extensions to the language, so I'm not quite sure what that means, in particular the juxtaposition of 1 and ⍵ toward the beginning. I think it parses as vertically_rolled = outerproduct(⌽, [-1, 0, 1], ⊂(⍵)) horizontally_rolled = outerproduct(⊖, [-1, 0, 1], vertically_rolled) neighbor_counts = sum(ravel(horizontally_rolled)) ↑(⍵(1, innerproduct(and, or, ⍵, [3, 4] == neighbor_counts))) but I'm not even sure of that. The Life rule (which is pretty optimal for being expressed in APL, given its array orientation) is slightly simpler than the Wireworld rule: def run_wireworld_rule old_cells = @cells @cells = fresh_cells each_coord do |x,y| # This could be optimized somewhat by only recalculating # the neighbors of dirty cells. case old_cells[x][y] when :electron_head set x, y, :electron_tail when :electron_tail set x, y, :wire when :empty # do nothing; fresh_cells are all :empty when :wire case electron_head_count old_cells, x, y when 1, 2 set x, y, :electron_head else # Don’t call `set` in this case so as not to mark # the cell dirty for redrawing. @cells[x][y] = :wire end end end end # This could perhaps be optimized somewhat with a sum table. def electron_head_count(cells, base_x, base_y) count = 0 ([0, base_x-1].max..[base_x+1, @nx-1].min).each do |x| ([0, base_y-1].max..[base_y+1, @ny-1].min).each do |y| count += 1 if cells[x][y] == :electron_head end end return count end def each_coord (0..@nx-1).each do |x| (0..@ny-1).each do |y| yield x, y end end end The APL people claim that their programs are more readable because they're shorter, but I'm not sure how much I believe their claim. It's certainly true that other kinds of mathematical notation benefit enormously from brevity and consistency that permits mechanical manipulation, and I don't see why algorithms should be different, but empirically I have a lot less trouble with Ruby or Python (or even C) than with plain-English descriptions of mathematical equations.
- rufugee 15y agoHi there...experienced ruby guy here who is doing python work at the moment. The nice thing I find about python, even in its verbosity relative to ruby, is that I always for the most part know what it's doing. With ruby, the impulse to overload operators and create dsls mean that I have to learn a new language (potentially) with every new project or library I choose to use. It's not immediately clear what code is really doing. With python, it usually is. Just my (quick) two cents.
- kragen 15y agoYes, I think the verbosity I'm complaining about above is the fruit of three of the principles of the Zen of Python: Explicit is better than implicit. Errors should never pass silently. Namespaces are one honking great idea -- let's do more of those! Maybe after I spend more time with Ruby, I'll see the dark side of DSLs, but so far I'm loving it :)
- rufugee 15y agoYou probably will, if you're anything like me. However, at the end of the day, I find the "require" statement in ruby to be the truly most irritating feature. You simply don't get much control over what is slurped into your code. Python's import statement is so refreshing in comparison. I feel more in control in python, and I say this as someone who really likes ruby.
- thurn 15y agoI don't understand this code. In the Ruby example, mouse_x() appears to be a method on the current object, in the Python one it's a method on some other object (a module named "processing"?). Since the code examples are the same, I'd say whichever language I'm wrong about is the less clear one.
- kragen 15y agoYes. I don't have a Python version of Processing, so I thought about how the Processing API would best be exposed in Python — and it would probably be by calling functions exported by a module named "processing", rather than by inheriting your class from processing.App. So you're right about both languages.
- Peaker 15y agoYou could use: from processing import ...
- shazow 15y agofrom processing import LEFT, mouse_x, mouse_y, mouse_button Now you don't have to prefix things with `processing` just like you don't in your Ruby version.
- kragen 15y agoIf the program used them in several places, that would be an improvement, but it doesn't. It's maybe a little bit atypical, but nearly all of the things it calls from Processing, it calls in exactly one site: the above, plus color_mode, no_stroke, smooth, background, and rect. It does call fill from four call sites, but that was because there's no reasonable way to return a list of four arguments from a method in Ruby, as far as I can tell.
- zwp 15y ago> way to return a list of four arguments from a method p, q, r, s = * method_that_returns_array_of_4_things * aka "splat operator"
- kragen 15y agoThat's great, thanks! And that works for method calls too. So now I can replace set_color_from @cells[x][y] with fill *color_from(@cells[x][y]), reducing the number of side-effecting methods by one, and now I'm calling every Processing function in at most one place.