开发者

Can you represent the same example using Procedural, Functional, Logic and OO programming Languages?

开发者 https://www.devze.com 2023-04-09 09:19 出处:网络
Can anyone please provide me with an example that can help me to understand Procedural, functional, Logic and Object Oriented programming models side by side by using nearly same example-problem.

Can anyone please provide me with an example that can help me to understand Procedural, functional, Logic and Object Oriented programming models side by side by using nearly same example-problem.

开发者_StackOverflowPlease give me example code-snippets of the somewhat same problem using Procedural, Functional, Logic and OO programming languages.


Let's try simpler example - just calculating n-th Fibonacci number.

First, procedural (in Pascal):

program Fibonacci;

function fib(n: Integer): Integer;
var a: Integer = 1;
    b: Integer = 1;
    f: Integer;
    i: Integer;
begin
  if (n = 1) or (n = 2) then
     fib := 1
  else
    begin
      for i := 3 to n do
      begin
         f := a + b;
         b := a;
         a := f;
      end;
      fib := f;
    end;
end;

begin
  WriteLn(fib(6));
end.

This example shows features of procedural languages:

  • There are some subroutines (function in this case)
  • Variables are assigned value probably multiple times ( := operator)
  • There are cycles (for operator in this case)
  • Language is imperative, i.e. we are telling computer what to do in what order

Second, object oriented (in Python):

class Fibonacci:
   def __init__(self):
       self.cache = {}
   def fib(self, n):
       if self.cache.has_key(n):
           return self.cache[n]
       if n == 1 or n == 2:
            return 1
       else:
           a = 1
           b = 1
           for i in range(2, n):
               f = a + b;
               b = a;
               a = f;
           self.cache[n] = f;
           return f;


fibonaccyCounter = Fibonacci()
print fibonaccyCounter.fib(6)

Actually the problem is not worth creating a class, so I added caching of already calculated results.

This example shows:

  • class and its instantiation (creating instance)
  • class has own section of memory, own state (self and its members)
  • Language is imperative, i.e. we are telling computer what to do in what order

Not shown but we can e.g. descend this class from abstract class returning n-th member of some sequence. By subslassing we get class defining Fibonacci sequence, sequence 1,2,3..., sequence 1,4,9,16,... etc.

Third, in functional style (Haskell):

import Text.Printf

fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

main = printf "%d\n" (fib 6)

Following features of a functional programming paradigm are demonstrated:

  • there is no state, no variables - just functions defined
  • there are no cycles - only recursion
  • pattern matching: we separately defined "fib 0", "fib 1" and "fib n" for rest of numbers, no constructs like if were needed
  • declarative style - we don't define the order of steps to calculate main function value: the compiler/interpreter/runtime figures this out by itself, given the function definitions. We tell the computer what we want to get, not what to do.
  • Lazy evaluation. If main was calling only "fib 2" then "fib n" was not called because functions are evaluated only when their result is needed to be passed as parameter to other functions.

But the main feature of functional languages is that functions are first class objects. This can be demonstrated by other implementation of fib:

fib n = fibs!!n
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

Here we are passing fibs function as parameter to zipWith function. This example also demonstrates lazy evaluation: "infinite" list is computed only to extent it is needed for other functions.

By the way, functional does not necessary mean not object oriented. An example of programming language that is both functional and object oriented is Scala.

Prolog:

fib(1, 1).
fib(2, 1).


fib(X, Y):-
        X > 1,
        X1 is X - 1,
        X2 is X - 2,
        fib(X1, Z),
        fib(X2, W),
        Y is W + Z.


main :-
   fib(6,X), write(X), nl.

Following features of logic programming style can be seen:

  • Language is declarative. As in functional style, we define things and not telling in what order to do them.
  • But the difference with functional style is that we define predicates, not functions. In this case, predicate fib(X, Y) means "X-th Fibonacci number is Y". Given some known predicates (fib(1, 1) and fib(2, 1) - i.e. first Fibonacci number is 1 and second Fibonacci number is 1) and rules to infer other predicates (Y is X-th Fibonacci number is Y is a sum of X-1th Fibonacci number and X-2th Fibonacci number), Prolog infers predicates in question. Actually there could be more than 1 answer!
  • There is no input values and return value - instead of this we define a relation between "input" and "output".

