@@ -9,99 +9,44 @@ namespace Exercises.Chapter5.Solutions
9
9
{
10
10
static class Exercises
11
11
{
12
- // 1. Write some tests to illustrate that `IEnumerable` obeys the functor laws
13
- // (tip: you can take the tests for `Option` in LaYumba.Functional.Tests/Option/FunctorLaws
14
- // as a starting point).
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`:
14
+ decimal AverageEarningsOfRichestQuartile ( List < Person > population )
15
+ => population
16
+ . OrderByDescending ( p => p . Earnings )
17
+ . Take ( population . Count / 4 )
18
+ . Select ( p => p . Earnings )
19
+ . Average ( ) ;
15
20
16
- // Answers in LaYumba.Functional.Tests/IEnumerable/FunctorLaws
21
+ // OrderByDescending : (IEnumerable<T>, (T -> decimal)) -> IEnumerable<T>
22
+ // particularized for this case:
23
+ // OrderByDescending : (IEnumerable<Person>, (Person -> decimal)) -> IEnumerable<Person>
17
24
18
- // 2. Implement `Map` in terms of `Bind` and `Return` for `Option`
19
- // and `IEnumerable`.
20
-
21
- static Option < R > Map < T , R > ( this Option < T > @this
22
- , Func < T , R > func )
23
- => @this . Bind ( v => Some ( func ( v ) ) ) ;
25
+ // Take : (IEnumerable<T>, int) -> IEnumerable<T>
26
+ // particularized for this case:
27
+ // Take : (IEnumerable<Person>, int) -> IEnumerable<Person>
24
28
25
- static IEnumerable < R > Map < T , R > ( this IEnumerable < T > @this
26
- , Func < T , R > func )
27
- => @this . Bind ( v => List ( func ( v ) ) ) ;
29
+ // Select : ( IEnumerable<T>, (T -> R)) -> IEnumerable<R>
30
+ // particularized for this case:
31
+ // Select : (IEnumerable<Person>, (Person -> decimal)) -> IEnumerable<decimal>
28
32
29
- // 3. Write a class `Switch<T, R>` that would enable you to write the code
30
- // in the snippet below. (Hint: notice how each `.Case` is equivalent to a
31
- // `KeyValuePair` in the `TransferMappingRules` Dictionary we have used
32
- // above.)
33
+ // Average : IEnumerable<T> -> T
34
+ // particularized for this case:
35
+ // Average : IEnumerable<decimal> -> decimal
33
36
34
- static string DailyComment ( DateTime day )
35
- {
36
- Predicate < DateTime > always = d => true ;
37
- Predicate < DateTime > weekend = d => d . DayOfWeek == DayOfWeek . Saturday
38
- || d . DayOfWeek == DayOfWeek . Sunday ;
39
- Predicate < DateTime > friday = d => d . DayOfWeek == DayOfWeek . Friday ;
40
- Predicate < DateTime > nearEOY = d => d . Month == 12 && d . Day > 25 ;
41
37
42
- var @switch = Switch < DateTime , string > ( )
43
- . Case ( always , d => d . DayOfWeek . ToString ( ) + ":" )
44
- . Case ( weekend , d => "chilling..." )
45
- . Case ( friday , d => "thank God it's the weekend!" )
46
- . Case ( nearEOY , d => "getting ready for a party!" ) ;
38
+ // 2 Check your answer with the MSDN documentation: https://docs.microsoft.com/
39
+ // en-us/dotnet/api/system.linq.enumerable. How is Average different?
47
40
48
- IEnumerable < string > comments = @switch . MatchAll ( day ) ;
49
- return string . Join ( " " , comments . ToArray ( ) ) ;
50
- }
41
+ // Average is the only method call that does not return an IEnumerable ;
42
+ // this also means that Average is the only greedy method and causes all the
43
+ // previous ones in the chain to be evaluated
51
44
52
- // note that here I use a tuple instead of KeyValuePair, but that's a minor detail
53
- static IEnumerable < ( Predicate < T > , Func < T , R > ) > Switch < T , R > ( )
54
- => Enumerable . Empty < ( Predicate < T > , Func < T , R > ) > ( ) ;
55
-
56
- static IEnumerable < ( Predicate < T > , Func < T , R > ) > Case < T , R > (
57
- this IEnumerable < ( Predicate < T > , Func < T , R > ) > rules
58
- , Predicate < T > pred , Func < T , R > func )
59
- => rules . Append ( ( pred , func ) ) ;
60
45
61
- static IEnumerable < R > MatchAll < T , R > ( this IEnumerable < ( Predicate < T > , Func < T , R > ) > rules , T t )
62
- => rules . Where ( rule => rule . Item1 ( t ) )
63
- . Map ( rule => rule . Item2 ( t ) ) ;
64
-
65
- // 4. Write tests to ensure it works as expected.
46
+ // 3 Implement a general purpose Compose function that takes two unary functions
47
+ // and returns the composition of the two.
66
48
67
- [ TestCase ( "24 Jun 2016" , ExpectedResult = "Friday: thank God it's the weekend!" ) ]
68
- [ TestCase ( "27 Dec 2016" , ExpectedResult = "Tuesday: getting ready for a party!" ) ]
69
- public static string TestDateComment ( string date )
70
- => DailyComment ( DateTime . Parse ( date ) ) ;
71
-
72
- // 5. Exhance `Switch<T, R>` so that it can also be used like the `switch`
73
- // statement in the C# language, by adding an overload taking a constant
74
- // value to match against `Case(T value, Func<T, R> func)`, and a `Match`
75
- // method that returns the result of invoking the function of the _first_
76
- // matching case.
77
-
78
- static IEnumerable < ( Predicate < T > , Func < T , R > ) > Case < T , R > (
79
- this IEnumerable < ( Predicate < T > , Func < T , R > ) > rules
80
- , T value , Func < T , R > func )
81
- => rules . Append ( ( new Predicate < T > ( v => v . Equals ( value ) ) , func ) ) ;
82
-
83
- static R Match < T , R > ( this IEnumerable < ( Predicate < T > , Func < T , R > ) > rules , T t )
84
- => rules . Where ( rule => rule . Item1 ( t ) )
85
- . First ( ) . Item2 ( t ) ;
86
-
87
- // Write a simple scenario with those methods, and unit test it
88
-
89
- static string NumberComment ( int number )
90
- {
91
- Predicate < int > isOdd = i => i % 2 == 1 ;
92
-
93
- var @switch = Switch < int , string > ( )
94
- . Case ( 33 , _ => "33 is my favourite number!" ) // match on specific value
95
- . Case ( isOdd , i => $ "{ i } is a bit of an odd one...") // match on a condition
96
- . Case ( _ => true , i => $ "{ i } is just a boring, even number") ; // catch all
97
-
98
- return @switch . Match ( number ) ;
99
- }
100
-
101
-
102
- [ TestCase ( 33 , ExpectedResult = "33 is my favourite number!" ) ]
103
- [ TestCase ( 27 , ExpectedResult = "27 is a bit of an odd one..." ) ]
104
- [ TestCase ( - 2 , ExpectedResult = "-2 is just a boring, even number" ) ]
105
- public static string TestNumberComment ( int number ) => NumberComment ( number ) ;
49
+ static Func < T1 , R > Compose < T1 , T2 , R > ( this Func < T2 , R > g , Func < T1 , T2 > f )
50
+ => x => g ( f ( x ) ) ;
106
51
}
107
52
}
0 commit comments