Programming Paradigms

Types of programming paradigms:

  1.  Imperative programming:  describes computation in terms of control flow / statements that change the state of the memory of the computer. Imperative languages can relatively easily be translated into efficient machine-code, and so are usually considered to be highly efficient
    1.  Procedural programming: Program is build by one or more procedures (functions or subroutines). e.g. C. Important concepts in this context are modularity, scoping, variables (and their execution environment), data structures, subroutines. This paradigm may give rise to side effects. For example if we have a procedure increment_one(y) containing assignment statement x : = x + 1, where x is a global variable. Here calling this procedure twice will have an effect different from calling it  once, hence the side effect of calling a procedure. A function or expression is said to have a side effect if, in addition to returning a value, it also modifies some state or has an observable interaction with calling functions or the outside world. For example, a function might modify a global or static variable, modify one of its arguments, raise an exception, write data to a display or file, read data, or call other side-effecting functions. In the presence of side effects, a program's behavior depends on history; that is, the order of evaluation matters. Understanding a program with side effects requires knowledge about the context and its possible histories; and therefore can be hard to read, understand and debug
      1. Structured Programming: It's heavily procedural, in which state changes are localized to procedures or restricted to explicit arguments and returns from procedures.
  2. Declarative Programming: Non-Imperative programming, where programs describe the desired results of the program (what to do), without explicitly listing control flow of statements (how to do) to achieve the results
    1. Functional Programming:  Computation is expressed as the evaluation of mathematical functions. These are different from imperative language functions, which they lack referential transparency, i.e. the same language expression can result in different values at different times depending on the state of the executing program. Conversely, in functional code, the output value of a function depends only on the arguments that are input to the function, so calling a function f twice with the same value for an argument x will produce the same result f(x) both times. Eliminating side effects can make it much easier to understand and predict the behavior of a program.All functions in functional language are without side effects and do not perform the state change. For example Haskell is pure functional language, Lisp is procedural-functional mix. As an illustration, in a pure functional language the mathematical identity like hello(x) + hello(x) = 2 * hello(x) should hold, which is only possible if the functions are without side effect. Functional languages such as Standard ML and Scheme do not restrict side effects, but it is customary for programmers to avoid them. The functional paradigm is hard to implement efficiently because if a storage location is once used to hold a value it is not obvious when it can be re-used - a computer running a program using the functional paradigm can spend a lot of effort determining the re-usability of store. Languages that prohibit side effects are called pure. Even though some functional languages are impure they often contain a pure subset that is also useful as a programming language. The three features of functional languages are
      1. Immutable data: Purely functional programs typically operate on immutable data. Instead of altering existing values, altered copies are created and the original is preserved. Since the unchanged parts of the structure cannot be modified, they can often be shared between the old and new copies, which saves memory. 
      2. Referential transparency: Pure computations yield the same value each time they are invoked. This property is called referential transparency and makes possible to conduct equational reasoning on the code. For instance if y = f x and g = h y y then we should be able to replace the definition of g with g = h (f x) (f x) and get the same result; only the efficiency might change. 
      3. Lazy evaluation: Since pure computations are referentially transparent they can be performed at any time and still yield the same result. This makes it possible to defer the computation of values until they are needed, that is, to compute them lazily. Lazy evaluation avoids unnecessary computations and allows, for example, infinite data structures to be defined and used. The lambda calculus provides the model for functional programming. A data type is called first-class when you can pass instances of the data type as function arguments or return them as function values. In LISP function itself could be treated as first-class objects.
        defun double (x)
        (* 2 x))
        (double (double (double 2)))
        16
        (defun repeat-n-times (function n arg)
        (if (zerop n)
        arg
        (repeat-n-times function (1- n) (funcall function arg))))
        (repeat-n-times (function double) 3 2)
        16   
        Instead of typing (function double) we may use #' double 
        (repeat-n-times #'double 3 2)  
        Lisp provides a mechanism to defining functions “in place” (in the form of lambda. It is useful because we don't need to define function as a global function before passing it into repeat-n-times. This is based on lambda calculus. 
        (repeat-n-times #'(lambda (x)(* x x)) 3 2)
        256
         
    2.  Logic Programming:  Computation in exclusively in terms of mathematical logic, where Programs consist of logical statements and execute by searching for proofs of the statements. e.g. Prolog. While the functional paradigm emphasizes the idea of a mathematical function, the logic paradigm focuses on predicate logic, in which the basic concept is a relation. Logic languages are useful for expressing problems where it is not obvious what the functions should be. Unification in prolog: unification variables (starting with capital letter)  are used, which will be unified to match a relation given in the program. Using the Prolog, following is an example of DFA parser for the state diagram
      parse(L) :- start(S), 
      trans(S,L).

      trans(X,[A|B]) :-
      delta(X,A,Y), /* X ---A---> Y */
      write(X),
      write(' '),
      write([A|B]),
      nl,
      trans(Y,B).
      trans(X,[]) :-
      final(X),
      write(X),
      write(' '),
      write([]), nl.
      delta(0,a,1).   
      delta(0,b,0).
      delta(1,a,1).
      delta(1,b,2).
      delta(2,a,2).
      delta(2,b,2).

      start(0).

      final(2) 
      ?- parse([b,b,a,a,b,a,b]).
      0 [b,b,a,a,b,a,b]
      0 [b,a,a,b,a,b]
      0 [a,a,b,a,b]
      1 [a,b,a,b]
      1 [b,a,b]
      2 [a,b]
      2 [b]
      2 []
      yes

      ?- parse([b,b,a]).
      0 [b,b,a]
      0 [b,a]
      0 [a]
      no
  3. Object Oriented Programming: We associate behavior with data structures called objects consisting of data and methods in terms of interactions of objects.Important concepts in this context are data abstraction, encapsulation, messaging, inheritance and polymorphism. It is however possible to use certain non object-oriented languages to write object-oriented programs. What is required is the ability to create data-structures that contain machine code, or pointers to machine code. This is possible in the C language and in most functional languages (where functions are represented as as code). An example of polymorphism in C++ is given as

    #include <iostream.h>

    class Shape {
            public:
                    void setsize(int owbig);
                    virtual float getarea() {};
            protected:
                    int mysize;
            };

    class Square: public Shape {
            public:
                    float getarea();
            };

    class Circle: public Shape {
            public:
                    float getarea();
            };

    void Shape::setsize(int sv) {
            mysize = sv;
            }

    float Square::getarea() {
            float area= mysize * mysize ;
            return (area);
            }

    float Circle::getarea() {
            float area= 3.1415 * mysize ;
            return area;
            }

    main () {

            int n = 10;
            Shape *shapes[n];

            for (int k=0; k<n; k+=2) {
                    shapes[k] = new Square();
                    shapes[k+1] = new Circle();
            }
            for (int k=0; k<n; k++) {
                    shapes[k]->setsize(10+k);
                    }
            for (int k=0; k<n; k++) {
                    float area = shapes[k]->getarea();
                    cout << "Area is " << area<< endl;
                    }
            return (0);
            }
  4. Automata based programming: program is like a model of a formal automaton consisting of potentially infinite set of possible state. The time period of the program's execution is clearly separated down to the steps of the automaton, which has a single entry point. Any communication between the steps is only possible via the explicitly noted set of variables named the state i.e. the state of the whole program, taken at any two moments, can only differ in the values of the variables being considered as the state of the automaton.
  5. Non-deterministic programming: which can specify, at certain points in the program, various alternatives for program flow ahead, the choice of which is not hard coded but the program must decide at run time between the alternatives, via some general method applied to all choice points. A hierarchy of choice points may be formed, with higher-level choices leading to branches that contain lower-level choices within them.

No comments:

Post a comment

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Blogger Templates