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 [2018/03/05 12:45]
pdmatei
pp:intro [2019/02/14 18:19] (current)
pdmatei
Line 5: Line 5:
 We start the lecture by looking at three different Java programs which reverse a sequence of integers. We start the lecture by looking at three different Java programs which reverse a sequence of integers.
  
-The //C-programmer// style:+The //Cook-book// style:
 <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 //functional// 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) 
-The //object-oriented// style:+  * After calling ​''​reverse'' ​we have two list objects, ​the original and reversed ​list. This could be useful to test if the sequence is a palindromeThis programming style is called //purely-functional//:​ objects are never modified. Instead, new objects are created from existing ones
 +  
 +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 106: Line 129:
  }  }
 } }
 +</​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>​ </​code>​
  
-The final alternative relies on the Java concept of **iterator*of collections. Informallya 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''​. ​+  ​ ​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 133: 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: