Skip to content

Commit 91c3296

Browse files
committed
fixed exercises and solutions for ch 3
1 parent 93e3ef2 commit 91c3296

File tree

3 files changed

+116
-75
lines changed

3 files changed

+116
-75
lines changed

Exercises/Chapter03/Exercises.cs

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,41 @@
55

66
namespace Exercises.Chapter3
77
{
8-
public static class Exercises
9-
{
10-
// 1. Write an overload of `GetOrElse`, that returns the wrapped value, or
11-
// calls a given function to compute an alternative default. What is the
12-
// benefit of this overload over the implementation we've seen above?
13-
14-
// 2. Write `Option.Map` in terms of `Match`.
15-
16-
// 3. Write an extension method `Lookup` on `IDictionary<K, T>`, that
17-
// takes a `K` and returns an `Option<T>`.
18-
19-
// 4. Write an extension method `Map` on `IDictionary<K, T>`, that takes a
20-
// `Func<T, R>` and returns a new `Dictionary<K, R>`, mapping the original
21-
// keys to the values resulting from applying the function to the values.
22-
}
23-
24-
// 6. Write implementations for the methods in the `AppConfig` class
25-
// below. (For both methods, a reasonable one-line method body is possible.
26-
// Assume settings are of type string, numeric or date.) Can this
27-
// implementation help you to test code that relies on settings in a
28-
// `.config` file?
29-
public class AppConfig
30-
{
8+
public static class Exercises
9+
{
10+
// 1 Write a generic function that takes a string and parses it as a value of an enum. It
11+
// should be usable as follows:
12+
13+
// Enum.Parse<DayOfWeek>("Friday") // => Some(DayOfWeek.Friday)
14+
// Enum.Parse<DayOfWeek>("Freeday") // => None
15+
16+
// 2 Write a Lookup function that will take an IEnumerable and a predicate, and
17+
// return the first element in the IEnumerable that matches the predicate, or None
18+
// if no matching element is found. Write its signature in arrow notation:
19+
20+
// bool isOdd(int i) => i % 2 == 1;
21+
// new List<int>().Lookup(isOdd) // => None
22+
// new List<int> { 1 }.Lookup(isOdd) // => Some(1)
23+
24+
// 3 Write a type Email that wraps an underlying string, enforcing that it’s in a valid
25+
// format. Ensure that you include the following:
26+
// - A smart constructor
27+
// - Implicit conversion to string, so that it can easily be used with the typical API
28+
// for sending emails
29+
30+
// 4 Take a look at the extension methods defined on IEnumerable inSystem.LINQ.Enumerable.
31+
// Which ones could potentially return nothing, or throw some
32+
// kind of not-found exception, and would therefore be good candidates for
33+
// returning an Option<T> instead?
34+
}
35+
36+
// 5. Write implementations for the methods in the `AppConfig` class
37+
// below. (For both methods, a reasonable one-line method body is possible.
38+
// Assume settings are of type string, numeric or date.) Can this
39+
// implementation help you to test code that relies on settings in a
40+
// `.config` file?
41+
public class AppConfig
42+
{
3143
NameValueCollection source;
3244

3345
//public AppConfig() : this(ConfigurationManager.AppSettings) { }
@@ -38,13 +50,13 @@ public AppConfig(NameValueCollection source)
3850
}
3951

4052
public Option<T> Get<T>(string name)
41-
{
42-
throw new NotImplementedException("your implementation here...");
43-
}
44-
45-
public T Get<T>(string name, T defaultValue)
46-
{
47-
throw new NotImplementedException("your implementation here...");
48-
}
49-
}
53+
{
54+
throw new NotImplementedException("your implementation here...");
55+
}
56+
57+
public T Get<T>(string name, T defaultValue)
58+
{
59+
throw new NotImplementedException("your implementation here...");
60+
}
61+
}
5062
}

Exercises/Chapter03/Solutions.cs

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,58 +3,90 @@
33
using System.Collections.Specialized;
44
//using System.Configuration;
55
using LaYumba.Functional;
6+
using static LaYumba.Functional.F;
7+
using System.Text.RegularExpressions;
68

