Skip to content

Commit

Permalink
raindrops: add pattern matching approach
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikSchierboom committed Jan 18, 2024
1 parent 39d35a4 commit bf13184
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 0 deletions.
18 changes: 18 additions & 0 deletions exercises/practice/raindrops/.approaches/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,24 @@
"uses:Enumerable.Aggregate"
]
}
},
{
"uuid": "9ed72d33-b8eb-4322-86a2-beab30677cb7",
"slug": "pattern-matching",
"title": "Pattern Matching",
"blurb": "Use pattern matching to find the right sound combination.",
"authors": [
"erikschierboom"
],
"tags": {
"all": [
"construct:pattern-matching"
],
"any": [
"construct:switch",
"construct:switch-expression"
]
}
}
]
}
24 changes: 24 additions & 0 deletions exercises/practice/raindrops/.approaches/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public static string Convert(int number)
}
```

This approach uses a series of `if`-statements and string concatentation to build up the result string.
For more information, check the [`if` statements approach][approach-if-statements].

## Approach: `StringBuilder`
Expand All @@ -41,6 +42,7 @@ public static string Convert(int number)
}
```

This approach uses a series of `if`-statements and a `StringBuilder` to build up the result string.
For more information, check the [`StringBuilder` approach][approach-string-builder].

## Approach: `Aggregate` LINQ method
Expand All @@ -54,8 +56,29 @@ public static string Convert(int number)
}
```

This approach uses LINQ to filter the right sounds.
For more information, check the [`Aggregate` approach][approach-aggregate].

## Approach: pattern matching

```csharp
public static string Convert(int number) =>
(number % 3, number % 5, number % 7) switch
{
(0, 0, 0) => "PlingPlangPlong",
(0, 0, _) => "PlingPlang",
(0, _, 0) => "PlingPlong",
(_, 0, 0) => "PlangPlong",
(0, _, _) => "Pling",
(_, 0, _) => "Plang",
(_, _, 0) => "Plong",
_ => number.ToString()
};
```

This approach uses pattern matching on a tuple to find the matching sounds.
For more information, check the [pattern matching approach][approach-pattern-matching].

## Which approach to use?

Although the `Aggregate` approach may be considered more "functional", it is about three times slower than the `if` statements approach.
Expand All @@ -64,3 +87,4 @@ Although the `Aggregate` approach may be considered more "functional", it is abo
[approach-if-statements]: https://exercism.org/tracks/csharp/exercises/raindrops/approaches/if-statements
[approach-aggregate]: https://exercism.org/tracks/csharp/exercises/raindrops/approaches/aggregate
[approach-string-builder]: https://exercism.org/tracks/csharp/exercises/raindrops/approaches/string-builder
[approach-pattern-matching]: https://exercism.org/tracks/csharp/exercises/raindrops/approaches/pattern-matching
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Pattern matching

```csharp
public static string Convert(int number) =>
(number % 3, number % 5, number % 7) switch
{
(0, 0, 0) => "PlingPlangPlong",
(0, 0, _) => "PlingPlang",
(0, _, 0) => "PlingPlong",
(_, 0, 0) => "PlangPlong",
(0, _, _) => "Pling",
(_, 0, _) => "Plang",
(_, _, 0) => "Plong",
_ => number.ToString()
};
```

This approach uses [pattern matching][pattern-matching] to find the _pattern_ of the matching sounds, and in particular the [positional pattern][positional-pattern].
To do so, we construct a [tuple][tuples] with three values, which are the remainder of the input number for the numbers `3`, `5` and `7`:

```csharp
(number % 3, number % 5, number % 7)
```

To see how this works, consider when the value of `number` is `15`, for which we should return `"PlingPlang"`.
As `number % 3` and `number % 5` are both equal to `0`, and `number % 7` is equal to `1`, the tuple will be

```csharp
(0, 0, 1)
```

We can then pattern match on the tuple and test its against the above pattern:

```csharp
(number % 3, number % 5, number % 7) switch
{
(0, 0, 1) => "PlingPlang",
}
```

Let's add a pattern for `"PlingPlangPlong"`, which requires the remainder to be `0` for all three values:

```csharp
(number % 3, number % 5, number % 7) switch
{
(0, 0, 1) => "PlingPlang",
(0, 0, 0) => "PlingPlangPlong",
}
```

While this works, a better option would be to reverse the order of the patterns.
As patterns are matched from top to bottom, we can rewrite this to:

```csharp
(number % 3, number % 5, number % 7) switch
{
(0, 0, 0) => "PlingPlangPlong",
(0, 0, _) => "PlingPlang",
}
```

The thing to note is that we're now using the wildcard pattern (`_`) in the second pattern, as we don't really care for its value, as long as it's not `0`, and we can now guarantee that as otherwise the first pattern would have matched.

It's now easy to add the remaining patterns:

```csharp
(number % 3, number % 5, number % 7) switch
{
(0, 0, 0) => "PlingPlangPlong",
(0, 0, _) => "PlingPlang",
(0, _, 0) => "PlingPlong",
(_, 0, 0) => "PlangPlong",
(0, _, _) => "Pling",
(_, 0, _) => "Plang",
(_, _, 0) => "Plong",
_ => number.ToString()
}
```

The last pattern is the wildcard pattern again, but this time we're saying that we don't care about the actual values of the entire tuple, not just one or two specific values.
In that case, we'll return the string version of the number.

[pattern-matching]: https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching
[positional-pattern]: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#positional-pattern
[tuples]: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(number % 3, number % 5, number % 7) switch
{
(0, 0, 0) => "PlingPlangPlong",
(0, 0, _) => "PlingPlang",
(_, 0, _) => "Plang",
(_, _, 0) => "Plong",
_ => number.ToString()
};

0 comments on commit bf13184

Please sign in to comment.