Need examples comparing Ruby to Python

David MacQuigg said:

Yes. That works if I change the ‘it’ to ‘t’.

Yeah, I lost the “i” when I was fixing a line wrap issue in the email
client. Sorry.

BTW, I reviewed the docs for squeeze, and you probably will want to use
squeeze(" ") instead.

···


– Jim Weirich jim@weirichhouse.org http://onestepback.org

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

David MacQuigg wrote:

I’m not seeing any fundamental advantage of Ruby blocks over Python
functions. If your block is more than one line, or needs a print
statement, just give it a name and let it be a function. lambda’s are
used only for very short blocks, where for example, you want to pass a
simple function in an argument list, but don’t want to waste a line
giving that function a name.

David,

The last thing I want is to start a flame war, especially since my
knowledge of Python is practically zero.

But I had to laugh at that statement – “if your block is more than
one line, or needs a print statement, just give it a name and let it
be a function.”

I’m not saying that your arguments are ridiculous or laughable – I’m
just saying there is obviously some very fundamental disconnect between
your thinking and mine. I’d like to discover the nature of that
disconnect.

Here is a simple example of a block that is both multi-line and has a
print statement. (Granted it could be done in one line.)

words.each do |word|
consonants = word.gsub(/[aeiou]/,“”)
puts consonants
end

How would you do that with a named function, and why would the new way
be better?

If I do:

def func(word)
consonants = word.gsub(/[aeiou]/,“”)
puts consonants
end

words.each {|word| func(word) }

then I have still used a block, one that happens to call a function
(with a name) that will only be used once.

Is this better than the other way?

Thanks,
Hal

I’m not seeing any fundamental advantage of Ruby blocks over Python
functions.

there are no fundamental advantages of any turing complete language over
another correct? iff so this is kindof a moot statement. however there is a
‘difference’: blocks form closures, function calls do not:

def callback; yield; end
def a_callback? x; x = 42; end

x = nil
callback{ x = 42 }
p x # => 42

x = nil
a_callback? x
p x # => nil

If your block is more than one line, or needs a print statement, just give
it a name and let it be a function. lambda’s are used only for very short
blocks, where for example, you want to pass a simple function in an argument
list, but don’t want to waste a line giving that function a name.

technically speaking (see above) this is true. however, consider apis which
have a massive proliferation of callbacks passed using blocks - say a gui api.
writing a separate method for each button may be over kill and not lend itself
to abstraction very well. there were a series of articles onlines recently
where matz said something about concentrating on the human aspect of computing
and that really rang true to me, what i mean is:

would you rather see

comment = %r/^\s*#/o
empty = %r/^\s*$/o

open(path).each do |line|
next if line =~ emtpy or line =~ comment
key, value = line.split(%r/=/o).map{|x| x.strip}
opts[key] = value
end

or

f = open(path)
comment = %r/^\s*#/o
empty = %r/^\s*$/o

begin
while((line = f.gets))
next if line =~ emtpy or line =~ comment
parse_line line, opts
end
ensure
f.close
end

insert 1000 lines of source code here so pare_line is nice and tough to find

def parse_line line, opts
key, value = line.split(%r/=/o).map{|x| x.strip}
opts[key] = value
end

doesn’t matter? how about if there a 50 of them? 100? if there were a
hundred of them would you rather see

handlers = Hash[
:foobar => proc{|arg| p arg},
:barfoo => proc{|arg| raise arg},
:foo => proc{|arg| throw arg},
# 100 more…
]

or

these are handlers - have fun naming all of them

def foobar arg; p arg; end
def barfoo arg; raise arg; end
def foo arg; throw arg; end

100 more…

now say you actually had to pass 20 or so handlers to a method - how would you
do this with functions? it would surely be uglier than:

parse_config handlers

obviously you could maintain a string list of all the handlers and keep it in
sync with your definitions… (sounds like a job for code generation -
tedious!)

see what i’m driving at: skipping around the source finding all those defs can
be frustrating and lead to errors. the stack overhead is also higher - though
that probably really is moot! :wink:

I like Ruby’s more compact |x| instead of Python’s keyword - lambda x: - but
that is just my personal preference.

that’s as ‘fundamental’ as it gets with turing languages - look no further! :wink:

truely - much research has been done to correlate a programmer’s productivity
with lines of code written - NOT with the type of code, in otherwords:

