From: pjb@... (Pascal J. Bourguignon) Date: 2009-01-11T02:24:31+09:00 Subject: Re: functional programming Brian Candler writes: >> It has already been shown. You need to make a sincere effort to >> understand the post to which you replied. If you did not understand >> Pascal's post, then ask a question. > > You mean this bit? > > (def function(designator,arity=(-1)) > .. > (args = (((1 .. arity) . map { | i | ("a" + i.to_s) }) . join(" > , "))) > (eval("(Proc . new { | " + args + " | " + > "(" + (designator . to_s) + " " + args + " )})")) > > ?? > > Well, if I call > > function(:smallest, 1) > > it returns > > eval "(Proc . new { | a1 | (smallest a1 )})" > > I've never had to use any eval construct like this in real Ruby > programs. That's because Matzacred lisp lacks macros, because it's not an homoiconic language. > The nearest I can think of is obj.method(:smallest) which > gives a bound method object I can pass around and call later. Why should it be bound? Why if we wanted to map over recipient objects? Why make a difference between the reciever of a message and the other arguments of a method? > Is it trying to defeat auto-splat? This is certainly an area where Ruby > appears to be broken. In part yes. > irb(main):001:0> def f(x); puts "*** #{x.inspect} ***"; end > => nil > irb(main):002:0> ff = method(:f) > => # > irb(main):003:0> ff[1] > *** 1 *** > => nil > irb(main):004:0> [1,2,3].map(&ff) > *** 1 *** > *** 2 *** > *** 3 *** > => [nil, nil, nil] > irb(main):005:0> [[1],[2],[3]].map(&ff) > *** [1] *** > *** [2] *** > *** [3] *** > => [nil, nil, nil] > irb(main):006:0> [[1],[2],[3,4]].map(&ff) > *** [1] *** > *** [2] *** > ArgumentError: wrong number of arguments (2 for 1) > from (irb):1:in `f' > from (irb):1:in `to_proc' > from (irb):6:in `map' > from (irb):6 > from :0 > > However, this problem doesn't occur if you use lambda rather than > method(:...) > > irb(main):007:0> ff = lambda { |x| puts "*** #{x.inspect} ***" } > => # > irb(main):008:0> [[1],[2],[3,4]].map(&ff) > *** [1] *** > *** [2] *** > *** [3, 4] *** > => [nil, nil, nil] Are you saying that you must use lambda everywhere, even when you have a named function? That named functions are less than anonymous functions? Indeed, that's why I introduced (function :name,arity) and (method :name,arity), to put them to the same level as lambda (Proc instances). > so it seems to me that everything Pascal wrote could be written directly > in corresponding Ruby. For example: > > # (begin > # (printlist (mapcar (lambda {|x| (x + 1)}),(list 1,2,3))) > # (terpri) > # end) > > p [1,2,3].map { |x| x + 1} > > # (def smallestElement(list,minimum) > # (if (endp list) > # minimum > # elsif (minimum < (first list)) > # (smallestElement (rest list),minimum) > # else > # (smallestElement (rest list),(first list)) > # end) > # end) > # > # (def smallest(list) > # (smallestElement (rest list),(first list)) > # end) > > smallestElement = lambda { |list,minimum| > if list.empty? > minimum > elsif minimum < list.first > smallestElement[list[1..-1],minimum] > else > smallestElement[list[1..-1],list.first] > end > } > > smallest = lambda { |list| > smallestElement[list[1..-1], list.first] > } There's a difference between (def smallest(x) ; x ; end) and (smallest = (lambda { |x| x })) In the former case, you can write (smallest [1,2,3]) in the later you can't: irb(main):001:0> (def smallest(x) ; x ; end) (def smallest(x) ; x ; end) nil 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 })) # 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] There's also this brokenness that once you assign a variable, you can't call the function of same name anymore: irb(main):005:0> (def smallest(x) ; x ; end) (def smallest(x) ; x ; end) nil irb(main):006:0> (smallest [1,2,3]) (smallest [1,2,3]) (irb):3: warning: multiple values for a block parameter (3 for 1) from (irb):6 [1, 2, 3] irb(main):007:0> (smallest = nil) (smallest = nil) nil irb(main):008:0> (smallest [1,2,3]) (smallest [1,2,3]) NoMethodError: undefined method `[]' for nil:NilClass from (irb):8 irb(main):009:0> > # (begin > # (terpri) > # (printlist (mapcar (function :smallest,1),(list (list 1), > # (list 1,1,1,1), > # (list 1,2,3,4), > # (list 4,3,2,1), > # (list > 1,2,3,4,3,2,1), > # (list > 4,3,2,1,2,3,4)))) > # (terpri) > # end) > > p [ > [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(&smallest) > > I'm afraid I don't really have the patience to convert all of the rest. > If you are only going to talk in hints and enigmas, perhaps you could > hint at which point of all this Ruby breaks down? As you show it, it is perfectly possible to do it this way in ruby. But it's more complex. You have to know now what this & syntax does. You have to wonder why you cannot write: (def biggest(x) ... end) [ [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(&biggest) ArgumentError: wrong number of arguments (0 for 1) from (irb):17:in `biggest' from (irb):17 Nor: [ [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(biggest) ArgumentError: wrong number of arguments (0 for 1) from (irb):26:in `biggest' from (irb):26 irb(main):027:0> So I concede that you can write what I wrote in Ruby (I wrote it in Ruby!), but the way I wrote it is more homogenous, and therefore easier to write, even if it means greenspunning some correct Lisp over the ruins of Ruby. -- __Pascal Bourguignon__