5 kyu
Iterators
Loading description...
Algorithms
Data Structures
View
This comment has been reported as {{ abuseKindText }}.
Show
This comment has been hidden. You can view it now .
This comment can not be viewed.
- |
- Reply
- Edit
- View Solution
- Expand 1 Reply Expand {{ comments?.length }} replies
- Collapse
- Spoiler
- Remove
- Remove comment & replies
- Report
{{ fetchSolutionsError }}
-
-
Your rendered github-flavored markdown will appear here.
-
Label this discussion...
-
No Label
Keep the comment unlabeled if none of the below applies.
-
Issue
Use the issue label when reporting problems with the kata.
Be sure to explain the problem clearly and include the steps to reproduce. -
Suggestion
Use the suggestion label if you have feedback on how this kata can be improved.
-
Question
Use the question label if you have questions and/or need help solving the kata.
Don't forget to mention the language you're using, and mark as having spoiler if you include your solution.
-
No Label
- Cancel
Commenting is not allowed on this discussion
You cannot view this solution
There is no solution to show
Please sign in or sign up to leave a comment.
this part looks confusing, as my code already passed most other tests: "Map([10, 11, 12, 13, 14, 15], multiply_by_2)[next_0] after the underlying forward-iterator was advanced twice: 20 should equal 24" please give the example in detail. thanks.
Here's a Python version of what's happening in that test:
Attempt fails with: 3_should_map_lazily "Infinite loop triggered by eager evaluation"
Can you give me some hints? Thanx.
The iterator shouldn't do any work unless explicitly requested, but your
map
(at least) starts processing the data immediately.Thank you for the anwer. So if I well understand, when iterator_map() is called I just have to store the mapping function and mapping function's output size and I will perform the actual mapping only on the neeeded elements when iterator_next is called. Am I correct?
Thanx.
Yes.
Shouldn't the test not allocate a buffer away from the stack? If the user messes up, he now also gets messed up error reporting. That should not happen.
Changed all tests to allocate a buffer dynamically.
What's the size of the integers in enumerate? It's not specced, but they should be 'tightly packed'.
iterator_enumerate
receives anint
as a starting index - I think it should be obvious what type is being produced.No, not at all, it could be any size. However, giving your response I presume it should be sizeof (int).
With initial index being
int
, the only logical conclusion is thatenumerate
also generatesint
's. It could belong
orlong long
in theory, but that'd make little sense since the iterator's capabilities would be limited by the function's interface.Issue in the test; reported
Zip([1, 2, 3, 4], Zip(Zip([10, 20, 30, 40], [100, 200, 300, 400]), [1000, 2000, 3000, 4000]))[next_0]: ....... should equal [1000, 10, 100, 1000]
Shouldn't that be[1,10,100,1000]
?Are you sure that's the message you're getting? The assertion is:
and as you can see, it should use correct values for formatting.
Edit: it seems you're overwriting values on the stack.
How can I overwrite values in testcode?? Shouldn't that be impossible?
What do you mean by saying "impossible"? Both the buffer and the expected values are stored on the stack - writing past the buffer bounds may easily affect the latter.
Pretty sure nothing is impossible in C, given the saying that UB can "make demons fly out of your nose" (source) ;-)
And why do you need to allocate a buffer for MapFunction, if iterate_next already gives you a pointer to an output buffer? I think that's unclear from the description.
Added a line to the
iterator_next
's description that the output buffer has the same size as the values produced by the iterator.Would it not suffice for the output buffer to be large enough to contain the value produced by the iterator?
I'm not sure what you're talking about. If you mean that the description should state that "the values produced by the iterator being consumed fit in the buffer exactly", then it already says so, although with a different phrasing (saying that the buffer is "large enough" would imply the possiblity of it being bigger than required, and that'd be a lie). If you're suggesting that the buffer in the test code should be big enough to hold any value produced by any of the iterators in the whole consumption chain, then no - dealing with all this stuff correctly is the solver's responsibility.
I meant it should not matter if the caller happens to pass an output buffer larger than the expected output. Of course, the callee should not assume that the output buffer is any larger than necessary.
If MapFunction writes to an output buffer, without having the size of the output buffer as argument (hence the size is internal to the function), what's the use of the third argument to iterator_map? Note that the pseudocode for
map
is not helpful at all, first because it does not use an iterator as first argument, and second because it does NOT use the size argument at all.We know the size of the input to the
MapFunction
from the first argument toiterator_map
, but how do we know the size of the output, other than from the third argument? My solution makes use of that third argument, but it would be interesting to see an implementation that does not use it at all (if it exists).To create a buffer big enough to store the result of
MapFunction
? Of course, you can just allocate a 1MB buffer but that'd both consume a lot of memory for no reason, and rely onMapFunction
to never produce a value which requires more than 1MB of memory.I used preudocode because something like this:
is a lot shorter, cleaner, and easier to read than plain C code like this:
My intention was to convey how these iterators work, not to write a 100% right pseudocode. The specifications already mention that the first argument is
Iterator *
, and if you cannot infer that[1, 2, 3, 4]
is meant to representforward([1, 2, 3, 4])
, then I'd say that it's your problem. I could change the examples formap
,filter
,enumerate
, andzip
to be "correct", but I'd rather not do that.The size argument is an implementation detail for working with memory in C, and without any actual C code for context it's completely meaningless, hence not needed for showing what
iterator_map
does.Not sure if it's too late for this Kata (but then, there are only 18 completions at the time of writing), but IMO it could be made even better if the iterator could be initialized as an infinite sequence of successive natural numbers (modulo
ULLONG_MAX + 1
?). Because then, one can construct arbitrary infinite sequences by combining said infinite sequence with aMapFunction
. After all, a major advantage of iterators is their ability to model infinite sequences due to their lazy evaluation.I thought about adding generators (at least
count
,repeat
fromitertools
), and decided not to do so since there's nothing challenging or interesting about copy-pasting the same code several more times.A well-written and enjoyable Kata, thank you!
Yes, this is average 5kuy kata, no less)
Issue similar to the one below, but related to
zip
andenumerate
: it's not explained how "composite return values" should be returned via the outputvoid*
pointer. It's not clear what caller expects the underlying buffer to look like: should items be justmemcpy
'ed one immediately after another, or maybe the buffer provides some guarantees on alignment, or maybe it points to some kind of structure?The note already explains this in the specific context of
MapFunction
andFilterFunction
so it's probably reasonable to expectnext
to behave in similar way forzip
andenumerate
, but I think it would be useful to mention this explicitly.Okay, so it sseems there's this note in the comment of
iterator.next
: "// multiple values produced by "enumerate" or "zip" should be tightly-packed" so the question is indeed explained, but then instead of the issue i'd suggest putting this remark in some more prominent place, for example in a dedicated bullet point of "Special arguments" or the note aboutMemFunction
andFilterFunction
.Gosh I cannot edit the label of the initial post to change it from issue to suggestion. Feel free to treat this one as just suggestion, or resolve it if you think it's completely invalid.
I've put it as a note after the "specifications" code block, so it should be more noticeable now.
Alignment requirements for the arguments of
MapFunction
andFilterFunction
and alignment guarantees for the argument ofiterator_next
aren't clear. In particular, the sample tests suggest that it isn't safe to passoutput pointers fromunaligned pointers to map/filter functions, which isn't mentioned in the description.iterator_next
If I understand correctly, it is unsafe in C to reinterpret unaligned pointers (or something like that), right? Would it suffice to simply state that "
MapFunction
andFilterFunction
assume the inputs pointers to be properly aligned, or they may fail otherwise"?I'm not sure what you mean exactly, but it's kind of like that. A pointer can always be cast to
void*
and back, an object representation can always bememcpy
ed from anywhere to anywhere, but an object can't exist and be used normally without proper alignment.I guess so. It's hard to test it reliably because buffers can happen to be aligned accidentally. There are pointers in
iterator_next
too that are impossible to align with zipping and enumerating.Added a note about this after the
MapFunction
andFilterFunction
. If you have any other remarks/ideas, please share them.What is "the proper alignment" in a context where some objects have to remain unaligned (results of zip and enumerate)? Is it the alignment of the first object, the LCM of subobject alignments or the maximum possible alignment? (None allows to access the second object directly, but some choices can give some usefull information for certain use cases.) What about the pointer in
iterator_next
? (It could be used for direct filtering.)If I'm not mistaken, my solution uses pointers aligned according to the LCM of all the intermediate alignments, and it happens to work, although I'm not really sure. I didn't consider the objects' alignment when authoring this, and I'm wondering now if it would be better to rewrite all the functions to work with
char *
and read values using buffers instead of casting the pointer directly? If I understand correctly, this should resolve the problem entirely.It looks like
filter_iterator_next
writes to unalignedchar buffer[]
...Alignment of non-vector types almost doesn't matter on x86, so anything will work at the low level as long as Clang doesn't apply any clever optimizations based on assumptions around alignment.
There isn't much difference between
char*
andvoid*
, it doesn't matter.Yes, probably. The compiler knows anyway if unaligned access is possible on a given platform, so with optimizations
memcpy
s will probably be just normal memory reads and writes whenever possible.I've updated all the functions to read the data by
memcpy
'ing it, and changed the note in the description to explain how exactlyMapFunction
andFilterFunction
work.Why not
_Bool
akabool
in 2021?For some reason I was thinking that
bool
was an "alias" for some 4-byte type and there was no point in ever using it. Changedint
tobool
everywhere.What about
iterator_next
?Changed as well.
This message does not help for debugging
I've added an error message for this test, but the random tests will probably have to log
X should equal Y
because printing hundreds-of-elements-long arrays would not be useful, and idk if there's any other meaningful information that could be provided instead.Well, according to the current suite, i guess one will pass the random part if they pass the others. But at least you can show how the iterator is constructed.
Are we supposed to add fields and derive types from this?
Not necessarily. If you don't want to, you could also try storing the iterators' states in some global table, but I don't think it's a reasonable approach.
Can you give example in the description on how the iterator would differ from simple returning the pointer?
By looking at this example:
It seems to me that there is no difference, as all I've to do is simply return the array.
Edit: The analogy with python without giving concrete example won't work, because for instance in python
map
object is returned instead of the array.Rewrote the examples to make the
next
usage clear.