diff --git a/concepts/comparison-operators/about.md b/concepts/comparison-operators/about.md index 319df12ba..3e97c3f0d 100644 --- a/concepts/comparison-operators/about.md +++ b/concepts/comparison-operators/about.md @@ -1,35 +1,66 @@ # Comparison Operators -PHP has ten built in comparison operators: - -| Name | Example | Result | -| ----- | ---------- | -------------------------------------------------- | -| Equal | `$a == $b` | true if `$a` is equal to `$b` after type juggling. | -| Identical | `$a === $b` | true if `$a` is equal to `$b` and the same type. | -| Not Equal | `$a != $b` | true if `$a` is not equal to `$b` after type juggling. | -| Not Equal | `$a <> $b` | true if `$a` is not equal to `$b` after type juggling. | -| Not identical | `$a !== $b` | true if `$a` is not equal to `$b` or not of the same type. | -| Less than | `$a < $b` | true if `$a` is strictly less than `$b`. | -| Greater than | `$a > $b` | true if `$a` is strictly greater than `$b`. | -| Less than or equal to | `$a <= $b` | true if `$a` is less than or equal to `$b`. | -| Greater than or equal to | `$a >= $b` | true if `$a` is greater than or equal to `$b`. | -| Spaceship | `$a <=> $b` | returns an integer less than, equal to, or greater than `0`i when `$a`. | - -PHP has distinct definitions that differentiate between `equal` and `identical`. -Sometimes `identical` is also referred to as `strictly equal`. +Comparison operators compare two values. +Most of them return a boolean (`true` or `false`). +The spaceship operator (`<=>`) is different: it returns `-1`, `0`, or `1`. + +PHP has ten built-in comparison operators: + +| Name | Example | Result | +| --- | --- | --- | +| Equal | `$a == $b` | `true` if `$a` is equal to `$b` after type juggling | +| Identical | `$a === $b` | `true` if `$a` is equal to `$b` and the same type | +| Not Equal | `$a != $b` | `true` if `$a` is not equal to `$b` after type juggling | +| Not Equal | `$a <> $b` | `true` if `$a` is not equal to `$b` after type juggling | +| Not identical | `$a !== $b` | `true` if `$a` is not equal to `$b` or not the same type | +| Less than | `$a < $b` | `true` if `$a` is strictly less than `$b` | +| Greater than | `$a > $b` | `true` if `$a` is strictly greater than `$b` | +| Less than or equal to | `$a <= $b` | `true` if `$a` is less than or equal to `$b` | +| Greater than or equal to | `$a >= $b` | `true` if `$a` is greater than or equal to `$b` | +| Spaceship | `$a <=> $b` | `-1`, `0`, or `1` when `$a` is less than, equal to, or greater than `$b` | + +## Equal and identical + +PHP distinguishes between **equal** (`==`) and **identical** (`===`). +Identical is also called **strictly equal**. + +With `==`, PHP may change the type of a value before comparing (type juggling). +With `===`, both the value and the type must match. ```php true, equal +1 == "1"; // => true, equal after implicit conversion of string to int 1 === "1"; // => false, not identical -// Comparisons between integers and floating point values -1 == 1.0; // => true +1 == 1.0; // => true, equal after implicit conversion of int to float 1 === 1.0; // => false +``` + +The same idea applies to "not equal" (`!=` / `<>`) versus "not identical" (`!==`). + +## Comparing objects + +For objects, `==` checks whether properties are equal. +`===` checks whether both sides refer to the same instance. + +```php + true +new stdClass() === new stdClass(); // => false +``` + +## The spaceship operator -// Comparisons between object instances -new stdClass() == new stdClass(); // => true, properties are equal -new stdClass() === new stdClass(); // => false, references are not identical +`$a <=> $b` returns: + +```php + 2; // => -1 +1 <=> 1; // => 0 +2 <=> 1; // => 1 ``` + +This is useful when you need an ordering result, not only `true` or `false`. diff --git a/concepts/comparison-operators/introduction.md b/concepts/comparison-operators/introduction.md index 601e43d0e..2f61a1785 100644 --- a/concepts/comparison-operators/introduction.md +++ b/concepts/comparison-operators/introduction.md @@ -1,36 +1,59 @@ # Comparison Operators -PHP has ten built in comparison operators: - -| Name | Example | Result | -| ----- | ---------- | -------------------------------------------------- | -| Equal | `$a == $b` | true if `$a` is equal to `$b` after type juggling. | -| Identical | `$a === $b` | true if `$a` is equal to `$b` and the same type. | -| Not Equal | `$a != $b` | true if `$a` is not equal to `$b` after type juggling. | -| Not Equal | `$a <> $b` | true if `$a` is not equal to `$b` after type juggling. | -| Not identical | `$a !== $b` | true if `$a` is not equal to `$b` or not of the same type. | -| Less than | `$a < $b` | true if `$a` is strictly less than `$b`. | -| Greater than | `$a > $b` | true if `$a` is strictly greater than `$b`. | -| Less than or equal to | `$a <= $b` | true if `$a` is less than or equal to `$b`. | -| Greater than or equal to | `$a >= $b` | true if `$a` is greater than or equal to `$b`. | -| Spaceship | `$a <=> $b` | returns an integer less than, equal to, or greater than `0`i when `$a`. | - -PHP has distinct definitions that differentiate between `equal` and `identical`. -Sometimes `identical` is also referred to as `strictly equal`. +Comparison operators compare two values and usually return a boolean (`true` or `false`). +They are commonly used to make decisions in code. + +PHP has **identical** comparisons and relational comparisons between numbers: ```php true, equal -1 === "1"; // => false, not identical +5 === 5; // => true +5 !== 0; // => true +3 < 7; // => true +9 > 2; // => true +5 <= 5; // => true +4 >= 8; // => false +``` + +| Operator | Meaning | +| --- | --- | +| `$a === $b` | identical: equal and the same type | +| `$a !== $b` | not identical: different type or not equal | +| `$a < $b` | less than | +| `$a > $b` | greater than | +| `$a <= $b` | less than or equal to | +| `$a >= $b` | greater than or equal to | + +## The spaceship operator -// Comparisons between integers and floating point values -1 == 1.0; // => true -1 === 1.0; // => false +The spaceship operator (`<=>`) also compares two values, but it returns an integer instead of a boolean: + +- `-1` when the left value is less than the right value +- `0` when both values are equal +- `1` when the left value is greater than the right value + +```php + true, properties are equal -new stdClass() === new stdClass(); // => false, references are not identical +3 <=> 7; // => -1 +5 <=> 5; // => 0 +9 <=> 2; // => 1 ``` +## Using comparisons in an `if` statement + +A comparison can be used as the condition of an `if` statement. +If the comparison is `true`, the code inside the braces runs: + +```php += 5) { + return 0.9; +} +``` diff --git a/config.json b/config.json index 670d309ad..2468989f0 100644 --- a/config.json +++ b/config.json @@ -82,6 +82,20 @@ ], "status": "beta" }, + { + "slug": "cars-assemble", + "name": "Cars, Assemble!", + "uuid": "4949e5b9-c685-4380-9d70-46ae970035cf", + "concepts": [ + "comparison-operators" + ], + "prerequisites": [ + "booleans", + "integers", + "floating-point-numbers" + ], + "status": "beta" + }, { "slug": "windowing-system", "name": "Windowing System", diff --git a/exercises/concept/cars-assemble/.docs/hints.md b/exercises/concept/cars-assemble/.docs/hints.md new file mode 100644 index 000000000..9d3bce497 --- /dev/null +++ b/exercises/concept/cars-assemble/.docs/hints.md @@ -0,0 +1,29 @@ +# Hints + +## General + +- The introduction covers the comparison operators you need for this exercise. +- You may also review the [comparison operators][comparison-operators] documentation. + +## 1. Calculate the success rate + +- Compare `$speed` with the boundary values from the table. +- Use `identity`, `greater than`, `greater or equal`, `lower than`, `lower or equal` operators as needed. +- You can use an `if` statement to return the matching success rate. + +## 2. Calculate the production rate per hour + +- Reuse the `successRate()` method you wrote earlier: `$this->successRate($speed)`. +- Multiply `221`, `$speed`, and the success rate. +- PHP can multiply integers and floating-point numbers together; the result may be a float. + +## 3. Check whether the line is running + +- Use a not-identical comparison (`!==`) against `0`. +- Return the boolean result of that comparison. + +## 4. Compare two line speeds + +- The spaceship operator (`<=>`) returns `-1`, `0`, or `1` directly. + +[comparison-operators]: https://www.php.net/manual/en/language.operators.comparison.php diff --git a/exercises/concept/cars-assemble/.docs/instructions.md b/exercises/concept/cars-assemble/.docs/instructions.md new file mode 100644 index 000000000..bd2d4b21f --- /dev/null +++ b/exercises/concept/cars-assemble/.docs/instructions.md @@ -0,0 +1,82 @@ +# Instructions + +In this exercise you'll write code to analyze the production of an assembly line in a car factory. +The assembly line's speed can range from `0` (off) to `10` (maximum). + +At its lowest speed (`1`), `221` cars are produced each hour. +The production increases linearly with the speed. +So with the speed set to `4`, the line should produce `4 * 221 = 884` cars per hour. +However, higher speeds increase the likelihood that faulty cars are produced, which then have to be discarded. + +You have four tasks. + +## 1. Calculate the success rate + +Implement the `successRate()` method to calculate the ratio of cars created without error for a given speed. +Use comparisons to choose the correct rate from this table: + +- `0`: 0% success rate (`0.0`) +- `1` to `4`: 100% success rate (`1.0`) +- `5` to `8`: 90% success rate (`0.9`) +- `9`: 80% success rate (`0.8`) +- `10`: 77% success rate (`0.77`) + +```php +successRate(10); +// => 0.77 +``` + +## 2. Calculate the production rate per hour + +Implement the `productionRatePerHour()` method to calculate how many cars are successfully produced per hour. +Multiply the base production (`221` cars per hour at speed `1`) by the speed and by the success rate for that speed. + +```php +productionRatePerHour(6); +// => 1193.4 +``` + +## 3. Check whether the line is running + +Implement the `isLineRunning()` method to return whether the assembly line is running. +The line is running when its speed is **not identical** to the `off` speed (`0`). + +```php +isLineRunning(0); +// => false + +$assemblyLine->isLineRunning(3); +// => true +``` + +## 4. Compare two line speeds + +Implement the `compareSpeeds()` method to compare two speeds. +It should return: + +- `-1` when the first speed is less than the second +- `0` when both speeds are equal +- `1` when the first speed is greater than the second + +```php +compareSpeeds(3, 7); +// => -1 + +$assemblyLine->compareSpeeds(5, 5); +// => 0 + +$assemblyLine->compareSpeeds(9, 2); +// => 1 +``` diff --git a/exercises/concept/cars-assemble/.docs/introduction.md b/exercises/concept/cars-assemble/.docs/introduction.md new file mode 100644 index 000000000..5192ccc83 --- /dev/null +++ b/exercises/concept/cars-assemble/.docs/introduction.md @@ -0,0 +1,61 @@ +# Introduction + +## Comparison Operators + +Comparison operators compare two values and usually return a boolean (`true` or `false`). +They are commonly used to make decisions in code. + +For learning PHP, start with **identical** comparisons and relational comparisons between numbers: + +```php + true +5 !== 0; // => true +3 < 7; // => true +9 > 2; // => true +5 <= 5; // => true +4 >= 8; // => false +``` + +| Operator | Meaning | +| --- | --- | +| `$a === $b` | identical: equal and the same type | +| `$a !== $b` | not identical | +| `$a < $b` | less than | +| `$a > $b` | greater than | +| `$a <= $b` | less than or equal to | +| `$a >= $b` | greater than or equal to | + +### The spaceship operator + +The spaceship operator (`<=>`) also compares two values, but it returns an integer instead of a boolean: + +- `-1` when the left value is less than the right value +- `0` when both values are equal +- `1` when the left value is greater than the right value + +```php + 7; // => -1 +5 <=> 5; // => 0 +9 <=> 2; // => 1 +``` + +### Using comparisons in an `if` statement + +A comparison can be used as the condition of an `if` statement. +If the comparison is `true`, the code inside the braces runs: + +```php += 5) { + return 0.9; +} +``` diff --git a/exercises/concept/cars-assemble/.docs/introduction.md.tpl b/exercises/concept/cars-assemble/.docs/introduction.md.tpl new file mode 100644 index 000000000..1a3e7c8f8 --- /dev/null +++ b/exercises/concept/cars-assemble/.docs/introduction.md.tpl @@ -0,0 +1,3 @@ +# Introduction + +%{concept:comparison-operators} diff --git a/exercises/concept/cars-assemble/.meta/config.json b/exercises/concept/cars-assemble/.meta/config.json new file mode 100644 index 000000000..b377f15cc --- /dev/null +++ b/exercises/concept/cars-assemble/.meta/config.json @@ -0,0 +1,21 @@ +{ + "authors": [ + "pablo-miralles" + ], + "files": { + "solution": [ + "CarsAssemble.php" + ], + "test": [ + "CarsAssembleTest.php" + ], + "exemplar": [ + ".meta/exemplar.php" + ] + }, + "forked_from": [ + "csharp/cars-assemble" + ], + "icon": "cars-assemble", + "blurb": "Learn about comparison operators by analyzing a car assembly line!" +} diff --git a/exercises/concept/cars-assemble/.meta/design.md b/exercises/concept/cars-assemble/.meta/design.md new file mode 100644 index 000000000..3c42ccfba --- /dev/null +++ b/exercises/concept/cars-assemble/.meta/design.md @@ -0,0 +1,47 @@ +# Design + +## Goal + +Teach students how to compare numbers with PHP comparison operators, using a small factory production story. + +## Learning objectives + +- Know how to compare numbers with `===`, `!==`, `<`, `>`, `<=`, and `>=`. +- Know how to use the spaceship operator `<=>`. +- Know that most comparison operators return a boolean, while `<=>` returns `-1`, `0`, or `1`. + +## Out of scope + +- Teaching `if` / `elseif` / `else` in depth (`if` is hand-waving only). +- `switch` / `match` / ternary. +- Type declarations and `declare(strict_types=1)`. +- Explicit type casting / type juggling (`(int)`, `intval()`, loose `==`). +- Class constants and `self::`. +- Object comparison. + +## Concepts + +- `comparison-operators` + +## Prerequisites + +- `booleans` +- `integers` +- `floating-point-numbers` + +Arithmetic multiplication is assumed from earlier exercises. + +## Tasks and operators practiced + +1. `successRate($speed)`: `===`, `>=`, `>` (or equivalent range comparisons) with simple `if` hand-waving. +2. `productionRatePerHour($speed)`: reuse `successRate`; multiply with `221` (no casting). +3. `isLineRunning($speed)`: `!==`. +4. `compareSpeeds($left, $right)`: `<=>`. + +## Analyzer + +Possible future rules: + +- `actionable`: suggest reusing `successRate` inside `productionRatePerHour`. +- `informative`: if `compareSpeeds` uses nested `if` instead of `<=>`, suggest the spaceship operator. +- `informative`: if `isLineRunning` wraps a comparison in unnecessary `if` / `return true/false`, suggest returning the comparison directly. diff --git a/exercises/concept/cars-assemble/.meta/exemplar.php b/exercises/concept/cars-assemble/.meta/exemplar.php new file mode 100644 index 000000000..196292492 --- /dev/null +++ b/exercises/concept/cars-assemble/.meta/exemplar.php @@ -0,0 +1,40 @@ += 5) { + return 0.9; + } + + if ($speed > 0) { + return 1.0; + } + + return 0.0; + } + + public function productionRatePerHour($speed) + { + return 221 * $speed * $this->successRate($speed); + } + + public function isLineRunning($speed) + { + return $speed !== 0; + } + + public function compareSpeeds($left, $right) + { + return $left <=> $right; + } +} diff --git a/exercises/concept/cars-assemble/CarsAssemble.php b/exercises/concept/cars-assemble/CarsAssemble.php new file mode 100644 index 000000000..cb10ca681 --- /dev/null +++ b/exercises/concept/cars-assemble/CarsAssemble.php @@ -0,0 +1,24 @@ +successRate(0); + $this->assertEqualsWithDelta(0.0, $actual, 0.001); + } + + /** + * @task_id 1 + */ + #[TestDox('Success rate for speed 1')] + public function testSuccessRateForSpeedOne() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->successRate(1); + $this->assertEqualsWithDelta(1.0, $actual, 0.001); + } + + /** + * @task_id 1 + */ + #[TestDox('Success rate for speed 4')] + public function testSuccessRateForSpeedFour() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->successRate(4); + $this->assertEqualsWithDelta(1.0, $actual, 0.001); + } + + /** + * @task_id 1 + */ + #[TestDox('Success rate for speed 5')] + public function testSuccessRateForSpeedFive() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->successRate(5); + $this->assertEqualsWithDelta(0.9, $actual, 0.001); + } + + /** + * @task_id 1 + */ + #[TestDox('Success rate for speed 8')] + public function testSuccessRateForSpeedEight() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->successRate(8); + $this->assertEqualsWithDelta(0.9, $actual, 0.001); + } + + /** + * @task_id 1 + */ + #[TestDox('Success rate for speed 9')] + public function testSuccessRateForSpeedNine() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->successRate(9); + $this->assertEqualsWithDelta(0.8, $actual, 0.001); + } + + /** + * @task_id 1 + */ + #[TestDox('Success rate for speed 10')] + public function testSuccessRateForSpeedTen() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->successRate(10); + $this->assertEqualsWithDelta(0.77, $actual, 0.001); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for "off" (speed 0)')] + public function testProductionRatePerHourForSpeedZero() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->productionRatePerHour(0); + $this->assertEqualsWithDelta(0.0, $actual, 0.001); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for speed 1')] + public function testProductionRatePerHourForSpeedOne() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->productionRatePerHour(1); + $this->assertEqualsWithDelta(221.0, $actual, 0.001); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for speed 4')] + public function testProductionRatePerHourForSpeedFour() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->productionRatePerHour(4); + $this->assertEqualsWithDelta(884.0, $actual, 0.001); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for speed 6')] + public function testProductionRatePerHourForSpeedSix() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->productionRatePerHour(6); + $this->assertEqualsWithDelta(1193.4, $actual, 0.001); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for speed 9')] + public function testProductionRatePerHourForSpeedNine() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->productionRatePerHour(9); + $this->assertEqualsWithDelta(1591.2, $actual, 0.001); + } + + /** + * @task_id 2 + */ + #[TestDox('Production rate per hour for speed 10')] + public function testProductionRatePerHourForSpeedTen() + { + $assemblyLine = new CarsAssemble(); + $actual = $assemblyLine->productionRatePerHour(10); + $this->assertEqualsWithDelta(1701.7, $actual, 0.001); + } + + /** + * @task_id 3 + */ + #[TestDox('Line is not running when "off" (speed 0)')] + public function testIsLineRunningForSpeedZero() + { + $assemblyLine = new CarsAssemble(); + $this->assertFalse($assemblyLine->isLineRunning(0)); + } + + /** + * @task_id 3 + */ + #[TestDox('Line is running at speed 1')] + public function testIsLineRunningForSpeedOne() + { + $assemblyLine = new CarsAssemble(); + $this->assertTrue($assemblyLine->isLineRunning(1)); + } + + /** + * @task_id 3 + */ + #[TestDox('Line is running at speed 10')] + public function testIsLineRunningForSpeedTen() + { + $assemblyLine = new CarsAssemble(); + $this->assertTrue($assemblyLine->isLineRunning(10)); + } + + /** + * @task_id 4 + */ + #[TestDox('Compare speeds when the first is smaller')] + public function testCompareSpeedsWhenFirstIsSmaller() + { + $assemblyLine = new CarsAssemble(); + $this->assertSame(-1, $assemblyLine->compareSpeeds(3, 7)); + } + + /** + * @task_id 4 + */ + #[TestDox('Compare speeds when both are equal')] + public function testCompareSpeedsWhenEqual() + { + $assemblyLine = new CarsAssemble(); + $this->assertSame(0, $assemblyLine->compareSpeeds(5, 5)); + } + + /** + * @task_id 4 + */ + #[TestDox('Compare speeds when the first is greater')] + public function testCompareSpeedsWhenFirstIsGreater() + { + $assemblyLine = new CarsAssemble(); + $this->assertSame(1, $assemblyLine->compareSpeeds(9, 2)); + } +}