‘compact’ => ‘advantage’

and, i beleive, this relationship is some sort of exponential curve.

cheers.

-a

···

On Tue, 24 Feb 2004, David MacQuigg wrote:

===============================================================================

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
URL :: Solar-Terrestrial Physics Data | NCEI
TRY :: for l in ruby perl;do $l -e “print "\x3a\x2d\x29\x0a"”;done
===============================================================================

Hi,

If I do:

def func(word)
consonants = word.gsub(/[aeiou]/,“”)
puts consonants
end

words.each {|word| func(word) }

then I have still used a block, one that happens to call a function
(with a name) that will only be used once.

A difference between ruby and python is that functions can easily be
passed as arguments in python. For example a function that returns the
composition of two functions applied to a value. (An artificial example :slight_smile:

def compose(f, g, x):
return f(g(x))

But then this is even more nicer i scheme.

(defun (compose f g)
(lambda (x) (f (g x))) ;; returns a function, that can be applied later.

((compose 1+ 1+) 1) => 3

In ruby you have to create a Method instance first, and then pass it as an
argument. (newbie warning. :slight_smile:

, Tobias

···

On Wed, 25 Feb 2004, Hal Fulton wrote:

David MacQuigg wrote:

I’m not seeing any fundamental advantage of Ruby blocks over Python
functions. If your block is more than one line, or needs a print
statement, just give it a name and let it be a function. lambda’s are
used only for very short blocks, where for example, you want to pass a
simple function in an argument list, but don’t want to waste a line
giving that function a name.

David,

The last thing I want is to start a flame war, especially since my
knowledge of Python is practically zero.

Hal,

You won’t get any flames from me, especially since my knowledge of
Ruby is near zero. :>)

[…]

Here is a simple example of a block that is both multi-line and has a
print statement. (Granted it could be done in one line.)

words.each do |word|
consonants = word.gsub(/[aeiou]/,“”)
puts consonants
end

How would you do that with a named function, and why would the new way
be better?

If I do:

def func(word)
consonants = word.gsub(/[aeiou]/,“”)
puts consonants
end

words.each {|word| func(word) }

then I have still used a block, one that happens to call a function
(with a name) that will only be used once.

Is this better than the other way?

I would say “yes”, but that is probably a personal bias due to my
better knowledge of Python. In Python, I would say:
for word in words: func(word)
which seems like the most natural way to say it.

The original statement was a response to the complaint I’ve heard
numerous times that Python’s lambda functions are no good ( one line
only, no print statements, etc.) I wasn’t being critical of Ruby
blocks, just saying that I see no reason to have them in Python. 99%
of the time, I just use a simple function. For the few cases where I
want to squeeze these functions into a really tight space, I use
lambda functions. The irony to me is that they wasted six characters
for the name ‘lambda’ and added it as a keyword to a language with
only 29 keywords. I would rather see |x,y| instead of ‘lambda x,y:’
in a tight space like an argument list. I also like @name instead of
self.name, but that is a debate that has been going on for about ten
years on the Python lists.

Adding up all of the above “personal preference” pluses, they seem
about equal to the one big personal preference minus I see - the fact
that Ruby doesn’t use indentation to establish structure. When I
first started using Python, I thought that was strange, then I found
that I almost never made a mistake with indentation, and when I did,
the interpreter caught it right away. The big plus is that I can now
read other peoples code and count on the indentation to mean
something.

– Dave

···

On Wed, 25 Feb 2004 05:06:02 +0900, Hal Fulton hal9000@hypermetrics.com wrote:

I’m not seeing any fundamental advantage of Ruby blocks over Python
functions.

there are no fundamental advantages of any turing complete language over
another correct? iff so this is kindof a moot statement.

What I mean by “fundamental advantage” is not anything to do with
Turing completeness, but rather the amount of effort required to
perform useful tasks, assuming equal familiarity with the chosen
language. This has to be something more than just personal
preference. It seems to me there is no fundamental advantage of
(-1942).abs over abs(-1942), although I prefer the latter.

I’m finding it very difficult to separate fundamental advantage from
personal preference, but I think we can all agree that
fnlt = line.chomp.squeeze.split(/\s*|\s*/)
is less mental effort than
fnlt = [’ ‘.join(t.split()).strip() for t in line.split(’|')]

It seems to me there is something more fundamental here than personal
preference. With the first statement, I can type the operations in
the order that I need to visualize intermediate results. I can even
hit return with each added method, and see these intermediate results
on the screen. Then ‘up arrow’ and continue with the next step. With
the second statement, I really have to struggle, building it
piece-by-piece, with lots of cut-and-paste. This extra effort is in
spite of the fact that, being a Python user, I am more familiar with
the methods in the second statement.

This string processing problem is one of the few examples I have been
able to isolate where Ruby has what I would call a fundamental
advantage over Python. Everything else so far has turned out to be
less-than-optimimum coding, or just personal preference.

however there is a
‘difference’: blocks form closures, function calls do not:

def callback; yield; end
def a_callback? x; x = 42; end

x = nil
callback{ x = 42 }
p x # => 42

x = nil
a_callback? x
p x # => nil

I don’t understanding the purpose of this example, so I don’t know how
to write some Python that we can compare it with. We need to back up
a step or two and say what useful result we are trying to achieve.
With that as a starting point, maybe we can resolve a question like
“Are closures really necessary?”

If your block is more than one line, or needs a print statement, just give
it a name and let it be a function. lambda’s are used only for very short
blocks, where for example, you want to pass a simple function in an argument
list, but don’t want to waste a line giving that function a name.

technically speaking (see above) this is true. however, consider apis which
have a massive proliferation of callbacks passed using blocks - say a gui api.
writing a separate method for each button may be over kill and not lend itself
to abstraction very well. there were a series of articles onlines recently
where matz said something about concentrating on the human aspect of computing
and that really rang true to me, what i mean is:

Python and Ruby share a common goal here - making programming easier
for humans. I’ve been pretty happy with Python since I started using
it about a year ago. Just recently, some folks I’m working with in
the UserLinux group told me that Ruby was something we should
consider, so I started investigating. Most of what I’ve found highly
touted in Ruby turns out to be just personal preference, but having
found the example above, I believe there may be a few more.

I think what has happened is that both languages have adopted each
others best features, so there may be no longer many fundamental
differences that can be shown with short examples. It may be that the
remaining examples are larger than just the five-line snippets I was
hoping to put on our web page.

I have to break from this project and get some other work done, but I
will study the example below and others that have been suggested.

–Dave

···

On Tue, 24 Feb 2004 15:41:01 -0700, “Ara.T.Howard” ahoward@fattire.ngdc.noaa.gov wrote:

On Tue, 24 Feb 2004, David MacQuigg wrote:

would you rather see

comment = %r/^\s*#/o
empty = %r/^\s*$/o

open(path).each do |line|
next if line =~ emtpy or line =~ comment
key, value = line.split(%r/=/o).map{|x| x.strip}
opts[key] = value
end

or

f = open(path)
comment = %r/^\s*#/o
empty = %r/^\s*$/o

begin
while((line = f.gets))
next if line =~ emtpy or line =~ comment
parse_line line, opts
end
ensure
f.close
end

insert 1000 lines of source code here so pare_line is nice and tough to find

def parse_line line, opts
key, value = line.split(%r/=/o).map{|x| x.strip}
opts[key] = value
end

doesn’t matter? how about if there a 50 of them? 100? if there were a
hundred of them would you rather see

handlers = Hash[
:foobar => proc{|arg| p arg},
:barfoo => proc{|arg| raise arg},
:foo => proc{|arg| throw arg},

100 more…

]

or

these are handlers - have fun naming all of them

def foobar arg; p arg; end
def barfoo arg; raise arg; end
def foo arg; throw arg; end

100 more…

now say you actually had to pass 20 or so handlers to a method - how would you
do this with functions? it would surely be uglier than:

parse_config handlers

obviously you could maintain a string list of all the handlers and keep it in
sync with your definitions… (sounds like a job for code generation -
tedious!)

see what i’m driving at: skipping around the source finding all those defs can
be frustrating and lead to errors. the stack overhead is also higher - though
that probably really is moot! :wink:

I like Ruby’s more compact |x| instead of Python’s keyword - lambda x: - but
that is just my personal preference.

that’s as ‘fundamental’ as it gets with turing languages - look no further! :wink:

truely - much research has been done to correlate a programmer’s productivity
with lines of code written - NOT with the type of code, in otherwords:

‘compact’ => ‘advantage’

and, i beleive, this relationship is some sort of exponential curve.

cheers.

-a

Tobias Nurmiranta said:

A difference between ruby and python is that functions can easily be
passed as arguments in python. For example a function that returns the
composition of two functions applied to a value. (An artificial example :slight_smile:

def compose(f, g, x):
return f(g(x))

But then this is even more nicer i scheme.

(defun (compose f g)
(lambda (x) (f (g x))) ;; returns a function, that can be applied later.

((compose 1+ 1+) 1) => 3

The Python and Scheme examples are a bit different. The Scheme code
returns a newly created function that is the composition of two functions.
The Python code merely returns the result of applying two functions to a
value.

The Scheme version in Ruby …

oneplus = lambda {|x| x+1 }
def compose(f, g)
lambda { |x| f[g] }
end
compose(oneplus, oneplus) [1] #=> 3

The Python version of compose is more like this …

def compose(f, g, x)
f[g]
end

···


– Jim Weirich jim@weirichhouse.org http://onestepback.org

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

What I think that you’ll find is that many people who chose Ruby over Python
is over this very issue. I had the choice to learn Python or Ruby. I think
that syntactically significant whitespace is one of the dumbest ideas I’ve
ever heard of, and refuse to learn Python for that very reason, even though
Python and Ruby are effectively equivalent at this point from a language
functionality perspective. Some people (like yourself) prefer it. I don’t.
And that’s the main difference between Ruby and Python from my perspective.

There is a logical reason for this. Often, when I’m doing “spot debugging”,
I’ll do something like:

class Foo
def bar(x)
p @baz
@baz = x
p @baz
end

I outdent the “p” lines because it is something that is going to go
“bye-bye” when I am done with the spot-debugging. Some I’ll promote to full
debugging statements that become part of the official code, but that’s rare.
In vim, I can do /^p and find these in my files for easy deletion.

Sure, there are other things that bother me more, now that I understand
Python a bit more than I did when I made the initial choice. 1. Python’s OO
capabilities feel as unnatural as Perl’s OO capabilities and just as
bolted-on. 2. There’s not enough consistency; things that would sensibly be
methods might be functions in Python (making some of the same choices as
Perl). I believe that join and map are cases for this. 3. The culture of
Python – there’s “one right way” to do things – doesn’t fit what I liked
about the culture of Perl. 4. The culture of Python, to me, seems less
welcoming than the culture of Ruby.

-austin

···

On Wed, 25 Feb 2004 10:49:49 +0900, David MacQuigg wrote:

Adding up all of the above “personal preference” pluses, they seem about
equal to the one big personal preference minus I see - the fact that Ruby
doesn’t use indentation to establish structure. When I first started
using Python, I thought that was strange, then I found that I almost
never made a mistake with indentation, and when I did, the interpreter
caught it right away. The big plus is that I can now read other peoples
code and count on the indentation to mean something.


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2004.02.25
* 12.52.48

Hi,

The Python and Scheme examples are a bit different.

Yes, thats why I had a comment in the scheme code.

The Scheme version in Ruby …

oneplus = lambda {|x| x+1 }
def compose(f, g)
lambda { |x| f[g] }
end
compose(oneplus, oneplus) [1] #=> 3

Ah interesting, so one can use f instead of f.call(x), by defining
in Method. But if you want to call compose with defined functions instead
of lambdas, you have to use:
compose(method(:f1), method(:f2))

I guess that this syntax “overhead” is due to that we want to be able to
call function f with “f” instead of “f()”. Otherwise functions could be
passed as arguments more easily, with one syntax to call them.

Given “oneplus = lambda {|x| x + 1}” wouldn’t it be possible to call the
function with “oneplus(1)”, implemented so that the interpreter checks if
it is a Method instance, and then calls the method. Then you wouldn’t have
to have several ways to call a function.

The Python version of compose is more like this …

def compose(f, g, x)
f[g]
end

My python interpreter (2.3.2) doesn’t accept this code.
, Tobias

···

On Wed, 25 Feb 2004, Jim Weirich wrote:

Austin Ziegler wrote:

I think that syntactically significant whitespace is one of the
dumbest ideas I’ve ever heard of, and refuse to learn Python for
that very reason,

Same here! Every once in a while I have to go into some python code,
and the “indentation level” thing just annoys the heck out of me.

I do like the fact that it uses native threads though… that’s my
number one wish for ruby.

-joe

Tobias Nurmiranta said:

Ah interesting, so one can use f instead of f.call(x), by defining
in Method. But if you want to call compose with defined functions instead
of lambdas, you have to use:
compose(method(:f1), method(:f2))

It would be more accurate to say: “But if you want to call compose with
defined methods instead of functions, you have to get a callable version
of the method by using …”

You see, Ruby doesn’t have functions. Not really. What it does have are
objects that look sufficiently like functions that we can can use them in
functional ways. These “functional objects” (or “functors”) can be called
by sending a “call” message to the object. The “” convention is just a
little syntax sugar for the call message.

I guess that this syntax “overhead” is due to that we want to be able to
call function f with “f” instead of “f()”. Otherwise functions could be
passed as arguments more easily, with one syntax to call them.

More accurately, the syntax “f()” is reserved for sending the message “f”
to self, not for function calls.

Given “oneplus = lambda {|x| x + 1}” wouldn’t it be possible to call the
function with “oneplus(1)”, implemented so that the interpreter checks if
it is a Method instance, and then calls the method. Then you wouldn’t have
to have several ways to call a function.

There are no functions in Ruby, only functors. And there is only one way
to call a functor … by sending it a message.

I think this goes to the heart of the difference between Python and Ruby.
Pythonists see functions as fundamental and build objects composed of
functions callable through a dictionary mechanism. Rubyist see objects as
fundamental and build functions through callable objects.

The Python version of compose is more like this …
[…]
My python interpreter (2.3.2) doesn’t accept this code.

I should have said “The Ruby translation of the Python version of compose…”

···


– Jim Weirich jim@weirichhouse.org http://onestepback.org

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

already defined for you in ‘callable’ objects (proc, methods,
contiunations)

···

il Wed, 25 Feb 2004 17:34:56 +0900, Tobias Nurmiranta spyck@lysator.liu.se ha scritto::

Ah interesting, so one can use f instead of f.call(x), by defining
in Method.

Hear, hear! I have been promoting Ruby at work for web stress testing, but at
this point it looks like I’ll have to suspend the effort and switch to Python
mainly because of its superior threading implementation.

···

On Thu, Feb 26, 2004 at 03:18:50AM +0900, Joseph McDonald wrote:

I do like the fact that it uses native threads though… that’s my
number one wish for ruby.


Jos Backus / /// Sunnyvale, CA
_/ _/ _/
/ ///
_/ _/ _/ /
jos at catnook.com // //
/ require ‘std/disclaimer’

Tobias Nurmiranta schrieb:

Hi,

Moin!

Given “oneplus = lambda {|x| x + 1}” wouldn’t it be possible to call the
function with “oneplus(1)”, implemented so that the interpreter checks if
it is a Method instance, and then calls the method. Then you wouldn’t have
to have several ways to call a function.

Yes, but it wouldn’t be possible to call it without parentheses and
there would be other semantic differences.

I think it’s either optional parentheses or function object access via name.

The Python version of compose is more like this …
def compose(f, g, x)
f[g]
end
My python interpreter (2.3.2) doesn’t accept this code.

Yup, you will have to backport it. I think he just used Ruby to show the
different semantics of the original posters LISP and Python versions.
(The LISP version returns a function which can be called to get the
result and the Python version directly returns the result.)

Backported it looks like this:

def compose(f, g, x): # the result version
return f(g(x))

def compose_callable(f, g): # the function version
return lambda x: f(g(x))

Regards,
Florian Gross

I think it’s either optional parentheses or function object access via name.

Yes, I guess the choice in Ruby is quite ok.

Yup, you will have to backport it. I think he just used Ruby to show the
different semantics of the original posters LISP and Python versions.
(The LISP version returns a function which can be called to get the
result and the Python version directly returns the result.)

Backported it looks like this:

def compose(f, g, x): # the result version
return f(g(x))

def compose_callable(f, g): # the function version
return lambda x: f(g(x))

Yes, I just didn’t have the energy to remember the Python syntax :). It’s
interesting that Python added closures after several years of development,
instead of including it from the beginning. In some releases you had to
“from future import nested_scopes” to enable it.

, Tobias

···

On Thu, 26 Feb 2004, Florian Gross wrote: