Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
pp:intro [2016/05/05 16:55]
vrares
pp:intro [2019/02/14 18:19] (current)
pdmatei
Line 1: Line 1:
-====== Introduction =====+===== Introduction ​to Programming Paradigms ​=====
-----+
  
-A Programming Paradigm ​is a notion that defines ​a style or way of programming ​and not the programming language itself. A programming language is said to use specific paradigm only when it (the programming ​languagesatisfies all the requirements stated by the paradigm.+==== In how many different ways can we reverse a sequence of integers? ==== 
 + 
 +We start the lecture by looking at three different Java programs which reverse a sequence of integers. 
 + 
 +The //​Cook-book//​ style: 
 +<code java> 
 +public class Rev { 
 + public static void main (String[] args) { 
 + Integer[] v = new Integer[] {1,​2,​3,​4,​5,​6,​7,​8,​9};​ 
 + int i=0; 
 + while (i < v.length/​2){ 
 + int t = v[i]; 
 + v[i] = v[v.length-1-i];​ 
 + v[v.length-1-i] = t; 
 + i++; 
 + }  
 + show(v);​ 
 +
 +
 +</​code>​ 
 +//​Features://​ 
 +  * the reversal ​is implemented over an array, and performed **in-place** by swapping the first half of the array with the second. 
 +  * the code is **compact** and **efficient** ($math[n/2] swaps over an array of size $math[n]). 
 +  * the solution modifies the sequence to reversed one. 
 + 
 +//Possible usages:// 
 +  * when development speed is critical 
 +  * when minimising the number of computation steps is critical 
 + 
 +The //​Undergrad-math-teacher-style//​ style (short //​Undergrad//​ style): 
 +<code java> 
 +interface List { 
 + public Integer head (); 
 + public List tail (); 
 +
 + 
 +class Cons implements List { 
 + Integer val; 
 + List next; 
 + public Cons (Integer val, List next){ 
 + this.val = val; 
 + this.next = next; 
 +
 + @Override 
 + public Integer head() {return val;} 
 + @Override 
 + public List tail() {return next;} 
 +
 + 
 +class Empty implements List { 
 + @Override 
 + public Integer head() {return -1;} 
 + @Override 
 + public List tail() {return null;} 
 +
 + 
 +public class V2 { 
 + 
 + private static List rev (List x, List y){ 
 + if (x instanceof Empty) 
 + return y; 
 + return rev(x.tail(),​ new Cons(x.head(),​y));​ 
 +
 + 
 + public static List reverse (List l){ 
 + return rev(l,new Empty()); 
 +
 + 
 + public static void main (String[] args){ 
 + List v = new Cons(1, new Cons(2, new Cons(3, new Empty())));​ 
 + List r = reverse(v);​ 
 +
 +  
 +
 +</​code>​ 
 +//​Features://​ 
 +  * This solution first attempts to **separate** the sequence representation from the **reversal algorithm**.  
 +  * The sequence is **represented** as **list** in the ADT-style
 +  * The in the reversal, the internal list representation is abstracted by using constructors (''​Empty''​ and ''​Cons''​) together with observers (''​head''​ and ''​tail''​). 
 +  * The code focuses more on **input-output**:​ reversal takes list (instead ​of an array), ​and produces another list. The strategy relies on an auxiliary function ''​rev''​ which uses an accumulator to reverse the list. Both ''​rev''​ and the display function are recursive. However, ''​rev''​ is **tail-recursive** hence efficient as long as the programming language ​supports tail-end optimisation (which Java 8 does not support). 
 + 
 +//Possible usages:// 
 +  * When it is necessary to separate the (reversal) algorithm from the sequence representation (e.g. when we want a single reversal algorithm for several types of lists) 
 +  * After calling ''​reverse''​ we have two list objects, the original and reversed list. This could be useful to test if the sequence is a palindrome. This programming style is called //​purely-functional//:​ objects are never modified. Instead, new objects are created from existing ones. 
 +  
 +The //​Industry//​ style: 
 +<code java> 
 +import java.util.Iterator;​ 
 + 
 +class RevView<​T>​ implements Iterable<​T>​ { 
 + private T[] array; 
 + 
 + public RevView(T[] array){ 
 + this.array = array; 
 +
 + 
 + @Override 
 + public Iterator<​T>​ iterator() { 
 + return new Iterator<​T>​(){ 
 + private int crtIndex = array.length - 1; 
 + 
 + @Override 
 + public boolean hasNext(){ 
 + return crtIndex >= 0; 
 +
 + 
 + @Override 
 + public T next(){ 
 + return array[crtIndex--];​ 
 + }  
 + 
 + @Override 
 + public void remove () {} 
 + }; 
 +
 +
 + 
 +public class OORev { 
 + 
 + public static void main (String[] args) { 
 + String[] s = new String[]{"​1",​ "​2",​ "​3",​ "​4",​ "​5",​ "​6"​};​ 
 +  
 + Iterator<​String>​ r = (new RevView<​String>​(s)).iterator();​ 
 + while (r.hasNext()){ 
 + System.out.println(r.next());​ 
 +
 +
 +
 +</​code>​ 
 +//​Features://​ 
 +  * The solution here focuses on **views**. The sequence is represented as an **array of strings**, but it could be basically any kind of array. The object ''​RevView''​ is a **view** over the array: 
 +    * Note that ''​RevView''​ only holds a reference to the array, not the array itself. ​This means that the array could be modified from //outside// the ''​RevView''​ class; 
 +    * ''​RevView''​ returns an iterator over the array, which allows **traversing** the array in reverse order; 
 +  * The solution also separates the sequence representation (and the separation could be improved), and focuses on **viewing** or **traversing** the list rather than constructing another list from the original one; 
 + 
 +//Possible usages:// 
 +  * Such a solution could be used when different **threads** may want to **read** the same sequence - **views** allow inspecting the sequence without modifying it (in our example).  
 +  * Also, it could be used to check properties of the sequence (e.g. palindrome) without duplicating data, using different views over the same sequence. 
 + 
 + 
 +The //​Uni-math-teacher style// (short. //Uni// style): 
 + 
 +<code java> 
 +interface Op <A,B> { 
 + public B call (A a, B b); 
 +
 + 
 +interface Foldable <A,B> { 
 + public B fold (Op<​A,​B>​ op, B init); 
 +
 + 
 +class List implements Foldable<​Integer,​List>​{ 
 +  
 + Integer val; 
 + List next; 
 + public List (Integer val, List next){ 
 + this.val = val; 
 + this.next = next; 
 +
 + 
 + public List fold (Op<​Integer,​List>​ op, List init){ 
 + if (this.next == null) 
 + return op.call(this.val,​init);​ 
 + return this.next.fold(op,​op.call(this.val,​ init)); 
 +
 +
 + 
 +public class V4 { 
 + 
 + public static void main (String[] args){ 
 +  
 + List v = new List(1, new List(2, new List(3, null))); 
 + List r = v.fold(new Op<​Integer,​List>​(){ 
 + @Override 
 + public List call (Integer i, List l){ 
 + return new List(i,​l);​ 
 +
 + },​null);​ 
 +
 +
 +</​code>​ 
 + 
 +//​Features://​ 
 +  * The solution relies on the observation that **reversing a list** is a particular type of **folding operation**. Consider the sequence ''​1,​2,​3'',​ the ''​+''​ operation and the initial value ''​0''​. Folding the sequence with ''​+''​ and ''​0'',​ amounts to summing up the numbers from the sequence: 
 +<​code>​ 
 +fold({1,​2,​3},​+,​0) = 
 + ​fold({2,​3},​+,​1+0) = 
 +   ​fold({3},​+,​2+1+0) = 
 +    fold({},​+,​3+2+1+0) = 
 +     ​3+2+1+0 
 +</​code>​ 
 + 
 +  *  However, the folding operation need not be arithmetic (nor the initial value - a number). Suppose we replace ''​+''​ by ''​cons''​ ('':''​) and ''​0''​ by the empty list ''​[]''​. The result of the folding operation is ''​3:​2:​1:​[]'',​ which is precisely the reversed list. In this solution, the ''​fold''​ operation is implemented in an abstract fashion, with respect to a generic binary operation ''​op''​ and a generic initial value ''​init''​.  
 +  * List reversal is a **particular case of a fold operation**  
 + 
 +//Possible usages:// 
 +  * This solution is also characteristic to the functional programming style, more specifically,​ programming with **higher-order functions**. A higher-order function (like ''​fold''​) takes other functions (like ''​op''​) and implements some functionality in terms of it. In a functional ​programming language, implementing (and using) a ''​fold'' ​is much simpler and elegant than in an imperative language (like Java). 
 +  * Using higher-order functions is very useful when writing programs over large data (for processing like that done in Machine Learning). Such processing is done using a combination of //​map-reduce//​ functions: //map// transforms data uniformly, and //reduce// computes a new (reduced) value from it. Map-reduce programs are easily ​to parallelise (however, reversal is not demonstration for that). 
 + 
 + 
 +==== Why so many reversals? ==== 
 + 
 +Note that **reversal** is an algorithmically trivial task: take the sequence of elements of the collection at hand, be it array of list (or anything else), in their **reverse** order. 
 + 
 +Our point is that, apart from mastering algorithms, a skilled programmer needs **solid knowledge** on programming concepts and on the way programming ​languages (and the hardware they employare designed. In some cases, these concepts may be subtle (e.g. programming with Monads in Haskell) and may supersede ​the algorithm at hand in complexity. However, they are crucial in developing a **efficient**,​ **secure** and **correct** applications. 
 + 
 +This lecture will focus on different ways of **writing code** for specific algorithms, in different programming languages, with an emphasis on Functional Languages (Haskell) and Logic-based Languages (Prolog). 
 + 
 +We have clear metrics for choosing algorithms. Do we also have metrics for **code writing**? For instance: 
 +  * legibility 
 +  * compactness (number of code lines) 
 +  * ease of use (can other programmers use the code easily) 
 +  * extensibility (can other programmers add modifications to the code easily) 
 +  * good documentation 
 + 
 +is a plausible list. While some of these criteria overlap and are difficult to assess objectively,​ programmers more often than not agree that some programs are **well-written while others are poor** (w.r.t. some criteria). 
 + 
 +==== How many other ways? ==== 
 + 
 +There are many possible variations (and combinations) to our examples, but a few elements do stand out: 
 +  * the functional style for **representing a list** - very akin to Abstract Datatypes 
 +  * the functional style for reversal - relying on recursion, relying on folding 
 +  * the Object Oriented style for **traversing a list** in the third example - which although is tightly linked to Java Collections,​ can be migrated to any other object-oriented language. 
 + 
 +Such **elements of style** are may be called **design patterns**, i.e. generic ways of writing code, which can be deployed for different implementations.  
 + 
 +Some elements of style may have some common ground, or require certain traits from the programming language. These latter are called programming **paradigms**. For instance, writing programs as recursive functions, using higher-order functions and representing data as ADTs (recall that constructors //are// functions) are both styles of the **functional** ​paradigm. 
 + 
 +In this lecture, we shall go over the following paradigms:​ 
 +  * imperative 
 +  * object oriented 
 +  * **functional** 
 +  * **logical** (or logic programming) 
 +  * **associative** (or rule-based) 
 + 
 +===== Other questions ===== 
 + 
 +  * Is there a **right** programming language? How to people choose programming languages?​ 
 +  * Who invented paradigms? (A history between Lisp vs C) 
 +  * Relationship between paradigms and programs (one-to-one,​ one-to-many,​ many-to-many) 
 +  * How **extensible** can programs be?