Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion exercises/practice/hamming/.approaches/config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"introduction": {
"authors": [
"erikschierboom"
"erikschierboom",
"aage"
]
},
"approaches": [
Expand All @@ -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"
]
}
]
}
20 changes: 19 additions & 1 deletion exercises/practice/hamming/.approaches/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +1 to +8

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something to keep in mind: lines that are too long are clipped.

some measurements on my end
  • Desktop, overview page: room for 42 columns, clipped to 41 when longer.
  • Desktop, individual approach page: room for 36 columns, clipped to 35 when longer.
  • Phone, portrait, both pages: room for 30 columns, clipped to 29 when longer.
  • Phone, landscape, both pages: plenty of room.

@ErikSchierboom don't you think it would be better for the snippets to be horizontally scrollable rather than clipped?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe. Please open an issue on the forum

3 changes: 2 additions & 1 deletion exercises/practice/hamming/.meta/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"lestephane",
"robkeim",
"valentin-p",
"wolf99"
"wolf99",
"aage"
],
"files": {
"solution": [
Expand Down