It is unclear what an ‘idiomatic’ solution looks like, as in a sense the very problem statement itself is unidiomatic.
It is unclear what
Implement a series of basic list operations, without using existing functions.
should mean in the context of Python.
Many obvious implementations are boring: e.g. + for append, len for length, …
The instructions lack suggestions on how to make this exercise interesting.
It’s not just me: I have seen other students be confused by the instructions, not see the point of (parts of) the exercise, or wonder about real-world applicability.
List Ops has its origins in languages that prominently feature singly linked lists. For those languages this exercise makes a lot of sense. Perhaps we can recover this sense in Python?
I think we can: just have all the required functions take and return iterators instead of lists.
Now there is a real problem to be solved, allowing reflections on idioms.
There aren’t many pre-existing functions for working with iterators; elementary solutions now present themselves more easily. (Relatively.)
Solutions are now much more interesting: learn about yield, yield from, generators, itertools.
The problem itself is interesting.
Partial example solution for illustration. Many other implementations are possible.
def append(xs, ys):
yield from xs
yield from ys
return sum(1 for _ in xs)
def foldr(f, xs, init):
xs = iter(xs)
for x in xs:
return f(x, foldr(f, xs, init))
Of course this change will invalidate all prior submissions. However, many or most of these will be invalidated at some point anyway: fixing Bug in Python List Ops test alone will likely touch on the order of half of them already.
The test data would not need to change; only their realiziation in the tests.
For many exercises, it’s a rewarding as the effort that you put into it. I’m pretty pleased with my list-ops solution: think about what are the absolute minimal list operations that you need python to do and what can you implement yourself.
Perhaps this more overt challenge should be added to the python instructions.
We need python to be able to:
construct an array: [a, b, c]
spread and slurp arrays: a, b, *rest = [*xs]
tell us if an array is empty: if xs: print('not empty')
Everything else in this exercise can be accomplished with just those basic operations.
For example, don’t use the builtin len, reversed, map, filter functions, or the builtin + operator.
This kind of sounds like an “establishment” “don’t change anything” answer. I like alternate takes on exercises, and if they’re particularly more idiomatic, perhaps you can think about extending the current exercise with a new one that’s focused on iterators.
II’m inclined to agree with both Glenn and Isaac here. So most of what follows below is predicated on convincing us and others that this redesign for Python is worth doing. Fleshing out the docs/items below could help with that.
Just to be clear on numbers:
This exercise has been attempted by 1,942 students, and completed by 1, 508. (that’s a 77.7% completion rate).
Any redesign for Python is going to need the following components:
A clear instruction append, explaining to students how the Python implementation differs from the canonical one. See this example for how these are generally put together.
A new JinJa2 template for generating the Python test file. I am not going to accept a rework or redesign without one.
A new Python test file (generated from the template). This should follow unittest syntax. This file cannot rely on any third-party libraries, and must be runnable from our test runner docker container.
A new additional_tests.json, for any Python-specific tests that are not accounted for in the canonical data from the problem-specs repo.
A new hints file, so that students have information to get them unstuck. The current exercise doesn’t have one - but I think one is needed here.
Please also remember that while our tooling does run on Python 3.11.2, students will have Python versions spread between 3.7 -- 3.11.2. If your exercise relies on features in later versions of Python, you will need to note that in instructions, and understand that you are then closing the exercise off from students who don’t run that version.
And while I will happily answer questions – I am not listing all this out to be unwelcoming – I am not going to have time to walk you through in detail how to make a JinJa2 template, how to write test cases in unittest, nor how to assemble anything that is documented elsewhere.
It feels to me that if what is wanted on the Python track is an exercise on iterators, that a better effort is spent on writing the concept exercise for iterators, OR taking on the one for itertools. Both would reach far more students, and would have much more room for going over specific topics.
Sorry Glenn, I didn’t see your answer as I was typing mine. Agree. We can introduce a new practice exercise that focuses on iterators as well. We’ll most likely need one or more in support of the concept exercises I listed in my reply.
Edited to add: @IsaacG and I have had conversations around simple linked list and (perhaps) testing for the return of views or iterators, so a follow-on exercise to simple linked list that was iterator focused could be an option as well.
So I’d love to see this new iterator ops practice exercise happen. @MatthijsBlom - feel free to get started on this, and let me know if you need specific questions answered. The restrictions on tests still apply (I’ll make an exception if you need to use pytest functionality), but you need not worry about a JinJa2 template nor generating the test file, since this will be a track-specific practice exercise (at least at first). We can do a template/generator, but it’s a bit awkward, since we have to mock having canonical data for it to work.
One thing I would want is to follow the precedent of kytrinyx in her re-working project and provide a motivating story or scenario in addition to the instructions.
Another thing to keep in mind is that we don’t have syllabus items for iterators nor generators at this time. So an instruction append with some details is probably in order for this. I say instruction append, because we might want to propose this in problem specifications in the future, and having Python-specific things in an addendum would simplify a transfer.
I think you meant to reply to the other thread? But yes - we could swap the arguments in the template.
But I think I’d rather keep the acc, el convention. If we’re invalidating solutions, let’s take the path of least code changed. I think we can get away with the one line removal in the template, then modify the additional test case – or remove it altogether.
But as I said on the other thread, I think this exercise needs an instruction append. We can do the changes to use canonical data and test cases now … but we really need to also have some detail in the instructions to students. That can be a follow-on PR.