-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP/ RFC/First PR: Add unzip
to base.iterators.jl
#21208
Conversation
~~@StefanKarpinski~~ Julia needs an underling to give this PR a shot. [#13942](#13942). 1. I know I'm clearly missing some important things, but I hope the effort counts. I couldn't find a way to get the rows from the array comprehension... 2. Willing to start over from scratch - any comments/critiques/suggestions more than welcome. 3. I am first trying a MWE with simple structure and inputs. Then I will try to get something that works more generically. Is this a good way to procceed? [ci skip] Roadmap: * Verify each returned array is the same type as the types of the elements * Verify lengths of all arrays to be unzipped * Return as a single Array of Arrays or as separate Arrays? * Remove that `zip is its own inverse` in `@doc zip`? * Revise documentation phrasing and example
Definitely; I almost always start with examples and then generalize. If you want an array of arrays, you can use nested comprehensions. Ideally this would work with arbitrary iterators, which do not necessarily support
+1 to that |
update julia fork
~~@StefanKarpinski~~ Julia needs an underling to give this PR a shot. [#13942](#13942). 1. I know I'm clearly missing some important things, but I hope the effort counts. I couldn't find a way to get the rows from the array comprehension... 2. Willing to start over from scratch - any comments/critiques/suggestions more than welcome. 3. I am first trying a MWE with simple structure and inputs. Then I will try to get something that works more generically. Is this a good way to procceed? [ci skip] Roadmap: * Verify each returned array is the same type as the types of the elements * Verify lengths of all arrays to be unzipped * Return as a single Array of Arrays or as separate Arrays? * Remove that `zip is its own inverse` in `@doc zip`? * Revise documentation phrasing and example
If I may offer a couple suggestions, I would have the function return a tuple of iterators rather than an array of arrays, and I would restrict unzip(iter::AbstractZipIterator) = _unzip(iter)
# This avoids exporting the method that isn't for AbstractZipIterators
_unzip(iter::Zip1) = (iter.a,)
_unzip(iter::Zip2) = (iter.a, iter.b)
_unzip(iter::Zip) = (iter.a, _unzip(iter.z)...)
_unzip(iter) = (iter,) This makes use of the internal structure of the |
@JeffBezanson Thanks! Been lurking for a while. Time to be brave and attempt a commit.
Tried quite a few variations of that - still haven't got the hang of it yet. I will try following the advice that it says to push onto the patch-3 branch on my own fork. |
@kshyatt Added tests |
I'll try to break it down so it's more understandable. First we're defining a function to export called unzip(iter::AbstractZipIterator) = _unzip(iter) We're just making it call an internal, unexported function Now we define the internal function. This exploits the structure of the struct Zip1{I} <: AbstractZipIterator
a::I
end
struct Zip2{I1, I2} <: AbstractZipIterator
a::I1
b::I2
end
struct Zip{I, Z<:AbstractZipIterator} <: AbstractZipIterator
a::I
z::Z
end For _unzip(iter::Zip1) = (iter.a,) For _unzip(iter::Zip2) = (iter.a, iter.b)
_unzip(iter::Zip) = (iter.a, _unzip(iter.z)...) Then we define a fallback method that just wraps the input in a tuple. This is a safeguard against any potential infinite recursion in the _unzip(iter) = (iter,) And that's it! Seem any less magical? |
Damn, this is why I love Julia. I guess I have some questions still though: I have no idea how to proceed now, though I just added edited the doc lines to make sure that |
Type inference can reason about tuples and the types of individual slots in them. Inference can only reason about the element type of an entire array, which in this case is not nearly as helpful. |
Thanks for taking a crack at this @miguelraz! |
@StefanKarpinski Thanks to you guys for helping me learn!
Aaaah. And inference leads to speed. No wonder Base likes Tuples so much. |
test/iterators.jl
Outdated
# unzip | ||
let z = zip(1:2) | ||
@test unzip(z) == 1:2 | ||
@test eltype(unzip(z)) == UnitRange{Int64} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UnitRange{Int}
here since it will fail on systems with Int == Int32
(and same on the tests below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Corrected now.
base/iterators.jl
Outdated
@@ -240,6 +240,32 @@ end | |||
iteratorsize{I1,I2}(::Type{Zip{I1,I2}}) = zip_iteratorsize(iteratorsize(I1),iteratorsize(I2)) | |||
iteratoreltype{I1,I2}(::Type{Zip{I1,I2}}) = and_iteratoreltype(iteratoreltype(I1),iteratoreltype(I2)) | |||
|
|||
# unzip | |||
```jldoctest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only the code should be within ```jldoctest ... ``` (i.e. the parts with julia>
)
Also, the complete docstring should be within """ ... """, take a look at the docstring for zip
on the lines above in this file!
(Also note that the examples are outdated after you updated unzip
to only work with AbstractZipIterators
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, tried to fix this up.
Review comments?
For iterable objects of the same length, returns iterable sets, where the ith set contains | ||
the ith component of each input iterable object. | ||
|
||
Note that the inverse of [`unzip`](@ref) is [`zip`](@ref). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps not necessary to reference unzip
in its own documentation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But zip
's docs say that zip
's inverse is unzip
.
(Or at least tried to indicate the inverse.)
... Decision time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just meant the @ref
part of unzip
here, no need to link to itself. (And the docs for zip
probably shouldn't have a @ref
for itself either.)
While I agree that an |
I would imagine that calling unzip on a zip would generally only be done in tests. After all, if you just zipped something, why are you unzipping it again? |
base/iterators.jl
Outdated
# This avoids exporting the method that isn't for AbstractZipIterators | ||
_unzip(iter::Zip1) = (iter.a,) | ||
_unzip(iter::Zip2) = (iter.a, iter.b) | ||
_unzip(iter::Zip) = (iter.a, unzip(iter.z)...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inner call to unzip
here should actually be _unzip
. (That was my typo initially, sorry about that.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. Corrected now.
test/iterators.jl
Outdated
@@ -22,6 +22,22 @@ let z = zip(1:2, 3:4, 5:6) | |||
@test eltype(z) == Tuple{Int,Int,Int} | |||
end | |||
|
|||
# unzip | |||
let z = zip(1:2) | |||
@test unzip(z) == 1:2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since unzip
always returns a Tuple
, the right hand side of this test should be (1:2,)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. Corrected as well.
That's a fair point, but if you haven't zipped something, why would you be unzipping it at all? |
On Wed, Mar 29, 2017 at 12:28 PM Alex Arslan ***@***.***> wrote:
After all, if you just zipped something, why are you unzipping it again?
That's a fair point, but if you *haven't* zipped something, why would you
be unzipping it at all?
I would have thought that Stefan had been around this business long enough
to know that, just because something doesn't make sense, doesn't mean
someone won't want to do it.
… |
Work log: |
|
||
```jldoctest | ||
"""jldoctest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still not quite right, try looking at enumerate on line 35.
@@ -22,6 +22,22 @@ let z = zip(1:2, 3:4, 5:6) | |||
@test eltype(z) == Tuple{Int,Int,Int} | |||
end | |||
|
|||
# unzip |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this a good place to use @testset
?
@@ -240,6 +241,34 @@ end | |||
iteratorsize{I1,I2}(::Type{Zip{I1,I2}}) = zip_iteratorsize(iteratorsize(I1),iteratorsize(I2)) | |||
iteratoreltype{I1,I2}(::Type{Zip{I1,I2}}) = and_iteratoreltype(iteratoreltype(I1),iteratoreltype(I2)) | |||
|
|||
# unzip | |||
""" | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have thought that unzip
would need it's own method name, something like unzip(iter::AbstractZipIterator)
. Also, I think that you need to add unzip
to exports.jl
.
Replaced by #33515 (@StefanKarpinski there's a couple tests here, if you don't want to write your own 😜) |
@StefanKarpinskiJulia needs an underling to give this PR a shot. #13942.[ci skip]
Roadmap:
zip is its own inverse
in@doc zip
?