"William James" <w_a_x_man / yahoo.com> writes:

> 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:

No, you're wrong.  All the above code IS 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.

Wrong, the above code wasn't Lisp, it was Matzacred Lisp, aka Ruby.

Ruby is to a vacuum tube what debris of glass and tungsten filaments
are.

-- 
__Pascal Bourguignon__