Add cars-assemble concept exercise - #1035
Conversation
Unlocks if-control-structures and comparison-operators after booleans and numbers, ported from csharp/cars-assemble. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Thanks a lot for your work! New to the PHP track and head first into concept exercise writing. Do you have experience with Exercism approach to concepts, concept exercises and the didact concept behind all this code and files? I tried to get my head around this PR, but I run out of time for today. I hope to find time for the review this weekend. |
|
@mk-mxp Thanks! Take your time with the review, no rush. I’m used to seeing PHP and how it works, because I work with it daily in WordPress. Writing it from scratch has always been harder for me. I’ve been more of an “understand, copy and paste” person. That’s why I’m going through this track myself. Along the way, I thought it could be a good idea to help improve it while I review and reinforce concepts. What’s new for me is Exercism’s approach to concepts, concept exercises, and the docs/meta structure around them. How I built this PR: I used the C# cars-assemble as a close reference (especially the story/instructions), rewrote the PHP docs, and used another existing PHP track exercise as a reference for PHPUnit syntax, since I hadn’t written those tests by hand before. The idea was almost a 1:1 port from the C# track, but I think it may feel too complex for the actual level of the PHP track. For example, the exemplar uses self::, which isn’t taught yet on the PHP track. Would you rather I keep working on this PR and simplify it, or close it for now so I can open a small issue first and rewrite the exercise in a simpler way? My intention is to help improve the PHP track while learning a bit myself. I don’t want to create noise or extra work for you, so I’ll follow your suggestion. Thanks again! |
Just to clarify one point from my previous comment: what’s new for me is not the syllabus idea itself. I get that Exercism teaches roughly one concept at a time, like a skill tree that unlocks as you go. What’s newer for me is the practical side of writing the docs/meta files around a concept exercise. As I said, this was almost a 1:1 port from C#. I could clean it up a bit so the idea stays the same, but making the code a little more beginner-friendly. The class constant / self:: in the exemplar is the part that bothers me most. Happy to wait for your comments and suggestions. I’m here to try and help if I can, not to make things harder. Please feel completely free to tell me if it makes sense for me to keep contributing, or if right now it would be more of a burden than a help. I’d rather ask than get in the way. |
|
@resu-xuniL This might be interesting for you, too! Let's start with common details: There is a lot of documentation what the files are and what they must / should contain. We have a tool Concepts and concept exercises are much more track specific than practice exercises. So while there is a problem specification repo for practice exercises, concept exercises must be hand picked and adjusted to the track heavily. You picked one from C# that has a good fit for PHP. You may also look at JavaScript or Python, which also have great exercises we may use. Our list of concepts is much bigger than the list of concept exercises. Ideally each concept is accompanied by one concept exercise, tailored to make students use and understand this concept. The design.md of the exercise should clearly state, what is taught. But there also may be multiple concepts / concept exercises around a single topic - e.g. variable-length arguments are an addition to user-defined functions - it depends on what we think is suitable for a learning unit. The other way around (one exercise shared by multiple concepts) has shown to be un-helpful. Students get irritated by getting told, that they have learned a concept they didn't even look at. We want to avoid that, but especially for the first few exercises this is really hard to do. So Exercism says, we should do "hand-waving" instead - use languge constructs not yet introduced, but not explain them in detail. We think hard to avoid this - idiomatic, production-like code is not the goal. First of all it needs to be understandable for people without PHP knowledge. In addition, the concepts have 2 parts on them. One part is We also have a Weaving the exercise into the sylabus tree: Avoid too many prerequisites (1-2, 3 at max). Assume all dependencies of the chosen prerequisites to be fulfilled, don't repeat them. Ask first, what logic prerequisites a concept has, then think about how the concept exercise constrained to these prerequisites should look like. In this example, as you already pointed out, the class constant is a construct we shouldn't use. Also look at the I'm out of time again, I'll come back to this tomorrow. |
|
@pablo-miralles @resu-xuniL I'll go more specifically into this concept / concept exercise now. @pablo-miralles Please keep working on this PR and modify it. I think it is a good fit and not too far away from being merged 😄 As said before, sharpening the focus could help with more intensely teaching If possible, try to stay away from type casting / type conversions, too. When giving code examples, in concept or exercise documents, we have to use PHP specific syntax. This a bit of a challenge for polyglot programmers, but it hits students hard when they copy code into their exercise and it doesn't work. Please look through the documents and fix places like Looking forward to the next iteration, you are welcome to help! |
8c14320 to
1e6928c
Compare
|
@pablo-miralles To avoid that I read code you still work on, please drop me a note when you are done with the iteration. |
Focus the exercise on one concept, simplify the exemplar, and align docs with maintainer feedback. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@mk-mxp Thanks again for taking the time to explain how Exercism concepts and concept exercises work. That context helped a lot, especially as this is my first contribution here :) Also, sorry about the accidental update on this PR earlier. I pushed a revision by mistake while still iterating locally, then force-pushed back to the previous commit so the PR would show the original version again. I know that still leaves a noisy history, and I’m sorry for the extra noise. I’ve now revised the exercise locally following your guidance (narrower focus on comparison-operators, simpler exemplar, no casting / self::, updated concept docs, and PHP-valid examples). I’ve pushed that next iteration shortly for review. Thanks again for the clear feedback and for welcoming the help! |
mk-mxp
left a comment
There was a problem hiding this comment.
Getting closer to merging. We can discuss all of these, they are suggestions.
Thanks a lot for the help!
| | 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` | |
There was a problem hiding this comment.
While in most situations -1, 0 and 1 are the result, PHP only defines An int less than, equal to, or greater than zero in the documentation. I think we should stick with the official documentation wording here, not stating something that might not always hold true.
There was a problem hiding this comment.
Thanks! I’ll update this table row to match the PHP docs wording.
Same idea also shows up in a few other place, worth updating those too for consistency?
- about.md L5 (intro sentence)
- about.md L56–63 (spaceship section)
- introduction.md L32–34 (the -1 / 0 / 1 list)
For the code examples, I’d leave the -1/0/1 results as they are. What do you think?
There was a problem hiding this comment.
Adjust about.md L5 (intro sentence) and introduction.md L32–34 (the -1 / 0 / 1 list), but not about.md L56–63 (spaceship section) or introduction.md L39–41 which are both provable concrete examples known to yield -1 / 1.
I think that's the distinguishing thing: Is it proven to be -1 / 1 or hypothetical?
| $assembly_line = new CarsAssemble(); | ||
| $this->assertTrue($assembly_line->isLineRunning(10)); | ||
| } | ||
|
|
There was a problem hiding this comment.
If you want people to really use not identical, you must test with a different mindset, too:
/**
* @task_id 3
*/
#[TestDox('Line is running when speed is negative')]
public function testIsLineRunningForSpeedMinusOne()
{
$assemblyLine = new CarsAssemble();
$this->assertTrue($assemblyLine->isLineRunning(-1));
}
/**
* @task_id 3
*/
#[TestDox('Line is not running when speed is a string')]
public function testIsLineRunningWhenSpeedIsString()
{
$assemblyLine = new CarsAssemble();
$this->assertFalse($assemblyLine->isLineRunning("1, but a string is not a speed to run at!"));
}There was a problem hiding this comment.
Yes, right. With only 0 vs positive speeds, students could pass without really using !==. I’ll add both test cases as you suggested. Change it then? Thanks!
| $assembly_line->productionRatePerHour(6); | ||
| // => 1193.4 | ||
| ``` | ||
|
|
There was a problem hiding this comment.
While I read through it again: Where is the comparison part in this task 2? Maybe, the production rate should not be calculated, when the speed is over the line's maximum (> 10) or running backwards (< 0)?
There was a problem hiding this comment.
Right now task 2 doesn’t introduce a new comparison on purpose: it’s meant to reuse successRate() and multiply (221 * $speed * successRate). The comparison practice sits in tasks 1, 3, and 4.
Adding a guard for speeds < 0 or > 10 would give task 2 a clear comparison moment. Something like:
## 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.
If the speed is below `0` or above the line's maximum (`10`), do not calculate production — return `0.0`.<?php
$assemblyLine = new CarsAssemble();
$assemblyLine->productionRatePerHour(6);
// => 1193.4
$assemblyLine->productionRatePerHour(-1);
// => 0.0
$assemblyLine->productionRatePerHour(11);
// => 0.0Do you think we should go that way, or leave task 2 as reuse + arithmetic?
There was a problem hiding this comment.
I'd add the comparisons. But use -1.0 as the production rate for invalid input: 0.0 * X === 0.0 anyways (the success rate for <= 0 is 0.0).
|
@pablo-miralles If you have any questions, I'm here to help. You said, it's your first PR ever: I can explain things about GitHubs interface, too. |
Co-authored-by: mk-mxp <55182845+mk-mxp@users.noreply.github.com>
|
Thank you @mk-mxp ! Working through comments was a little difficult, but I accepted almost all of them. The parts you improved sound a lot better, and I've learn that I should use camelCase variables in PHP. I've also answered the small questions you sent me. I'll wait for your reply and fix those parts too. Thank you for your help and pacience. |
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Have you seen the above 15 comments initially hidden by GitHub? Like the one on "Using comparisons in an It also helps to explicitly mark discussions as resolved, if you applied suggestions. I can then easily see open questions vs. things done. |
|
Yes, I've already applied the vast majority of your suggestions here: The remaining ones are the ones I'm asking you about, along with a few others that I either can't or don't know how to address directly in the review thread (for example, the piece of code you moved. I don't get the same button that appeared for the other suggestions). I wanted to go through the other comments where I asked you questions first, and then make another commit with all of those final adjustments. I'll review the rest of your comments today and tomorrow, and I'll push another commit once we've addressed everything. Thanks! |
Summary
cars-assembleconcept exercise, ported fromcsharp/cars-assemble.comparison-operatorsandif-control-structuresafterbooleansand the number concepts frompizza-pi.Test plan
bin/configlet generate/fmt/lintPHPUNIT_BIN=phpunit bin/test.sh cars-assemble(18 tests passing)Made with Cursor