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 [2019/02/14 16:09]
pdmatei
pp:intro [2019/02/14 18:19] (current)
pdmatei
Line 8: Line 8:
 <code java> <code java>
 public class Rev { public class Rev {
- public static void show (Integer[] v){ 
- for (Integer i:v) 
- System.out.println(i);​ 
- } 
  public static void main (String[] args) {  public static void main (String[] args) {
  Integer[] v = new Integer[] {1,​2,​3,​4,​5,​6,​7,​8,​9};​  Integer[] v = new Integer[] {1,​2,​3,​4,​5,​6,​7,​8,​9};​
Line 25: Line 21:
 } }
 </​code>​ </​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 a reversed one.
  
-The first approach stores ​the sequence as an array, and performs reversal in-place, by swapping the first half of the array with the second. The code is **compact** and fairly **efficient** in terms of computational complexity. ​+//Possible usages:// 
 +  * when development speed is critical 
 +  * when minimising ​the number ​of computation steps is critical
  
-The //undergrad-math-teacher-style//​ style:+The //Undergrad-math-teacher-style//​ style (short //​Undergrad//​ style):
 <code java> <code java>
-class List {+interface List { 
 + public Integer head (); 
 + public List tail (); 
 +
 + 
 +class Cons implements ​List {
  Integer val;  Integer val;
  List next;  List next;
- public ​List(Integer val, List next){+ public ​Cons (Integer val, List next){
  this.val = val;  this.val = val;
  this.next = next;  this.next = next;
  }  }
  @Override  @Override
- public ​String toString(){ + public ​Integer head() {return val;} 
- if (next == null) + @Override 
- return val.toString()+ public List tail() {return ​next;}
- return val.toString()+" "+next.toString(); +
- }+
 } }
-public class FuncRev { 
  
- private static List rev(List x, List y){ +class Empty implements List { 
- if (x == null)+ @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 y;
- return rev(x.next,new List(x.val,y));+ return rev(x.tail(), new Cons(x.head(),y));
  }  }
 +
  public static List reverse (List l){  public static List reverse (List l){
- return rev(l,null);+ return rev(l,new Empty());
  }  }
- public static void main (String[] args) { + 
- List v = new List(1, new List(2, new List(3, new List(4, new List (5,null))))); + public static void main (String[] args){ 
- System.out.println(reverse(v));+ List v = new Cons(1, new Cons(2, new Cons(3, new Empty()))); 
 + List r = reverse(v);
  }  }
 +
 } }
 </​code>​ </​code>​
 +//​Features://​
 +  * This solution first attempts to **separate** the sequence representation from the **reversal algorithm**. ​
 +  * The sequence is **represented** as a **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 a 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).
  
-Unlike ​the former approach, here the code focuses more on **input-output**: ​reversal ​takes a 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 recursiveHowever''​rev''​ is **tail-recursive** hence quite efficient+//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 modifiedInsteadnew objects are created from existing ones
 + 
 The //​Industry//​ style: The //​Industry//​ style:
 <code java> <code java>
 import java.util.Iterator;​ import java.util.Iterator;​
  
-class RVList<T> implements Iterable<​T>​ {+class RevView<T> implements Iterable<​T>​ {
  private T[] array;  private T[] array;
  
- public ​RVList(T[] array){+ public ​RevView(T[] array){
  this.array = array;  this.array = array;
  }  }
Line 100: Line 123:
  String[] s = new String[]{"​1",​ "​2",​ "​3",​ "​4",​ "​5",​ "​6"​};​  String[] s = new String[]{"​1",​ "​2",​ "​3",​ "​4",​ "​5",​ "​6"​};​
   
- Iterator<​String>​ r = (new RVList<​String>​(s)).iterator();​+ Iterator<​String>​ r = (new RevView<​String>​(s)).iterator();​
  while (r.hasNext()){  while (r.hasNext()){
  System.out.println(r.next());​  System.out.println(r.next());​
Line 107: Line 130:
 } }
 </​code>​ </​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//:+ 
 +The //​Uni-math-teacher style// ​(short. //Uni// style):
  
 <code java> <code java>
Line 151: Line 183:
 </​code>​ </​code>​
  
-The final alternative ​relies on the Java concept of **iterator** of collectionsInformallya collection of elements has the property that its elements can be //​enumerated//​ or //​iterated//​Any such collection is an ''​Iterable''​ (extends ​the Java interface Iterable). Here, ''​RVList''​ is such an iterable collection, which takes its elements at construction,​ from an array. ''​RVList''​ is **generic**hence its elements can be of any type ''​T''​. ​+//​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 listIn this solution, the ''​fold'' ​operation ​is implemented in an abstract fashionwith respect to a generic binary operation ​''​op''​ and a generic initial value ''​init''​. ​ 
 +  * List reversal is a **particular case of a fold operation** ​
  
-The fundamental trait of an ''​Iterable''​ object ​is that it implements ​the method ''​iterator''​which returns just that. An **iterator** is an object which allows enumerating all elements of the collection at hand, via the methods ​''​hasNext'' ​and ''​next''​. Sometimes (as in the case of a ''​Set''​) ​the order is unimportantHerethe order matters ​elements ​are explored in their reverse order. Technicallythe iterator object ​is an **anonymous class**.+//Possible usages:// 
 +  * This solution ​is also characteristic to the functional programming stylemore 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 uniformlyand //reduce// computes a new (reduced) value from it. Map-reduce programs ​are easily to parallelise (howeverreversal ​is not a demonstration for that).
  
-This alternative focuses on **list traversal**,​ and on the **generality** of the approach. Basically, any Java collection (for which order makes sense) can be transformed to an array (via the ''​toArray''​ method) and subsequently - an ''​RVList''​ which can be traversed in reverse order via its iterator. 
  
 ==== Why so many reversals? ==== ==== Why so many reversals? ====
  
-The reason for choosing ​reversal ​as a running example is that the task is algorithmically trivial: take the sequence of elements of the collection at hand, be it array of list (or anything else), in their **reverse** order.+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.
  
-Howeverthere are **many different ways** for implementing reversal, and each makes perfect sense in some settingMoreoversome of these ways are**subtle**, **conceptually challenging**and require ​**higher skill/​knowledge** in operating the programming language at hand+Our point is thatapart from mastering algorithms, a skilled programmer needs **solid knowledge** on programming concepts and on the way programming languages (and the hardware they employ) are designed. In some casesthese concepts may be subtle (e.g. programming with Monads in Haskell) ​and may supersede the algorithm at hand in complexityHoweverthey are crucial in developing a **efficient**, **secure** and **correct** applications.
  
-Our point, and one of the major objectives of this lecture, is to emphasise that, apart from developing fast and correct algorithms, a task of equal challenge and importance is **writing ​down the code**, in the **suitable ​programming ​language**.+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: We have clear metrics for choosing algorithms. Do we also have metrics for **code writing**? For instance:
Line 176: Line 220:
 ==== How many other ways? ==== ==== How many other ways? ====
  
-There are many possible variations to our three examples, but a few elements do stand out: +There are many possible variations ​(and combinations) ​to our examples, but a few elements do stand out: 
-  * the functional style for **representing a list** ​in the second example ​- very akin to Abstract Datatypes +  * the functional style for **representing a list** - very akin to Abstract Datatypes 
-  * the functional style for reversal - relying on recursion, ​in the same example+  * 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.   * 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.
  
-These **elements of style** are frequently ​called **design patterns** ​generic ways of writing code, which can be deployed for different implementations. ​+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 rely on certain traits ​of the programming language. These latter are called programming **paradigms**. For instance, writing programs as recursive functions ​as well as representing data as ADTs (recall that constructors //are// functions) are both styles of the **functional** paradigm.+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: In this lecture, we shall go over the following paradigms: