Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
pp:2025:scala:l02 [2025/03/05 10:56] pdmatei |
pp:2025:scala:l02 [2025/03/16 01:35] (current) cata_chiru |
||
---|---|---|---|
Line 48: | Line 48: | ||
**2.2.2** Suppose $math[x_1, x_2, x_3] are the only values from a given range. What is the order in which $math[+] is performed, in ''foldWith(acc)( (x,y) => x+y) '' ? Write an alternative implementation for ''foldWith'' which would compute: $math[x_1 + (x_2 + (x_3 + acc))], in this particular order. | **2.2.2** Suppose $math[x_1, x_2, x_3] are the only values from a given range. What is the order in which $math[+] is performed, in ''foldWith(acc)( (x,y) => x+y) '' ? Write an alternative implementation for ''foldWith'' which would compute: $math[x_1 + (x_2 + (x_3 + acc))], in this particular order. | ||
- | **2.2.3** Define the function ''foldConditional'' which extends ''foldWith'' by also adding a predicate ''p: Int => Boolean''. ''foldConditional'' will reduce only those elements of a range which satisfy the predicate. | + | **2.2.3** Define the function ''foldConditional'' which extends ''foldWith'' by also adding a predicate ''p: Int => Boolean''. ''foldConditional'' will reduce only those elements of a range which satisfy the predicate. The accumulator should not be filtered. |
<code scala> | <code scala> | ||
- | def foldConditional(op: (Int,Int) => Int, p: Int => Boolean)(start: Int, stop: Int): Int = ??? | + | def foldConditional(acc: Int)(op: (Int,Int) => Int, p: Int => Boolean)(start: Int, stop: Int): Int = ??? |
</code> | </code> | ||
**2.2.4** Write a function ''foldMap'' which takes values $math[a_1, a_2, \ldots, a_k] from a range and computes $math[f(a_1)\;op\;f(a_2)\;op\;\ldots f(a_k)]. | **2.2.4** Write a function ''foldMap'' which takes values $math[a_1, a_2, \ldots, a_k] from a range and computes $math[f(a_1)\;op\;f(a_2)\;op\;\ldots f(a_k)]. | ||
- | Use the ''apply'' and ''foldWith'' methods | + | Use the ''apply'' and ''foldWith'' methods. The accumulator should not be transformed by ''f''. |
<code scala> | <code scala> | ||
- | def foldMap(op: (Int,Int) => Int, f: Int => Int)(start: Int, stop: Int): Int = ??? | + | def foldMap(acc: Int)(op: (Int,Int) => Int, f: Int => Int)(start: Int, stop: Int): Int |
+ | = ??? | ||
+ | </code> | ||
+ | |||
+ | **2.2.5** Write a function ''sum'' which takes values start, end and computes the sum of numbers between start and end. | ||
+ | Use the curry function ''foldWith'' defined before. | ||
+ | <code scala> | ||
+ | def sum(start: Int, stop: Int): Int = ??? | ||
</code> | </code> | ||
===== 2.3 Curry vs Uncurry ===== | ===== 2.3 Curry vs Uncurry ===== | ||
- | **2.3.1** Modify the function below so that it's curry and use it to calculate ''5*3'' | + | **2.3.1** Modify the function below so that it's curry and use it to compute ''5*3'' |
<code scala> | <code scala> | ||
- | def multiply(x:Int, y:Int): Int => x * y | + | def multiply(x:Int, y:Int): Int = x * y |
</code> | </code> | ||
Line 99: | Line 106: | ||
} | } | ||
</code> | </code> | ||
+ | |||
+ | ===== 2.5 Nice to know ===== | ||
+ | |||
+ | In a computer, we would like the central processing unit / thread to have predominantly a managerial role. | ||
+ | |||
+ | As a result, by executing intense computation on this branch, we would delay other functionalities or would lock into a specific task instead of scheduling. | ||
+ | |||
+ | To solve this limitation, programmers have created ways ('''functional closures''') in which the main thread injects all the needed parameters to functions and then passes them to other threads, from where they can be unpacked and run. | ||
+ | |||
+ | Such an implementation for functional closures is represented by '''zero-parameter lambdas''' where the evaluation is delayed by currying such a function over our method: | ||
+ | |||
+ | <code scala> | ||
+ | // Variation 1 - using zero-parameter lambdas | ||
+ | // the type of the target function | ||
+ | type Result = Int | ||
+ | |||
+ | // a closure is represented as a function that takes zero parameters | ||
+ | type Closure = () => Result | ||
+ | |||
+ | // type of a demo function | ||
+ | type TFun = (Int, Int) => Result | ||
+ | |||
+ | // run in thread will call that function to evaluate | ||
+ | def run_in_thread(f: Closure): Result = f() | ||
+ | |||
+ | // Dummy method used to illustrate the power of currying | ||
+ | // "f" in curry_for_thread | ||
+ | def process_data(x: Int, y: Int): Int = { | ||
+ | println("Hello") | ||
+ | x + y | ||
+ | } | ||
+ | |||
+ | // creates a lambda with zero parameters, thus "delaying" the call of f | ||
+ | // "f" in run_in_thread | ||
+ | def curry_for_thread(f: TFun, somex: Int, somey: Int): Closure = | ||
+ | () => f(somex, somey) | ||
+ | |||
+ | val close = curry_for_thread(process_data, 0, 1) | ||
+ | |||
+ | // If you run this code into a Scala Worksheet you will see that "Hello" gets printed when calling the function bellow. | ||
+ | // This is thanks to the 0 parameter lambda in curry_for_thread that "forces" | ||
+ | // another call of the function for it to execute its body. | ||
+ | run_in_thread(close) | ||
+ | </code> | ||
+ | |||
+ | Keep this example in mind! We will meet with it again in a future laboratory! :-) | ||
+ | |||
+ |