When I was completing the high-scores exercise, I noticed, that there is no need for me to re-calculate the latest and top three scores every time an appropriate function is called.
Instead I could simply store latest, best and top three scores in an object constructor and just return them, when the functions are called.
This approach works with the current canonical-data, but generally, if I were to change the input score list and call personalTop or personalBest later, this will cause an error.
So I wonder if a "persistence" property should be added? The test cases in this property would initialize the input list, get one of the changing properties (e.g. personalTop), then change the input list and get the property again to check if the changes were saved.
Well, part of the design of the exercise is to make it as simple as possible. This was mainly done due to a lack of basic/easy exercises that deal with arrays/collections. Introducing mutation into the exercise is something I'd rather not do, as that would mean that the exercise's difficulty increases and we could thus no longer use it as a basic/easy exercise to introduce arrays.
There are of course many more difficult exercises that _do_ have mutability, so we are able to teach students this concept later on.
Small aside: we are thinking of creating "follow-up" exercises, that are based on the current exercise but expand on that (e.g. perhaps by introducing mutability).
Does this make sense?
In general, yes this makes sense. But I would like to complete my argument based on the current exercise implementation.
Quickly looking through the language tracks one can notice that the high-scores is implemented on the Python and the JavaScript tracks.
Both these exercises expect the student to implement a class in their solution.
Both these exercises are close to the 'middle' difficulty (difficulty 2 for JavaScript and 4th core exercise for Python)
So maybe, based on the way the exercise is implemented, the point of the exercise is to teach about the data storage and not about the arrays? Perhaps in this case, some sort of mutability should be present in the test cases, since it is a source of a lot of errors?
I understand that the above reasoning is somewhat far-fetched and not enough to outargue the basic/easy exercises reasoning. I just wanted to make sure that I understand the purpose of the exercise in its current state. If anything this could help design the future follow-up exercise.
So maybe, based on the way the exercise is implemented, the point of the exercise is to teach about the data storage and not about the arrays?
Well, this is just how JavaScript and Python implement the exercise, that is not tied _per se_ to the exercise itself.
some sort of mutability should be present in the test cases, since it is a source of a lot of errors?
What are those errors if I may ask?
What are those errors if I may ask?
@ErikSchierboom , to name a few:
fork())Basically, all my examples may be summed up with the following - if you create or use already created object/class/storage you need to be careful when you are designing data access and data transformation, that will be used by you and other people.
Perhaps it is too broad and overloaded concept to teach it to the new student, but when they have learned the basic language concepts, this could be beneficial for them.
Right. But isn't this chiefly then an issue with how those tracks implement the exercise? One could implement this exercise without using state.
Right. But isn't this chiefly then an issue with how those tracks implement the exercise? One could implement this exercise without using state.
Edit:
The exercise isn't available in the Haskell track, but the following "solution" proves @ErikSchierboom's claim about a functional an implementation without explicit states.
import Data.List (sort)
latest :: [Integer] -> Integer
latest = last
personalBest :: [Integer] -> Integer
personalBest = maximum
personalTop :: [Integer] -> [Integer]
personalTop = take 3 . sort
report :: [Integer] -> String
report ns = "Your latest score was "
++ show (latest ns)
++ ". That's "
++ shortOf (personalBest ns) (latest ns)
++ "your personal best!"
where
shortOf target n = if target - n > 0
then show (target - n) ++ " short of "
else ""
Because languages can have very distinct idiomatic solutions, it is nice to have specifications that are as language neutral as possible, @ZapAnton.
While you suggestion makes sense, it appears that you are thinking in strict object-oriented programming.
Thank you for this discussion - it's very relevant for me.
My personal opinion is also that solutions that pre-calculate everything in the constructor can be a source of errors/unexpected behavior when used carelessly in the real world. For that reason, when I see solutions that do that I try to make sure the student understands the trade-offs of that decision. I do that in high-scores, even if mutation is not the goal of the exercise on the Ruby track.
However, I do agree with @ErikSchierboom and @rbasso that the exercise specification should be agnostic to the point where it's possible to satisfy the tests using paradigms other than OO. If the main goal of the exercise was mutation then we would probably add a test similar to your suggestion.
Yes - thank you for this discussion, it is helping me better clarify my "mentor notes" for this exercise.
To tag onto what @pgaspar has said, but also address where this exercise appears on the Python track (at position 4 currently):
I agree with @ErikSchierboom & @rbasso that the problem should be specified in a way that's general enough for all languages, and then allow for positioning and customization per-language. So the most basic points for this exercise are array/list/collection creation, storage, and manipulation. And for non-OOP languages, that's how High Scores could be implemented.
In languages that use OOP, one strategy for efficiently creating, storing, and manipulating data is to create an object. For Java, class creation's not optional. For JavaScript and Python (and Ruby?), it is.
So strange as it may sound, I've encountered many Python programmers who've been using the language happily for quite a while (in some cases years) ... and don't make their own classes _at all_. For these programmers, OOP and classes are considered an "advanced" or "esoteric" topic.
So - I've always viewed the High Scores problem as teaching a few basic things in Python:
1) Idiomatic lists/array creation/storage/manipulation.
2) Idiomatic indexing and slicing syntax.
3) Python-specific differences in the way variables work (_variable names are just labels, so assigning a new name does not copy data_)
4) Various ways to make usable copies of data (slicing, copy(), copy vs deep copy, etc.)
5) The difference between list.sort() and sorted() (_mutating in place vs making a copy_)
6) Pythonic built-in strategies and methods that work on iterables (max(), sorted(), etc.)
But then, it's wrapped in an introduction to classes:
1) Getting familiar with making classes and constructors in Python.
2) Getting familiar with what attributes are all about - what they do, how they're different from "normal" variables.
3) Sorting out what the heck Python means by self, and why you need that for object attributes and methods.
4) Being able to call methods from other methods within a class
So that's a lot for an exercise that's coming in at number 4.
And like @pgaspar, I try to talk about trade-offs between calculation at instantiation vs calculation on-demand when I feel it's warranted. Especially if I see a student who's written methods that look like they're thinking about new or additional scores that are coming in -- or they've used self.scores.sort(). But I often save the trade-off conversation for the Matrix problem that follows since it's a class-based problem as well. And that's another thing: I think it takes new OOP programmers quite a bit of repetition before all of the details sink in.
So - I think I'd be for potentially adding variant exercises down the line -- sort of similar to the Markdown exercise, which asks for a re-factor. Perhaps a Dynamic High Scores that requires the student to re-factor High Scores to address some of the points @ZapAnton has listed. But adding those things to the problem as it stands in the order seems too much. And it might cause dropout. Later in the track (or on the side as an OOP specialty) could be good.
Perhaps a
Dynamic High Scoresthat requires the student to re-factorHigh Scoresto address some of the points @ZapAnton has listed.
I like this a lot! Should we close this issue in favor of opening another issue?
Yes, lets close this one. The issue has exhausted itself
Most helpful comment
Right. But isn't this chiefly then an issue with how those tracks implement the exercise? One could implement this exercise without using state.