diff --git a/exercises/practice/hamming/.approaches/config.json b/exercises/practice/hamming/.approaches/config.json index 81676ad20..22b155ccc 100644 --- a/exercises/practice/hamming/.approaches/config.json +++ b/exercises/practice/hamming/.approaches/config.json @@ -1,7 +1,8 @@ { "introduction": { "authors": [ - "erikschierboom" + "erikschierboom", + "aage" ] }, "approaches": [ @@ -22,6 +23,15 @@ "authors": [ "erikschierboom" ] + }, + { + "uuid": "916c9769-a5aa-4188-b856-d6176921906f", + "slug": "list-comprehension", + "title": "List comprehension", + "blurb": "Use a list comprehension to incrementally calculate the distance", + "authors": [ + "aage" + ] } ] } diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md index 5d6b34fc2..313f3ab5f 100644 --- a/exercises/practice/hamming/.approaches/introduction.md +++ b/exercises/practice/hamming/.approaches/introduction.md @@ -36,11 +36,29 @@ let distance (strand1: string) (strand2: string): int option = This approach uses recursion to process the two strings' characters and calculate the hamming distance. For more information, check the [recursion approach][approach-recursion]. +## Approach: list comprehension + +```fsharp +let distance (strand1: string) (strand2: string) : int option = + + if strand1.Length <> strand2.Length + then None + else + [ for idx in 0 .. strand1.Length - 1 do + if strand1[idx] <> strand2[idx] then yield 1 else yield 0 ] + |> List.sum + |> Some +``` + +This approach uses a list comprehension to process the two strings' characters and calculate the hamming distance. +For more information, check the [list comprehension approach][approach-list-comprehension]. + ## Which approach to use? -Both approaches are equally valid, although the recursion one is more verbose, so which one to choose is basically up to personal preference. +All approaches are equally valid, although the recursion one is more verbose, so which one to choose is basically up to personal preference. [approach-recursion]: https://exercism.org/tracks/fsharp/exercises/hamming/approaches/recursion [approach-zip]: https://exercism.org/tracks/fsharp/exercises/hamming/approaches/zip +[approach-list-comprehension]: https://exercism.org/tracks/fsharp/exercises/hamming/approaches/list-comprehension [options]: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/options [seq.zip]: https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-seqmodule.html#zip diff --git a/exercises/practice/hamming/.approaches/list-comprehension/content.md b/exercises/practice/hamming/.approaches/list-comprehension/content.md new file mode 100644 index 000000000..2b2512c9b --- /dev/null +++ b/exercises/practice/hamming/.approaches/list-comprehension/content.md @@ -0,0 +1,59 @@ +# List comprehension + +```fsharp +module Hamming + +let distance (strand1: string) (strand2: string) : int option = + + if strand1.Length <> strand2.Length + then None + else + [ for idx in 0 .. strand1.Length - 1 do + if strand1[idx] <> strand2[idx] then yield 1 else yield 0 ] + |> List.sum + |> Some +``` + +## Error path + +We start by checking if the strings have unequal lengths, and return `None` if so: + +```fsharp +if strand1.Length <> strand2.Length +then None +``` + +```exercism/note +Note that we're using `string` class' `Length` property, not a function like `Seq.length`. +Even though F# is a functional-first language, you'll use types (like the `string` class) defined in the .NET framework, which is an object-oriented framework. +Inevitably, you'll thus use objects that have methods and properties defined on them. +Don't worry about using methods and objects though, F# is a multi-paradigm language and embraces the interaction with object-oriented code (like the `string` class). +``` + +## Happy path + +In the happy path, we know that the strings have the same length so we can use the length (minus one) of the first string as the max of a range of _indices_ to use to access each `char` of both `string`s and compare them: + +```fsharp +for idx in 0 .. strand1.Length - 1 do +``` + +The entire `for` expression is surrounded by square brackets (`[]`) indicating that this is a [List comprehension][list-comprehension]. +This gives you the power of returning intermediate results based on comparing each pair of `char`s (returning a `1` when they differ or a `0` if they don't) and then continuing the next pair until you reach the end: + +```fsharp +if strand1[idx] <> strand2[idx] then yield 1 else yield 0 +``` + +The `yield` keyword indicates that this concerns an intermediate result, this can also be used in [C#][yield-return]. + +The resulting list of `1`'s and `0`'s is then _piped_ into a [List.sum][list.sum] to get the hamming distance and finally the result is wrapped in a `Some`. + +```fsharp +|> List.sum +|> Some +``` + +[list-comprehension]: https://en.wikibooks.org/wiki/F_Sharp_Programming/Lists#Using_List_Comprehensions +[list.sum]: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/lists#arithmetic-operations-on-lists +[yield-return]: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/yield \ No newline at end of file diff --git a/exercises/practice/hamming/.approaches/list-comprehension/snippet.txt b/exercises/practice/hamming/.approaches/list-comprehension/snippet.txt new file mode 100644 index 000000000..8f3c573ab --- /dev/null +++ b/exercises/practice/hamming/.approaches/list-comprehension/snippet.txt @@ -0,0 +1,8 @@ +let distance (strand1: string) (strand2: string) : int option = + if strand1.Length <> strand2.Length + then None + else + [ for idx in 0 .. strand1.Length - 1 do + if strand1[idx] <> strand2[idx] then yield 1 else yield 0 ] + |> List.sum + |> Some \ No newline at end of file diff --git a/exercises/practice/hamming/.meta/config.json b/exercises/practice/hamming/.meta/config.json index 706316132..05b2c5700 100644 --- a/exercises/practice/hamming/.meta/config.json +++ b/exercises/practice/hamming/.meta/config.json @@ -8,7 +8,8 @@ "lestephane", "robkeim", "valentin-p", - "wolf99" + "wolf99", + "aage" ], "files": { "solution": [