Functional programming

Pascal J. Bourguignon wrote:

Ruby doesn’t work like that.

irb(main):033:0> plus4 = lambda { |x| x + 4}
plus4 = lambda { |x| x + 4}
#Proc:[email protected]:33(irb)
irb(main):034:0> (plus4 2)
(plus4 2)
NoMethodError: undefined method `plus4’ for main:Object
from (irb):34
irb(main):035:0>

plus4.call(2), or more briefly, plus4[2]

Yes, a variable holding a lambda is a different beast to a method. As
you said, methods are associated with objects, and lambdas are not.
Usually one suits the usage case better than the other.

I find the distinct behaviours of both to be very useful in practice.

Brian C. wrote:

(2) to understand Mike’s assertion
that Ruby lambdas are not first-class functions (i.e. “The difference
between lambda { } and a real first-class function is quite profound”).

Ruby:

mean = lambda { |*args|
(args.inject(0) { |acc, x| acc + x })/args.size.to_f
}
data = [[1,2,3], [4,5,6,7]]

data.map(&mean) # fail
data.map(&:mean) # fail
data.map { |*numbers| mean.call(*numbers) } # fail
data.map { |numbers| mean.call(*numbers) } # phew, found it

Haskell:

map mean data

On this example alone, I think my characterization of “quite profound”
is justified.

Brian C. wrote:

Yes, a variable holding a lambda is a different beast to a method. As
you said, methods are associated with objects, and lambdas are not.
Usually one suits the usage case better than the other.

I find the distinct behaviours of both to be very useful in practice.

Can you give an example of where the difference is “very useful”? In
every case I’ve encountered, it has been a hassle.

Because programmers can and should always be refactoring and changing
their mind, this entails the drudgework of adding/removing the
'.call()'s or the '[]'s or (in ruby 1.9) the '.()'s. The worst
outcome is when a programmer doesn’t bother to refactor because of the
hassle.

By the way, since locals are determined at parse time, I don’t see any
particular reason why this cannot work:

bisect = lambda { |x, y| (x + y)/2.0 }

bisect(5, 6)

Whether or not that should work is a different story. The pro is
that I can swap lambdas and methods without changing syntax. The con
is, at least, that the meaning of existing code could change (when a
bisect method already exists).

Mike G. wrote:

By the way, since locals are determined at parse time, I don’t see any
particular reason why this cannot work:

bisect = lambda { |x, y| (x + y)/2.0 }

bisect(5, 6)

Whether or not that should work is a different story. The pro is
that I can swap lambdas and methods without changing syntax. The con
is, at least, that the meaning of existing code could change (when a
bisect method already exists).

I see this is a bad idea due to the cases where the lambda is not a
local variable.

[lambda { |x, y| (x + y)/2.0 }].first(5, 6)

Since the rule couldn’t apply here, the special-case exception for a
local lambda would be silly.

Brian C. wrote:

Example: convert all hash keys to strings

src = {:one=>1, :two=>2}

Imperative

out = src.inject({}) { |h,(k,v)| h[k.to_s] = v; h }

Functional

out = src.inject({}) { |h,(k,v)| h.merge(k.to_s => v) }

src = {:one,1, :two,2}
==>{:one=>1, :two=>2}
Hash[ *src.map{|k,v| [k.to_s,v]}.flatten ]
==>{“two”=>2, “one”=>1}

Mike G. wrote:

data = [[1,2,3], [4,5,6,7]]
On this example alone, I think my characterization of “quite profound”
is justified.

Why isn’t this the ruby candidate for comparison:

mean = lambda { |ary|
(ary.inject { |acc, x| acc + x })/ary.size.to_f
}
data.map(&mean) # => [2.0, 5.5]

Pascal J. Bourguignon wrote:

irb(main):042:0> (car (cons 1,2))

    (i = (i - 1))

     (print " ")

(def printlist(cons)

  nil

as reverse:
(revappend list,nil)

(def method(designator,arity=(-1))
i | (“a” + i.to_s) }) . join(" , “)))) (eval(”(Proc . new { |
not the recipient class: # (function “SOME_MODULE.someFunction”,1)
(sep = " , “)
“(” + (designator . to_s) + " )})”))
(fun . call(*args))
(printlist (funcall (function :reverse,1),(list
1,2,3))) (terpri)
end)

(3 2 1)
nil

You have certainly proved that everything is 10 times as hard
as it should be when you use Commune Lisp!

Ruby:

[1,2,3].reverse
==>[3, 2, 1]

[1,2,3].send :reverse
==>[3, 2, 1]

(cons (funcall fun,(first list)),(mapcar fun,(rest list)))
nil

Ruby:

[1,2,3].map{|x| x + 1}
==>[2, 3, 4]

or:

augment = proc{|x| x + 1}
==>#Proc:[email protected]:11(irb)
[1,2,3].map &augment
==>[2, 3, 4]

 minimum

1,2,3,4,3,2,1),
(list 4,3,2,1,2,3,4)))) (terpri)
end)

(1 1 1 1 1 1)
nil

Ruby:

[[1],[1,1,1,1],[1,2,3,4],[4,3,2,1],[1,2,3,4,3,2,1],[4,3,2,1,2,3,4]].
map{|a| a.min}
==>[1, 1, 1, 1, 1, 1]

Yes, COBOL Lisp is an ancient, cumbersome, and clunky language.
Ruby is to it as a transistor is to a vacuum tube.

Joel VanderWerf wrote:

(args.inject(0) { |acc, x| acc + x })/args.size.to_f

map mean data

On this example alone, I think my characterization of “quite
profound” is justified.

Why isn’t this the ruby candidate for comparison:

mean = lambda { |ary|
(ary.inject { |acc, x| acc + x })/ary.size.to_f
}
data.map(&mean) # => [2.0, 5.5]

mean = proc{|a| a.inject{|s,x| s+x} / a.size.to_f }
==>#Proc:[email protected]:19(irb)
data.map &mean
==>[2.0, 5.5]

Pascal J. Bourguignon wrote:

irb(main):002:0> (smallest [1,2,3])
(smallest [1,2,3])
[1, 2, 3]
irb(main):003:0> (smallest = (lambda { |x| x }))
(smallest = (lambda { |x| x }))
#Proc:[email protected]:3(irb)
irb(main):004:0> (smallest [1,2,3])
(smallest [1,2,3])
(irb):3: warning: multiple values for a block parameter (3 for 1)
from (irb):4
[1, 2, 3]

foo = proc{|x| “#{x.size} items: #{ x.join ’ & ’ }” }
==>#Proc:[email protected]:15(irb)
foo.call( [2,3,4] )
==>“3 items: 2 & 3 & 4”
foo[ [2,3,4] ]
==>“3 items: 2 & 3 & 4”

Joel VanderWerf wrote:

Mike G. wrote:

data = [[1,2,3], [4,5,6,7]]
On this example alone, I think my characterization of “quite profound”
is justified.

Why isn’t this the ruby candidate for comparison:

mean = lambda { |ary|
(ary.inject { |acc, x| acc + x })/ary.size.to_f
}
data.map(&mean) # => [2.0, 5.5]

Sure, you can solve a different problem than the one I gave.
But the original problem remains. We cannot, in general, modify
‘mean’.

Do you propose to change every method in the ruby standard library
to take just one argument–an array? That would work. Sounds like
Lisp.

Haris B. wrote:

news:[email protected]… >“Haris B.”

One difficulty is that vectors are difficult to handle with purely
For an exemple here is a simple merge sort. We need a function to
(cons (first list2),(mergelist list1,(rest list2),lessp))

values
list),less),more) end)

nil
(less,more = (splitlist (rest list),pivot,lessp))
}),(list (list),(list 1),(list
5,4,3,2,1),(list 1,2,3,4,5),(list 1,3,5,4,2),(list 5,3,1,2,4))))
(terpri) end)

(nil (1) (1 2 3 4 5) (1 2 3 4 5) (1 2 3 4 5) (1 2 3 4 5))
nil

Ruby:

def merge list1, list2, &less
return list1 + list2 if list1.empty? or list2.empty?
less = ( less or proc{|a,b| a < b} )
less[ list1[0], list2[0] ] and
[ list1[0] ] + merge( list1[1…-1], list2, &less ) or
[ list2[0] ] + merge( list1, list2[1…-1], &less )
end

p merge( [1,3,5], [2,4,6] )
p merge( [-1,-3,-5], [-2,-4,-6] ){|a,b| a > b}

def merge_sort list
return list if list.size < 2
half = list.size / 2
merge merge_sort( list[0…half] ), merge_sort( list[half…-1] )
end

p merge_sort( [9,2,8,5,6,3] )
5.times{|i| p merge_sort( (0…i).to_a.reverse ) }

Mike G. wrote:

It’s a clever way to get
people accustomed to parens without actually using Lisp. A way-point
on the road to enlightenment.

There you have it. Hierophants of Commune Lisp are utter irrational.
It’s a religion to them. In comp.lang.lisp you will find threads
named “How did you discover Lisp?”, i.e., “How did you get religion?”

On Fri, Jan 9, 2009 at 1:39 PM, andrea [email protected] wrote:

The interpreter is going to have many new objects created around, and
so there will be more work for the garbage collector or more memory
used, am I wrong?
Yes or let us say you will be wrong very soon :).

Have a look at this to understand why:
http://rubyconf2008.confreaks.com/how-ruby-can-be-fast.html

HTH
Robert

Brian C. wrote:

manages to achieve ~50% of the speed of hand-written C. Given that C
can be considered portable machine code, this is a pretty impressive
achievement.

Don’t take Ed seriously. You will find that he never posts any
Ruby code that he has written.

And he has already been taught that OCaml can be as fast as C:

http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/e2138
1fb5b6a47f6

William J. wrote:

utter irrational.

utterly

William J. wrote:

manages to achieve ~50% of the speed of hand-written C. Given that C
can be considered portable machine code, this is a pretty impressive
achievement.

Don’t take Ed seriously. You will find that he never posts any
Ruby code that he has written.

Not to the mailing list … but to RubyForge :slight_smile:

And he has already been taught that OCaml can be as fast as C:

Of course any language can be as fast as any other language. But will
it be predictably so for a wide class of benchmarks or applications? I
rather doubt it.


M. Edward (Ed) Borasky, FBG, AB, PTA, PGS, MS, MNLP, NST, ACMC§, WOM

I’ve never met a happy clam. In fact, most of them were pretty steamed.

Hi –

On Mon, 12 Jan 2009, William J. wrote:

Haskell:
}
data.map(&mean) # => [2.0, 5.5]

mean = proc{|a| a.inject{|s,x| s+x} / a.size.to_f }
==>#Proc:[email protected]:19(irb)
data.map &mean
==>[2.0, 5.5]

I believe that’s the same as what Joel just posted (give or take some
cosmetics). Or is my eye skipping over something?

David


David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!

William J. wrote:

Mike G. wrote:

It’s a clever way to get
people accustomed to parens without actually using Lisp. A way-point
on the road to enlightenment.

There you have it. Hierophants of Commune Lisp are utter irrational.
It’s a religion to them. In comp.lang.lisp you will find threads
named “How did you discover Lisp?”, i.e., “How did you get religion?”

Ironically, a distinguishing characteristic of zealotry is the
inability to recognize waggishness or sarcasm.

There are several clues you could have picked up.

(1) If you are at all familiar with Star Wars, you could have noticed
that I paraphrased Obi-Wan Kenobi in the same post.

(2) Even if you’ve never heard of Star Wars, the hyperbolic nature of
my post should have been obvious.

(3) If you cared to check whether your assumptions had any merit, you
could have noticed the 40 non-trolling posts I’ve made to
comp.lang.ruby. No posts to comp.lang.lisp.

(4) I contributed a small patch to Ruby; I appear in the ChangeLog.

On the other hand, your posting history reveals that you persistently
troll comp.lang.lisp with Ruby evangelism, much to the annoyance of
its inhabitants.

“If you hate a person, you hate something in him that is part of
yourself. What isn’t part of ourselves doesn’t disturb us.”
–Herman Hesse

Mike G. wrote:

data.map(&mean) # => [2.0, 5.5]

Sure, you can solve a different problem than the one I gave.
But the original problem remains. We cannot, in general, modify
‘mean’.

Do you propose to change every method in the ruby standard library
to take just one argument–an array? That would work. Sounds like
Lisp.

Sorry, I may not have followed the discussion very well.

But it seems more natural in this specific case to define mean to take
one array argument, rather than *args, since (or so I imagine), that’s
usually how input will arrive. How often do you write code like

x = …
y = …
z = …
m = mean.call( x,y,z )

as opposed to

a = …
m = mean.call( a )

?

Perhaps I’m distracted by the specifics of mean and missing the general
point…

Joel VanderWerf wrote:

Perhaps I’m distracted by the specifics of mean and missing the general
point…

‘mean’ stands for anything. I should have called it ‘some_func’. You
can’t just change its meaning. In particular, you can’t change the
methods in the ruby standard library. (Well, you can, but you’ll be
in danger of receiving a painful retributive wedgie from those who use
your code. Perhaps even an atomic one.)

You can always write wrappers which convert a given method or lambda
to a one-argument lambda expecting an array. But this is just another
kind of Lisp emulation in Ruby, taking non-homogeneous primitives and
making them homogeneous.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs