|
3 | 3 | using System.Collections.Specialized;
|
4 | 4 | //using System.Configuration;
|
5 | 5 | using LaYumba.Functional;
|
| 6 | +using static LaYumba.Functional.F; |
| 7 | +using System.Text.RegularExpressions; |
6 | 8 |
|
7 | 9 | namespace Exercises.Chapter3
|
8 | 10 | {
|
9 | 11 | using static F;
|
10 | 12 |
|
11 | 13 | public static class Solutions
|
12 | 14 | {
|
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: |
40 | 17 |
|
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 | + } |
44 | 38 |
|
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 |
46 | 44 |
|
47 |
| - public AppConfig_Solution(NameValueCollection source) |
| 45 | + public class Email |
48 | 46 | {
|
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; |
50 | 60 | }
|
| 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? |
51 | 66 |
|
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; |
54 | 75 |
|
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 | + } |
59 | 91 | }
|
60 | 92 | }
|
0 commit comments