Skip to content

Commit

Permalink
[New concept + exercise]: Case (#1595)
Browse files Browse the repository at this point in the history
* Add case concept + exercise

* Add new line in end of exemplar

* Fix

* Add missing spacing

* Fixes based on feedback

* remove trailing whitespace

* Add contributor to config.json file

---------

Co-authored-by: Victor Goff <[email protected]>
  • Loading branch information
meatball133 and kotp authored Nov 13, 2023
1 parent 94cde87 commit 407f3fe
Show file tree
Hide file tree
Showing 13 changed files with 862 additions and 0 deletions.
5 changes: 5 additions & 0 deletions concepts/case/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"blurb": "Case is a form of control expression, and allows for writing large readable if-else-if statements.",
"authors": ["meatball133"],
"contributors": ["kotp"]
}
129 changes: 129 additions & 0 deletions concepts/case/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Case

[Case][case] (often referred to as switch in other languages) is a form of control expression like if-else.
Case allows for chaining of multiple if-else-if statements and can be more readable while still providing flow control.

A case is defined by the keyword `case` followed by an optional expression.
Then for each case, the keyword `when` is used followed by an expression which is compared to the case expression.
The `when` keyword should not be indented from the `case` keyword.
After the `when` keyword is the code that should be executed if the case expression matches the when expression.
Case allows for an optional `else` statement which is executed if no other case matches.

The case expression is evaluated and then compared to each `when` expression.
The expression is compared using the case equality operator (`===`).

```ruby
value = 1
case value
when 1
"One"
when 2
"Two"
else
"Other"
end

# This is the same as:
value = 1
if 1 === value
"One"
elsif 2 === value
"Two"
else
"Other"
end
```

## Case equality operator (`===`)

The case equality operator (`===`) is a bit different from the equality operator (`==`).
The operator checks if the right side is a member of the set described by the left side.
This means that it does matter where each operand is placed.
How this works depends on the type of the left side, for example a `Range` would check if the right side is in the range or a `Object` would check if the right side is an instance of the `Object`.

```ruby
(1..3) == 1 # => false
(1..3) === 1 # => true

String == "foo" # => false
String === "foo" # => true
```

## Case with multiple expressions

Cases allow for matching multiple expressions in a single case with each possible value separated by a comma.
It will execute the code if any of the expressions match.
This can be useful when you want a single case to have multiple possible values.

```ruby
case var
when 1, 2
"One or two"
else
"Other"
end
```

## Cases with ranges

Cases can also check if a value is in a range.
This is done by having a range as the when expression.

```ruby
case var
when 1..3
puts "One to three"
else
puts "Other"
end
```

## Cases with no case expression

When there is no need for a case expression, it is possible to omit it.
Doing this will make it so that each case expression is evaluated for truthiness.
And makes them behave like if-else-if statements.

```ruby
case
when 1 == 1
"One is equal to one"
when 1 > 2
"One is greater than two"
else
"Other"
end
```

## Single line when

Ruby allows for single line case statements.
This can be used when you have a simple single line statement.
The single line when statement is written as `when <expression> then <statement>`.
And when used in the else statement it is written as `else <statement>`.

```ruby
case var
when 1 then "One"
when 2 then "Two"
else "Other"
end
```

## Case with types

Case allows for the matching with types.
This is useful when wanting different behavior depending on the type of a variable.

```ruby
case var
when Integer
"Integer"
when String
"String"
else
"Other"
end
```

[case]: https://www.rubyguides.com/2015/10/ruby-case/
129 changes: 129 additions & 0 deletions concepts/case/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Case

[Case][case] (often referred to as switch in other languages) is a form of control expression like if-else.
Case allows for chaining of multiple if-else-if statements and can be more readable while still providing flow control.

A case is defined by the keyword `case` followed by an optional expression.
Then for each case, the keyword `when` is used followed by an expression which is compared to the case expression.
The `when` keyword should not be indented from the `case` keyword.
After the `when` keyword is the code that should be executed if the case expression matches the when expression.
Case allows for an optional `else` statement which is executed if no other case matches.

The case expression is evaluated and then compared to each `when` expression.
The expression is compared using the case equality operator (`===`).

