Account Options

  1. Sign in
The old Google Groups will be going away soon, but your browser is incompatible with the new version.
Google Groups Home
« Groups Home
Message from discussion functional programming
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Pascal J. Bourguignon  
View profile  
 More options Jan 8 2009, 5:33 pm
Newsgroups: comp.lang.ruby
From: p...@informatimago.com (Pascal J. Bourguignon)
Date: Thu, 08 Jan 2009 23:33:37 +0100
Local: Thurs, Jan 8 2009 5:33 pm
Subject: Re: functional programming

"Haris Bogdanovic" <fbogdano...@xnet.hr> writes:
> I'm starting to learn functional programming with Ruby.
> I would like for someone to write me an example of finding the smallest
> number in the list and a sorting algorithm.
> I got the concept of map (collect), filter (select, detect and reject) and
> reduce (inject) higher order functions but I don't know how to compose
> algorithms from them.
> So please write me those two algorithms or some other simple ones with some
> explanation.

You would have first to define the concept of list.  Indeed, the other
answer may have you misled into believe that [1,2,3] was a list, but it
is not, it is an Array:

irb(main):477:0> [1,2,3].class
Array

(actually it's a vector, Ruby has no multidimentional arrays like
Fortran or Lisp, or any other serrious programming language).

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

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

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

Oh, and by the way, instead of using Matzacred Lisp aka Ruby, you could
as well use directly the original, Lisp, and without dilation type away:

C/USER[7]> (defun smallest (list)
             (labels ((smallest-element (list minimum)
                        (cond
                          ((endp list)              minimum)
                          ((< minimum (first list)) (smallest-element (rest list) minimum))
                          (t                        (smallest-element (rest list) (first list))))))
               (smallest-element (rest list) (first list))))
SMALLEST
C/USER[8]> (mapcar (function smallest) (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)))
(1 1 1 1 1 1)
C/USER[9]>

( Try out http://clisp.cons.org or http://sbcl.sourceforge.net
  Have a look at http://www.gigamonkey.com/book )

> Some links to this subject would be nice also.

http://www.stanford.edu/class/cs242/readings/backus.pdf

For purely functional programming you could learn Haskel, or a language
of the ML family, but Common Lisp is more useful since it's a
multi-paradigm programming language: when you reach the limits of a
given paradigm, such as OO, or functional, you always have yet another
programming paradigm available in lisp (logical, declarative, you name
it).

--
__Pascal Bourguignon__


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.