2
2
using System . Collections . Generic ;
3
3
using System . Linq ;
4
4
using LaYumba . Functional ;
5
+ using static LaYumba . Functional . F ;
5
6
6
7
namespace Exercises . Chapter4 . Solutions
7
8
{
8
9
using static F ;
9
10
10
11
static class Solutions
11
12
{
12
- // 1. Without looking at any code or documentation (or intllisense), write the function signatures of
13
- // `OrderByDescending`, `Take` and `Average`, which we used to implement `AverageEarningsOfRichestQuartile`:
13
+ // 1 Implement Map for ISet<T> and IDictionary<K, T>. (Tip: start by writing down
14
+ // the signature in arrow notation.)
14
15
15
- // decimal AverageEarningsOfRichestQuartile(List<Person> population)
16
- // => population
17
- // .OrderByDescending(p => p.Earnings)
18
- // .Take(population.Count/4)
19
- // .Select(p => p.Earnings)
20
- // .Average();
21
-
22
- // OrderByDescending : (IEnumerable<T>, (T -> decimal)) -> IEnumerable<T>
23
- // particularized for this case:
24
- // OrderByDescending : (IEnumerable<Person>, (Person -> decimal)) -> IEnumerable<Person>
25
-
26
- // Take : (IEnumerable<T>, int) -> IEnumerable<T>
27
- // particularized for this case:
28
- // Take : (IEnumerable<Person>, int) -> IEnumerable<Person>
29
-
30
- // Select : (IEnumerable<T>, (T -> R)) -> IEnumerable<R>
31
- // particularized for this case:
32
- // Select : (IEnumerable<Person>, (Person -> decimal)) -> IEnumerable<decimal>
33
-
34
- // Average : IEnumerable<T> -> T
35
- // particularized for this case:
36
- // Average : IEnumerable<decimal> -> decimal
37
-
38
-
39
-
40
- // 2. Check your answer with the msdn documentation: https://msdn.microsoft.com/en-us/library/system.linq.enumerable(v=vs.110).aspx.
41
- // How is `Average` different?
42
-
43
- // Average is the only method call that does not return an IEnumerable;
44
- // this also means that Average is the only greedy method and causes all the
45
- // previous ones in the chain to be evaluated
46
-
47
-
48
-
49
- // 3. Implement a general purpose `Compose` function that takes two unary functions
50
- // and returns the composition of the two.
51
-
52
- static Func < T1 , R > Compose < T1 , T2 , R > ( this Func < T2 , R > g , Func < T1 , T2 > f )
53
- => x => g ( f ( x ) ) ;
16
+ // Map : ISet<T> -> (T -> R) -> ISet<R>
17
+ static ISet < R > Map < T , R > ( this ISet < T > ts , Func < T , R > f )
18
+ {
19
+ var rs = new HashSet < R > ( ) ;
20
+ foreach ( var t in ts )
21
+ rs . Add ( f ( t ) ) ;
22
+ return rs ;
23
+ }
54
24
25
+ // Map : IDictionary<K, T> -> (T -> R) -> IDictionary<K, R>
26
+ static IDictionary < K , R > Map < K , T , R >
27
+ ( this IDictionary < K , T > dict , Func < T , R > f )
28
+ {
29
+ var rs = new Dictionary < K , R > ( ) ;
30
+ foreach ( var pair in dict )
31
+ rs [ pair . Key ] = f ( pair . Value ) ;
32
+ return rs ;
33
+ }
55
34
56
35
57
- // 4. Implement a `Lookup` extension method on `IDictionary<T>` returning `Option<T>` .
36
+ // 2 Implement Map for Option and IEnumerable in terms of Bind and Return .
58
37
59
- static Option < T > Lookup < K , T > ( this IDictionary < K , T > dict , K key )
60
- {
61
- T value ;
62
- return dict . TryGetValue ( key , out value ) ? Some ( value ) : None ;
63
- }
38
+ public static Option < R > Map < T , R > ( this Option < T > opt , Func < T , R > f )
39
+ => opt . Bind ( t => Some ( f ( t ) ) ) ;
64
40
41
+ public static IEnumerable < R > Map < T , R > ( this IEnumerable < T > ts , Func < T , R > f )
42
+ => ts . Bind ( t => List ( f ( t ) ) ) ;
65
43
66
44
67
- // 5. Use ` Bind` and the ` Lookup` function from the previous exercise to
68
- // implement ` GetWorkPermit` below.
45
+ // 3 Use Bind and an Option-returning Lookup function (such as the one we defined
46
+ // in chapter 3) to implement GetWorkPermit, shown below.
69
47
70
48
static Option < WorkPermit > GetWorkPermit ( Dictionary < string , Employee > employees , string employeeId )
71
49
=> employees . Lookup ( employeeId ) . Bind ( e => e . WorkPermit ) ;
72
50
51
+
73
52
// Then enrich the implementation so that `GetWorkPermit`
74
53
// returns `None` if the work permit has expired.
75
54
@@ -82,8 +61,7 @@ static Option<WorkPermit> GetValidWorkPermit(Dictionary<string, Employee> employ
82
61
static Func < WorkPermit , bool > HasExpired => permit => permit . Expiry < DateTime . Now . Date ;
83
62
84
63
85
-
86
- // 6. Use `Bind` to implement `AverageYearsWorkedAtTheCompany` below(only
64
+ // 4 Use Bind to implement AverageYearsWorkedAtTheCompany, shown below (only
87
65
// employees who have left should be included).
88
66
89
67
static double AverageYearsWorkedAtTheCompany ( List < Employee > employees )
@@ -100,14 +78,5 @@ select YearsBetween(e.JoinedOn, leftOn)
100
78
101
79
static double YearsBetween ( DateTime start , DateTime end )
102
80
=> ( end - start ) . Days / 365d ;
103
-
104
-
105
- // 7. Write implementations of `Where`, `ForEach` and `Bind` for
106
- // `Container`. Try doing so without looking at the implementations for
107
- // `Option` first, and only check them if needed.
108
-
109
- // please see the implementation of Container
110
- // NOTE: `Where` cannot be implemented: since Container contains exactly
111
- // one value, you cannot "filter out" that value
112
81
}
113
82
}
0 commit comments