Pascal J. Bourguignon wrote: > So to make a list, first, the basic building block, the cons cell: > > (class Cons > (attr_accessor :car) > (attr_accessor :cdr) > (def initialize(car,cdr) > (@car = car) > (@cdr = cdr) > end) > end) > > > Let's wrap this object into a functional abstraction layer: > > (def cons(car,cdr) > (Cons . new(car,cdr)) > end) > > (def car(x) > (x . car) > end) > > (def cdr(x) > (x . cdr) > end) > > (def null(x) > (x . nil?) > end) > > irb(main):040:0> (cons 1,2) > #<Cons:0x7fdfb53eb808 @cdr=2, @car=1> > irb(main):042:0> (car (cons 1,2)) > 1 > irb(main):043:0> (cdr (cons 1,2)) > 2 > irb(main):044:0> (null (cons 1,2)) > false > irb(main):045:0> (null (car (cons 1,nil))) > false > irb(main):046:0> (null (cdr (cons 1,nil))) > true > > > > Then you can build a list over these cons cells: > > (def list(*args) > (i = (args . length)) > (r = nil) > (loop { > (if (i == 0) > (break) > else > (i = (i - 1)) > (r = (cons (args [ i ]),r)) > end) > }) > r > end) > > irb(main):127:0> (list 1,2,3) > #<Cons:0x7fdfb53b81d8 @cdr=#<Cons:0x7fdfb53b8200 > @cdr=#<Cons:0x7fdfb53b8228 @cdr=nil, @car=3>, @car=2>, @car=1> > > > Let's print them pretty: > > (def printelements(cons) > (if (Cons === cons) > (if (Cons === (car cons)) > (printlist (car cons)) > else > (print (car cons)) > end) > (if (Cons === (cdr cons)) > (print " ") > (printelements (cdr cons)) > elsif (not (null (cdr cons))) > (print " . ") > (print (cdr cons)) > end) > else > (print cons) > end) > end) > > (def printlist(cons) > (print "(") > (printelements cons) > (print ")") > cons > end) > > (def terpri() > (print "\n") > end) > > > irb(main):263:0> (begin > (terpri) > (printlist (list 1,2,3)) > (terpri) > end) > > (1 2 3) > nil > > You can also add some higher level abstractions: > > (def first(list) > (car list) > end) > > (def rest(list) > (cdr list) > end) > > (def endp(list) > (if (Cons === list) > nil > elsif (null list) > true > else > (error ("Expected a list instead of the atom " + (list . to_s))) > end) > end) > > > > Once you've got this list abstraction, you can build functions such > as reverse: > > (def revappend(list,tail) > (if (null list) > tail > else > (revappend (cdr list),(cons (car list),tail)) > end) > end) > > (def reverse(list) > (revappend list,nil) > end) > > > > irb(main):267:0> (begin > (printlist (reverse (list 1,2,3))) > (terpri) > end) > (3 2 1) > nil > > > > Now we also need the function abstraction. In ruby functions have to > be in modules, and always qualified by the module name. This is not > pretty, so we will allow any method to be treated as a function, but > we will ignore it's recipient object, always passing self. > > # For methods, the first function argument is the recipient of the > # message: > > (def method(designator,arity=(-1)) > # Important: the given arity must include the recipient object, > but not the recipient class: # (method :==,2) .call(42,42) vs. > 42.==(42) (if (arity == -1) > # This is the default, bugged behavior: > (Proc . new {| x , *args | (x . send(designator , *args))}) > elsif (arity == 0) > (raise (Exception.exception("An instance method must have an > arity>=1, not 0."))) else > (arity = (arity - 1)) > (args = (((arity == 0)? "" : " , ") + (((1 .. arity) . map { | > i | ("a" + i.to_s) }) . join(" , ")))) (eval("(Proc . new { | > x" + args + " | " + "( x . send( :" + (designator . to_s) > + args + " ))})")) end) > end) > > > # for functions, the recipient of the message is always 'self', all > # arguments are passed as arguments. > > (def function(designator,arity=(-1)) > # Important: the given arity must include the recipient object, but > not the recipient class: # (function "SOME_MODULE.someFunction",1) > .call(42) vs. SOME_MODULE.someFunction(42) # (function > :someFunction ,2) .call(42) vs. self.someFunction(42) > or someFunction(42) (if (String === designator) mod , met = > (designator . split(".")) (if (arity == -1) > # This is the default, bugged behavior: > (Proc . new {| *args | ((eval mod) . send((met . to_sym) , > *args))}) else > (args = (((1 .. arity) . map { | i | ("a" + i.to_s) }) . > join(" , "))) (sep = " ") > (if (0 < arity) > (sep = " , ") > end) > (eval("(Proc . new { | " + args + " | " + > "( " + mod + " . send( :" + met + sep + args + " > ))})")) end) > else > (if (arity == -1) > # This is the default, bugged behavior: > (Proc . new {| x , *args | (x . send(designator , *args))}) > elsif (arity == 0) > (eval("(Proc . new {" + > "(" + (designator . to_s) + " )})")) > else > (args = (((1 .. arity) . map { | i | ("a" + i.to_s) }) . > join(" , "))) (eval("(Proc . new { | " + args + " | " + > "(" + (designator . to_s) + " " + args + " )})")) > end) > end) > end) > > > (def funcall(fun,*args) > (fun . call(*args)) > end) > > > > irb(main):370:0> (funcall (method :+,2),3,4) > (funcall (method :+,2),3,4) > 7 > > irb(main):478:0> (begin > (terpri) > (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] > > > > Now that you have first class functions, you can start to implement > higher level functions: > > (def mapcar (fun,list) > (if (endp list) > nil > else > (cons (funcall fun,(first list)),(mapcar fun,(rest list))) > end) > end) > > > irb(main):271:0> (begin > (printlist (mapcar (lambda {|x| (x + 1)}),(list > 1,2,3))) (terpri) > end) > > (2 3 4) > nil > Ruby: [1,2,3].map{|x| x + 1} ==>[2, 3, 4] or: augment = proc{|x| x + 1} ==>#<Proc:0x0282c2cc@(irb):11> [1,2,3].map &augment ==>[2, 3, 4] > > > So at least, now you can find the smallest element of a list: > > This function implements an accumulator pattern: we pass the partial > result along with the parameters. We process the list item by item > and when its finished we return the result we've accumulated so far: > > (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) > > > > irb(main):422: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) > > (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.