-
-
Notifications
You must be signed in to change notification settings - Fork 112
Add Flags Attribute concept exercise #1387
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
blackk-foxx
wants to merge
6
commits into
exercism:main
Choose a base branch
from
blackk-foxx:feature/flags-enum-concept
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
1103977
Add Flags Attribute concept exercise
blackk-foxx a80fd1a
Add author to config.json; add missing hints.md
blackk-foxx 08b80ff
Use existing flag-discriminated-unions slug
blackk-foxx cec664d
Correct formatting
blackk-foxx 8a197fc
Add exercise ref to Exercises.slnx
blackk-foxx e19d68e
Remove incorrect assertions in docs
blackk-foxx File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "blurb": "The Flags attribute allows the values defined in a discriminated union to be represented as bit positions (i.e. flags).", | ||
| "authors": ["blackk-foxx"], | ||
| "contributors": ["ErikSchierboom"] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # About | ||
|
|
||
| The Flags attribute allows the values defined in a discriminated union to be represented as bit positions (i.e. flags). | ||
| As flags, such values can be combined, making it possible for multiple boolean conditions to be represented in a single value. | ||
|
|
||
| ```fsharp | ||
| [<Flags>] | ||
| type PhoneFeatures = | ||
| | Call = 1 | ||
| | Text = 2 | ||
| ``` | ||
|
|
||
| ```fsharp | ||
| [<Flags>] | ||
| type PhoneFeaturesBinary = | ||
| | Call = 0b00000001 | ||
| | Text = 0b00000010 | ||
| ``` | ||
|
|
||
| Setting a flag can be done with the bitwise OR operator (`|||`); unsetting a flag can be done with a combination of the bitwise AND operator (`&&&`) and the bitwise negation operator (`~~~`). | ||
| While checking flag's state can be done with the bitwise AND operator, one can also use the HasFlag() method. | ||
|
|
||
| ```fsharp | ||
| let features = PhoneFeatures.Call | ||
|
|
||
| // Set the Text flag | ||
| let moreFeatures = features ||| PhoneFeatures.Text | ||
|
|
||
| moreFeatures.HasFlag(PhoneFeatures.Call) // => true | ||
| moreFeatures.HasFlag(PhoneFeatures.Text) // => true | ||
|
|
||
| // Unset the Call flag | ||
| let lessFeatures = features &&& ~~~PhoneFeatures.Call | ||
|
|
||
| lessFeatures.HasFlag(PhoneFeatures.Call) // => false | ||
| lessFeatures.HasFlag(PhoneFeatures.Text) // => true | ||
| ``` | ||
|
|
||
| See [Summary of Bitwise Operators][bitwise-operators] for a complete list of the bitwise operators available in the F# language. | ||
|
|
||
| [bitwise-operators]: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/symbol-and-operator-reference/bitwise-operators#summary-of-bitwise-operators |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # Introduction | ||
|
|
||
| A common way to use discriminated union type in F# is to represent a fixed set of named constants -- a structure called an "enum" in other languages. | ||
|
|
||
| Normally, in such a discriminated union, each case can only refer to exactly one of those named constants. | ||
| However, sometimes it is useful to refer to more than one constant. | ||
| To do so, one can annotate the discriminated union with the Flags attribute. | ||
|
|
||
| A discriminated union with the Flags attribute can be defined as follows (using binary integer notation 0b): | ||
|
|
||
| ```fsharp | ||
| [<Flags>] | ||
| type PhoneFeatures = | ||
| | Call = 0b00000001 | ||
| | Text = 0b00000010 | ||
| ``` | ||
|
|
||
| A `PhoneFeatures` instance with the value 0b00000011 has both its Call and Text flags set. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| [ | ||
| { | ||
| "url": "https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-flagsattribute", | ||
| "description": "Flags attribute" | ||
| } | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
37 changes: 37 additions & 0 deletions
37
exercises/concept/improved-password-checker/.docs/instructions.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| The goal of this exercise is to improve upon the Password Checker exercise. | ||
| Since a given password will likely violate more than one rule at a time, a useful password checker ought to communicate to the user all the rules that are violated, instead of just the first one that happens to be discovered. | ||
| The improved password checker should indicate all of the rules being violated by a given password in one go. | ||
|
|
||
| The rules for this password checker are the same as in the previous Password Checker exercise: | ||
|
|
||
| - Must have 12 or more characters | ||
| - Must have at least one uppercase letter | ||
| - Must have at least one lowercase letter | ||
| - Must have at least one digit | ||
| - Must have at least one symbol in the set !@#$%^&\* | ||
|
|
||
| Your solution must use a `Result` to encapsulate the success or failure status. | ||
| For the success case, the `Result` must convey the validated password as a string. | ||
| For the failure case, the `Result` must indicate all of the violated rules. | ||
|
|
||
| ## 1. Modify the `PasswordError` discriminated union to allow the individual values to be treated as flags | ||
|
|
||
| Note that the tests will not compile until this essential step is complete. | ||
|
|
||
| ## 2. Implement the `checkPassword` function | ||
|
|
||
| The `checkPassword` function checks the given password against the aforementioned rules. On failure, it indicates the rule(s) that was/were violated by encapsulating one or more of the `PasswordError` values within the result value. | ||
|
|
||
| ```fsharp | ||
| checkPassword "abcdefghijk5" | ||
| // => Error (PasswordError.MissingUppercaseLetter ||| PasswordError.MissingSymbol) | ||
| ``` | ||
|
|
||
| ## 3. Implement the ``getStatusPhrases` function | ||
|
|
||
| The `getStatusPhrases` function returns a set of strings each containing a human-readable phrase corresponding to one of the erorrs in the result returned from `checkPassword`. | ||
|
|
||
| ```fsharp | ||
| getStatusPhrases (Error PasswordError.MissingDigit ||| PasswordError.LessThan12Characters) | ||
| // => Set ["12 characters"; "digit"] | ||
| ``` |
41 changes: 41 additions & 0 deletions
41
exercises/concept/improved-password-checker/.docs/introduction.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # Introduction | ||
|
|
||
| The Flags attribute allows the values defined in a discriminated union to be represented as bit positions (i.e. flags). | ||
| As flags, such values can be combined, making it possible for multiple boolean conditions to be represented in a single value. | ||
|
|
||
| ```fsharp | ||
| [<Flags>] | ||
| type PhoneFeatures = | ||
| | Call = 1 | ||
| | Text = 2 | ||
| ``` | ||
|
|
||
| ```fsharp | ||
| [<Flags>] | ||
| type PhoneFeaturesBinary = | ||
| | Call = 0b00000001 | ||
| | Text = 0b00000010 | ||
| ``` | ||
|
|
||
| Setting a flag can be done with the bitwise OR operator (`|||`); unsetting a flag can be done with a combination of the bitwise AND operator (`&&&`) and the bitwise negation operator (`~~~`). | ||
| While checking flag's state can be done with the bitwise AND operator, one can also use the HasFlag() method. | ||
|
|
||
| ```fsharp | ||
| let features = PhoneFeatures.Call | ||
|
|
||
| // Set the Text flag | ||
| let moreFeatures = features ||| PhoneFeatures.Text | ||
|
|
||
| moreFeatures.HasFlag(PhoneFeatures.Call) // => true | ||
| moreFeatures.HasFlag(PhoneFeatures.Text) // => true | ||
|
|
||
| // Unset the Call flag | ||
| let lessFeatures = features &&& ~~~PhoneFeatures.Call | ||
|
|
||
| lessFeatures.HasFlag(PhoneFeatures.Call) // => false | ||
| lessFeatures.HasFlag(PhoneFeatures.Text) // => true | ||
| ``` | ||
|
|
||
| See [Summary of Bitwise Operators][bitwise-operators] for a complete list of the bitwise operators available in the F# language. | ||
|
|
||
| [bitwise-operators]: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/symbol-and-operator-reference/bitwise-operators#summary-of-bitwise-operators |
55 changes: 55 additions & 0 deletions
55
exercises/concept/improved-password-checker/.meta/Exemplar.fs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| module ImprovedPasswordChecker | ||
|
|
||
| open System | ||
|
|
||
| [<Flags>] | ||
| type PasswordError = | ||
| | LessThan12Characters = 1 | ||
| | MissingUppercaseLetter = 2 | ||
| | MissingLowercaseLetter = 4 | ||
| | MissingDigit = 8 | ||
| | MissingSymbol = 16 | ||
|
|
||
| /// Validate the given password against the rules defined in the instructions. If it meets all | ||
| /// of the rules, return a result indicating success; otherwise return a result indicating | ||
| /// failure with an error value indicating all of the rules that were violated. | ||
| let checkPassword (password: string) : Result<string, PasswordError> = | ||
| let mutable errors = ( | ||
| PasswordError.LessThan12Characters ||| | ||
| PasswordError.MissingUppercaseLetter ||| | ||
| PasswordError.MissingLowercaseLetter ||| | ||
| PasswordError.MissingDigit ||| | ||
| PasswordError.MissingSymbol | ||
| ) | ||
| if password.Length >= 12 then | ||
| errors <- errors &&& ~~~PasswordError.LessThan12Characters | ||
| for (charTest, flag) in [ | ||
| (System.Char.IsUpper, PasswordError.MissingUppercaseLetter); | ||
| (System.Char.IsLower, PasswordError.MissingLowercaseLetter); | ||
| (System.Char.IsDigit, PasswordError.MissingDigit); | ||
| ((fun c -> "!@#$%^&*".Contains c), PasswordError.MissingSymbol) | ||
| ] do | ||
| if password |> String.exists charTest then | ||
| errors <- errors &&& ~~~flag | ||
|
|
||
| if int errors = 0 then | ||
| Ok password | ||
| else | ||
| Error errors | ||
|
|
||
| /// Return a set of human-readable phrases indicating the meaning of the given result value. | ||
| let getStatusPhrases (result: Result<string, PasswordError>) : Set<string> = | ||
| let mutable phrases: Set<string> = Set [ ] | ||
| match result with | ||
| | Error errors -> | ||
| for (flag, phrase) in [ | ||
| (PasswordError.LessThan12Characters, "12 characters"); | ||
| (PasswordError.MissingUppercaseLetter, "uppercase letter"); | ||
| (PasswordError.MissingLowercaseLetter, "lowercase letter"); | ||
| (PasswordError.MissingDigit, "digit"); | ||
| (PasswordError.MissingSymbol, "symbol") | ||
| ] do | ||
| if errors.HasFlag(flag) then | ||
| phrases <- phrases.Add(phrase) | ||
| | Ok _ -> () | ||
| phrases |
17 changes: 17 additions & 0 deletions
17
exercises/concept/improved-password-checker/.meta/config.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| { | ||
| "authors": [ | ||
| "blackk-foxx" | ||
| ], | ||
| "files": { | ||
| "solution": [ | ||
| "ImprovedPasswordChecker.fs" | ||
| ], | ||
| "test": [ | ||
| "ImprovedPasswordCheckerTests.fs" | ||
| ], | ||
| "exemplar": [ | ||
| ".meta/Exemplar.fs" | ||
| ] | ||
| }, | ||
| "blurb": "Learn how to use the Flags attribute to combine values within a discriminated union" | ||
| } |
18 changes: 18 additions & 0 deletions
18
exercises/concept/improved-password-checker/ImprovedPasswordChecker.fs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| module ImprovedPasswordChecker | ||
|
|
||
| type PasswordError = | ||
| | LessThan12Characters | ||
| | MissingUppercaseLetter | ||
| | MissingLowercaseLetter | ||
| | MissingDigit | ||
| | MissingSymbol | ||
|
|
||
| /// Validate the given password against the rules defined in the instructions. If it meets all | ||
| /// of the rules, return a result indicating success; otherwise return a result indicating | ||
| /// failure with an error value indicating all of the rules that were violated. | ||
| let checkPassword (password: string) : Result<string, PasswordError> = | ||
| failwith "Please implement this function" | ||
|
|
||
| /// Return a set of human-readable phrases indicating the meaning of the given result value. | ||
| let getStatusPhrases (result: Result<string, PasswordError>) : Set<string> = | ||
| failwith "Please implement this function" | ||
24 changes: 24 additions & 0 deletions
24
exercises/concept/improved-password-checker/ImprovedPasswordChecker.fsproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net10.0</TargetFramework> | ||
| <RootNamespace>Exercism</RootNamespace> | ||
|
|
||
| <IsPackable>false</IsPackable> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Compile Include="ImprovedPasswordChecker.fs" /> | ||
| <Compile Include="ImprovedPasswordCheckerTests.fs" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" /> | ||
| <PackageReference Include="Newtonsoft.Json" Version="13.0.4" /> | ||
| <PackageReference Include="xunit.v3" Version="3.2.2" /> | ||
| <PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" /> | ||
| <PackageReference Include="FsUnit.xUnit" Version="7.1.1" /> | ||
| <PackageReference Include="Exercism.Tests.xunit.v3" Version="0.1.0-beta1" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.