This program could also be used to find out that Fibonacci number 8 is at 6th position in the sequence:

?- between(0,inf,X), fib(X,8).
X = 6 .


http://99-bottles-of-beer.net/

(It features my own horribly contrived 99 language.)


Project Euler Problem number 2: http://projecteuler.net/problem=2

Haskell (functional/logic):

p2 = sum [x | x <- fibs, (x `mod` 2) == 0] where
    fibs = unfoldr acc (0,1) where
            acc (prev, cur) | (prev+cur) > 4000000 = Nothing
                            | otherwise            = Just (prev+cur, (cur, prev+cur))

Python (OO):

class FibSum(object):
    def __init__(self, end):
        self.end = end
        self.next_two = (1,0)
        self.sum = 0

    def __iter__(self):
        return self

    def next(self):
        current, previous = self.next_two
        self.next_two = (previous+current, current)
        new = current+previous

        if current >= self.end:
            raise StopIteration
        elif (new % 2) == 0:
            self.sum += new
        else:
            pass


fibcount = FibSum(4000000)
[i for i in fibcount]
print fibcount.sum

C (procedural/imperative):

#include <stdio.h>

int main(void) 
{
    long int sum, newnum, previous = 0;
    long int current = 1;

    while(current <= 4000000) 
    {
        newnum = previous+current;
        if ((newnum % 2) == 0) 
        {
            sum = sum + newnum;
        }
        previous = current;
        current = newnum;

    }
    printf("%d\n", sum);
}

And here is a very inefficient version written in MIT Scheme

(define (unfold func seed)
    (let* ((result (func seed)))
    (cond ((null? result) ())
    (else (cons (car result) (unfold func (second result)))))))

(define (test x)
    (cond ((> (sum x) 4000000) ())
    (else (list (sum x) (list (second x) (sum x))))))

(define (sum xs)
    (cond ((null? (cdr xs)) (first xs))
    (else (+ (car xs) (sum (cdr xs))))))

(sum (filter (lambda (x) (eq? (modulo x 2) 0)) (unfold test (list 0 1))))

Prolog: Take from this here, posted by 13tazer31

fibonacci(_,Current,End,0) :-
        Current > End.
fibonacci(Previous, Current, End, Total) :-
        divisible(Current, 2),
        Next is Current + Previous,
        fibonacci(Current, Next, End, Sum),
        Total is Sum + Current, !.
fibonacci(Previous, Current, End, Total) :-
        Next is Current + Previous,
        fibonacci(Current, Next, End, Total).

divisible(Number, 0) :-
        write(‘Error: division by 0′).         
divisible(Number, Divisor) :-
        Number mod Divisor =:= 0.


Well its very simple

  1. Procedural, Functional logic lets say you want to compute something (that's what computers do)
    1. break the problem into functions say f1,f2,f3.... now if you look at the problem , you see it divided into functions .. but there has to be a starting point (from where you start the computation) and ending point (where you end the computation) and between them are the functions (f1,f2,f3). Thus what you have really done is instead of writing one big function (F) that does every thing and is very long you have break it into smaller parts (this is known as refactoring). Understanding a single function that is 150 line long is boring.. when you get to the end of line you will forget where you started thus we break things apart. now how we do the computation --- > We create a single function say compute() (this is known as facade ) that will call the remaining functions (f1,f2,f3...) in desired ordered and will return the result. Now understand this: If we had written a single function that would have been around 150 lines (complexity increases).. assume we broke down it into 3 functions and say each function is of 50 lines (manageable) .. how did we reduce complexity since the sum of lines of 3 functions is still 150 :D . the complexity is reduced by function name .. which clearly states what the function does.. it means looking at the name you can have a idea what the function does.

OO Programming logic:
Now functions are scattered in functional logic .. when we bring all the related functions (behavior) inside a single umbrella (Class) we have further reduced the complexity .. how .. by "Class name". Now you can say that instead of calling f1,f2,f3.. we call c1.f1(),c2.f2,c3.f3() where "c" denotes a class (domain driven design).

imp .. no matter whether you use oops or functional logic there is always a starting and ending point of computation ... remember the compute() i talked about.. and the question is who call's it .. and the answer is you .. Everything OOP's logic or procedural logic is hidden behind it (Service Facade)

0

精彩评论

暂无评论...
验证码 换一张
取 消