Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
fp:lab06 [2021/04/15 15:55] pdmatei created |
fp:lab06 [2022/04/08 12:45] (current) pdmatei |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== 6. Functional closures ====== | + | ====== Lab 06. Polymorphism in Scala ====== |
- | ==== Function application and composition as higher-order functions ==== | + | This lab will start with the implementations discussed during lecture. Please find the polymorphic trait ''FList[A]'' below: |
- | One key idea from functional programming is that functions are **first-class** (or **first-order**) values, just like integers, strings, etc. . They can be passed as function **arguments** and also be returned by function application. | + | <code scala> |
+ | trait FList[A]{ // list with elements of type A | ||
+ | def length: Int | ||
+ | def head: A | ||
+ | def tail: FList[A] | ||
+ | def map[B](f: A => B): FList[B] | ||
+ | // a op (b op (c op acc)) | ||
+ | def foldRight[B](acc: B)(op: (A,B) => B): B | ||
+ | // ((acc op a) op b) op c | ||
+ | def foldLeft[B](acc: B)(op: (B,A) => B): B | ||
+ | |||
+ | def contains(e: A):Boolean = | ||
+ | this.foldRight(false)(_ == e || _) | ||
+ | |||
+ | } | ||
+ | </code> | ||
- | Functions which //take other functions as parameter// are called **higher-order**. | + | together with the implementations: |
+ | <code scala> | ||
+ | case class FNil[A]() extends FList[A]{ | ||
+ | override def length: Int = 0 | ||
+ | override def head: A = throw new Exception("head on empty list") | ||
+ | override def tail: FList[A] = throw new Exception("head on empty list") | ||
+ | override def map[B](f: A => B): FList[B] = FNil[B] | ||
+ | override def foldRight[B](acc: B)(op: (A,B) => B): B = acc | ||
+ | override def foldLeft[B](acc: B)(op: (B,A) => B): B = acc | ||
+ | } | ||
- | ==== Lambdas ==== | + | case class Cons[A](x:A, xs:FList[A]) extends FList[A]{ |
- | 46. Functions can be passed as arguments just like any other value value. Also, functions can be returned as parameter. In order to do so, it is convenient to define functions without naming them. This is done using **lambda**'s. For a more detailed discussion regarding lambdas, see the lecture. The following definitions are equivalent: | + | override def length = 1 + xs.length |
+ | override def head:A = x | ||
+ | override def tail:FList[A] = xs | ||
+ | override def map[B](f: A => B): FList[B] = | ||
+ | Cons(f(x),xs.map(f)) | ||
+ | override def foldRight[B](acc: B)(op: (A,B) => B): B = | ||
+ | op(x, xs.foldRight(acc)(op)) | ||
+ | override def foldLeft[B](acc: B)(op: (B,A) => B): B = | ||
+ | xs.foldLeft(op(acc,x))(op) | ||
+ | } | ||
- | <code haskell> | ||
- | f x y = x + y | ||
- | f x = \y -> x + y | ||
- | f = \x -> \y -> x + y | ||
- | f = \x y -> x + y | ||
</code> | </code> | ||
- | ==== 6.1. Warm-up ==== | + | Add the following methods in the trait ''FList'' and implement them. **Some methods can be directly implemented in the trait, using ''map'', ''foldRight'', ''foldLeft'' or other functions.** |
+ | * Can you figure which ones are best implemented in the trait and which in the case classes? | ||
- | Consider an URL such as: ''domain-name.com/books/get_item?param1=1234¶m2=5678''. In the previous example: | + | **6.1.** ''indexOf'' determines the position of a value in the list (starting with 0) |
- | * ''books'' is a resource-name | + | <code scala> |
- | * ''get_item'' is an action | + | def indexOf(e: A): Int |
- | * ''param1'' and ''param2'' are parameters with associated values ''1234'' and ''5678'' respectively. | + | </code> |
- | 6.1.1. Write the following functions: | + | **6.2.** ''update'' creates a new list where the given position is modified with a new value: |
- | * ''get_resource'' | + | <code scala> |
- | * ''get_action'' | + | //Cons(1,Cons(2,Cons(3,FNil()))).update(9,1) = Cons(1,Cons(9,Cons(3,FNil()))) |
- | * ''get_params'' | + | def update(e: A, pos: Int): FList[A] |
+ | </code> | ||
- | which extract the resource name, the action and the parameters of a given URL (represented as string). Write yourself the signature of the functions. When implementing, try to: | + | **6.3.** ''append'' concatenates this list to another: |
- | * generalise as much as possible | + | <code scala> |
- | * use higher-order functions and composition | + | def append(l: FList[A]): FList[A] |
+ | </code> | ||
- | 6.1.2. Given a list of URLs, extract those which satisfy, at the same time the following constraints: | + | **6.4.** ''reverse'' returns the reversed list: |
- | * have the domain name equal to "gmail.com" or "yahoo.com" | + | <code scala> |
- | * have the resource equal to "books" or "movies" | + | def reverse: FList[A] |
- | * have an action from the set ''{get_item, remove_item, insert_item}'' | + | </code> |
- | * have at least three parameters | + | |
- | Choose the most effective way of writing your code such that: | + | **6.5.** ''last'' returns the last element of the list: |
- | * your code is easy to use | + | <code scala> |
- | * your code is easy to **extend** | + | def last: A |
- | + | </code> | |
- | ===== 2. A predicate-based implementation for sets ===== | + | |
- | 2.1. Consider **sets** represented as characteristic functions with signature ''s :: Integer -> Bool'', where ''s x'' is true if ''x'' a member in the set. Examples: | + | |
- | <code haskell> | + | |
- | s1 1 = True | + | |
- | s1 2 = True | + | |
- | s1 _ = False | + | |
- | + | ||
- | s2 x = mod x 2 == 0 | + | |
- | + | ||
- | s3 _ = False | + | |
+ | **6.6.** ''filter'' filters the elements of the list: | ||
+ | <code scala> | ||
+ | def filter(p: A => Boolean): FList[A] | ||
</code> | </code> | ||
- | Above, ''s1'' is the set $math[\{1,2\}], ''s2'' is the set of even integers and ''s3'' is the empty-set. Write a function which tests if an element is a member of a set: | ||
- | <code haskell> | ||
- | mem :: (Integer -> Bool) -> Integer -> Bool | ||
- | mem = ... | ||
- | </code> | ||
- | |||
- | 2.2. Define the set $math[\{2^n \mid n\in\mathbb{N}\}]. | ||
- | |||
- | 2.3. Define the set of natural numbers. | ||
- | 2.4. Implement the intersection of two sets. Use lambdas. | + | **6.7.** ''zip'' combines two lists into a list of pairs. If **either** list is larger, the remaining elements are discarded. |
- | <code haskell> | + | <code scala> |
- | intersection :: (Integer -> Bool) -> (Integer -> Bool) -> (Integer -> Bool) | + | // Cons(1,(Cons(2,Cons(3,FNil()))).zip(Cons(true,Cons(false,Cons(true,FNil())))) = |
+ | // Cons((1,true),Cons((2,false),Cons((3,true),FNil()))) | ||
+ | def zip[B](l: FList[B]): FList[(A,B)] | ||
</code> | </code> | ||
- | 2.5. Write intersection in another way, (without using lambdas). | + | **6.8.** ''insSorted'' inserts an element into a sorted list so that the result is a sorted list. |
- | <code haskell> | + | <code scala> |
- | intersection' :: (Integer -> Bool) -> (Integer -> Bool) -> Integer -> Bool | + | def insSorted(f: A => Int)(e: A): FList[A] |
</code> | </code> | ||
- | 2.6. Write a function which takes a list of integers, and returns the set which contains them. | + | **6.9.** ''sortBy'' sorts a list using insertion sort. |
- | <code haskell> | + | <code scala> |
- | toSet :: [Integer] -> (Integer -> Bool) | + | def sortBy(f: A => Int): FList[A] |
</code> | </code> | ||
- | 2.7. Implement a function which takes a list of sets and computes their intersection. | + | **6.10 (!)** Implement a method ''pack'' which encodes a sorted list as follows: |
- | + | <code scala> | |
- | <code haskell> | + | [1,1,1,2,3,4,4,5,6].pack = [(1,3),(2,1),(3,1),(4,2),(5,1),(6,1)] |
- | capList :: [Integer -> Bool] -> Integer -> Bool | + | def pack: FList[(A,Int)] |
</code> | </code> | ||
- | |||