79
namespace Exercises.Chapter3
810
{
911
using static F;
1012

1113
public static class Solutions
1214
{
13-
// 1. Write an overload of `GetOrElse`, that returns the wrapped value, or
14-
// calls a given function to compute an alternative default.
15-
static T GetOrElse<T>(this Option<T> @this, Func<T> func)
16-
=> @this.Match(
17-
Some: t => t,
18-
None: func);
19-
20-
// What is the benefit of this overload over the implementation we've seen above?
21-
// 'func' is not evalueated unless necessary, resulting in a performance benefit
22-
// if 'func' is expensive
23-
24-
// 2. Write `Option.Map` in terms of `Match`.
25-
static Option<R> Map_Alt<T, R>(this Option<T> @this, Func<T, R> func)
26-
=> @this.Match(
27-
Some: value => F.Some(func(value)),
28-
None: () => F.None);
29-
30-
// 3. Write an extension method `Lookup` on `IDictionary<K, T>`, that
31-
// takes a `K` and returns an `Option<T>`.
32-
static Option<T> Lookup<K, T>(this IDictionary<K, T> map, K key)
33-
=> map.ContainsKey(key) ? Some(map[key]) : None;
34-
35-
// 4. Write an extension method `Map` on `IDictionary<K, T>`, that takes a
36-
// `Func<T, R>` and returns a new `Dictionary<K, R>`, mapping the original
37-
// keys to the values resulting from applying the function to the values.
38-
// TODO
39-
}
15+
// 1 Write a generic function that takes a string and parses it as a value of an enum. It
16+
// should be usable as follows:
4017

41-
public class AppConfig_Solution
42-
{
43-
NameValueCollection source;
18+
// Enum.Parse<DayOfWeek>("Friday") // => Some(DayOfWeek.Friday)
19+
// Enum.Parse<DayOfWeek>("Freeday") // => None
20+
21+
// solution: see LaYumba.Functional.Enum.Parse<T>
22+
23+
24+
// 2 Write a Lookup function that will take an IEnumerable and a predicate, and
25+
// return the first element in the IEnumerable that matches the predicate, or None
26+
// if no matching element is found. Write its signature in arrow notation:
27+
28+
// bool isOdd(int i) => i % 2 == 1;
29+
// new List<int>().Lookup(isOdd) // => None
30+
// new List<int> { 1 }.Lookup(isOdd) // => Some(1)
31+
32+
// Lookup : IEnumerable<T> -> (T -> bool) -> Option<T>
33+
public static Option<T> Lookup<T>(this IEnumerable<T> ts, Func<T, bool> pred)
34+
{
35+
foreach (T t in ts) if (pred(t)) return Some(t);
36+
return None;
37+
}
4438

45-
//public AppConfig_Solution() : this(ConfigurationManager.AppSettings) { }
39+
// 3 Write a type Email that wraps an underlying string, enforcing that it’s in a valid
40+
// format. Ensure that you include the following:
41+
// - A smart constructor
42+
// - Implicit conversion to string, so that it can easily be used with the typical API
43+
// for sending emails
4644

47-
public AppConfig_Solution(NameValueCollection source)
45+
public class Email
4846
{
49-
this.source = source;
47+
static readonly Regex regex = new Regex(@"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$");
48+
49+
private string Value { get; }
50+
51+
private Email(string value) => Value = value;
52+
53+
public static Option<Email> Create(string s)
54+
=> regex.IsMatch(s)
55+
? Some(new Email(s))
56+
: None;
57+
58+
public static implicit operator string(Email e)
59+
=> e.Value;
5060
}
61+
62+
// 4 Take a look at the extension methods defined on IEnumerable inSystem.LINQ.Enumerable.
63+
// Which ones could potentially return nothing, or throw some
64+
// kind of not-found exception, and would therefore be good candidates for
65+
// returning an Option<T> instead?
5166

52-
public Option<T> Get<T>(string key)
53-
=> Some((T)Convert.ChangeType(source[key], typeof(T)));
67+
// 5. Write implementations for the methods in the `AppConfig` class
68+
// below. (For both methods, a reasonable one-line method body is possible.
69+
// Assume settings are of type string, numeric or date.) Can this
70+
// implementation help you to test code that relies on settings in a
71+
// `.config` file?
72+
public class AppConfig
73+
{
74+
NameValueCollection source;
5475

55-
public T Get<T>(string key, T defaultValue)
56-
=> Get<T>(key).Match(
57-
Some: value => value,
58-
None: () => defaultValue);
76+
//public AppConfig() : this(ConfigurationManager.AppSettings) { }
77+
78+
public AppConfig(NameValueCollection source)
79+
{
80+
this.source = source;
81+
}
82+
83+
public Option<T> Get<T>(string key)
84+
=> Some((T)Convert.ChangeType(source[key], typeof(T)));
85+
86+
public T Get<T>(string key, T defaultValue)
87+
=> Get<T>(key).Match(
88+
Some: value => value,
89+
None: () => defaultValue);
90+
}
5991
}
6092
}

LaYumba.Functional/Enum.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
public static class Enum
66
{
77
public static Option<T> Parse<T>(this string s) where T : struct
8-
{
9-
T t;
10-
return System.Enum.TryParse(s, out t) ? Some(t) : None ;
11-
}
8+
=> System.Enum.TryParse(s, out T t) ? Some(t) : None ;
129
}
1310
}

0 commit comments

Comments
 (0)