```ruby
value = 1
case value
when 1
"One"
when 2
"Two"
else
"Other"
end

# This is the same as:
value = 1
if 1 === value
"One"
elsif 2 === value
"Two"
else
"Other"
end
```

## Case equality operator (`===`)

The case equality operator (`===`) is a bit different from the equality operator (`==`).
The operator checks if the right side is a member of the set described by the left side.
This means that it does matter where each operand is placed.
How this works depends on the type of the left side, for example a `Range` would check if the right side is in the range or a `Object` would check if the right side is an instance of the `Object`.

```ruby
(1..3) == 1 # => false
(1..3) === 1 # => true

String == "foo" # => false
String === "foo" # => true
```

## Case with multiple expressions

Cases allow for matching multiple expressions in a single case with each possible value separated by a comma.
It will execute the code if any of the expressions match.
This can be useful when you want a single case to have multiple possible values.

```ruby
case var
when 1, 2
"One or two"
else
"Other"
end
```

## Cases with ranges

Cases can also check if a value is in a range.
This is done by having a range as the when expression.

```ruby
case var
when 1..3
puts "One to three"
else
puts "Other"
end
```

## Cases with no case expression

When there is no need for a case expression, it is possible to omit it.
Doing this will make it so that each case expression is evaluated for truthiness.
And makes them behave like if-else-if statements.

```ruby
case
when 1 == 1
"One is equal to one"
when 1 > 2
"One is greater than two"
else
"Other"
end
```

## Single line when

Ruby allows for single line case statements.
This can be used when you have a simple single line statement.
The single line when statement is written as `when <expression> then <statement>`.
And when used in the else statement it is written as `else <statement>`.

```ruby
case var
when 1 then "One"
when 2 then "Two"
else "Other"
end
```

## Case with types

Case allows for the matching with types.
This is useful when wanting different behavior depending on the type of a variable.

```ruby
case var
when Integer
"Integer"
when String
"String"
else
"Other"
end
```

[case]: https://www.rubyguides.com/2015/10/ruby-case/
6 changes: 6 additions & 0 deletions concepts/case/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"url": "https://www.rubyguides.com/2015/10/ruby-case/",
"description": "Ruby Guides: The Many Uses Of Ruby Case Statements"
}
]
17 changes: 17 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,18 @@
"symbols"
]
},
{
"slug": "blackjack",
"name": "BlackJack",
"uuid": "414ee9b2-c62d-41d5-aa1e-c5d82fb8562c",
"concepts": [
"case"
],
"prerequisites": [
"ranges",
"modules"
]
},
{
"slug": "bird-count",
"name": "Bird Count",
Expand Down Expand Up @@ -1702,6 +1714,11 @@
"slug": "modules",
"name": "Modules"
},
{
"uuid": "bcea6b47-4db8-4cce-b341-c2f97182687b",
"slug": "case",
"name": "Case"
},
{
"uuid": "ad546bc1-7583-482d-9159-44e08c50c7b8",
"slug": "exceptions",
Expand Down
28 changes: 28 additions & 0 deletions exercises/concept/blackjack/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Hints

## 1. Calculate the value of any given card.

- The `Blackjack.parse_card` method should take a string (e.g. `"ace"`) and return its value (e.g. 11).
- Define a [`case` statement][case] and assign the value of the card as the case's value.
- King, Queen, Jack and 10 can be handled with a single case.
- The `case` can have an `else` case.
In any case the function should return `0` for unknown cards.

## 2. Name ranges of values.

- Compute the player score by adding up the values of the two player cards.
- Define a [`case` statement][case] and assign the name of the range as the case's value.
- The switch can use [`Range`][range] objects as cases to check if a value is in a range.

## 3. Implement the decision logic for the first turn.

- Compute the player score by adding up the values of the two player cards.
- You can either use a big [`case` statement][case] on the player
score (maybe with nested `if`-statements on the dealer-score in some cases),
- or you could distinguish separate player score categories (say "small hands"
with a score less than 12, "medium hands" with a score in the range 12..20 and
"large hands" with a score greater than 20) and write separate functions for
all (or some) of these categories.

[case]: https://www.rubyguides.com/2015/10/ruby-case/
[range]: https://rubyapi.org/o/range
Loading

0 comments on commit 407f3fe

Please sign in to comment.