Skip to content

Commit

Permalink
Add Iterators.map (#34352)
Browse files Browse the repository at this point in the history
  • Loading branch information
tkf authored Jun 18, 2020
1 parent e96728c commit 1f8b442
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 9 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ New library functions

* New function `Base.kron!` and corresponding overloads for various matrix types for performing Kronecker product in-place. ([#31069]).
* New function `Base.Threads.foreach(f, channel::Channel)` for multithreaded `Channel` consumption. ([#34543]).
* `Iterators.map` is added. It provides another syntax `Iterators.map(f, iterators...)`
for writing `(f(args...) for args in zip(iterators...))`, i.e. a lazy `map` ([#34352]).

New library features
--------------------
Expand Down
38 changes: 29 additions & 9 deletions base/iterators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ using .Base:
@inline, Pair, AbstractDict, IndexLinear, IndexCartesian, IndexStyle, AbstractVector, Vector,
tail, tuple_type_head, tuple_type_tail, tuple_type_cons, SizeUnknown, HasLength, HasShape,
IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds, Generator, AbstractRange,
LinearIndices, (:), |, +, -, !==, !, <=, <, missing, map, any, @boundscheck, @inbounds
LinearIndices, (:), |, +, -, !==, !, <=, <, missing, any, @boundscheck, @inbounds

import .Base:
first, last,
Expand All @@ -24,6 +24,26 @@ import .Base:

export enumerate, zip, rest, countfrom, take, drop, takewhile, dropwhile, cycle, repeated, product, flatten, partition

"""
Iterators.map(f, iterators...)
Create a lazy mapping. This is another syntax for writing
`(f(args...) for args in zip(iterators...))`.
!!! compat "Julia 1.6"
This function requires at least Julia 1.6.
# Examples
```jldoctest
julia> collect(Iterators.map(x -> x^2, 1:3))
3-element Array{Int64,1}:
1
4
9
```
"""
map(f, args...) = Base.Generator(f, args...)

tail_if_any(::Tuple{}) = ()
tail_if_any(x::Tuple) = tail(x)

Expand Down Expand Up @@ -322,17 +342,17 @@ eltype(::Type{Zip{Is}}) where {Is<:Tuple} = _zip_eltype(Is)
_zip_eltype(::Type{Is}) where {Is<:Tuple} =
tuple_type_cons(eltype(tuple_type_head(Is)), _zip_eltype(tuple_type_tail(Is)))
_zip_eltype(::Type{Tuple{}}) = Tuple{}
@inline isdone(z::Zip) = _zip_any_isdone(z.is, map(_ -> (), z.is))
@inline isdone(z::Zip, ss) = _zip_any_isdone(z.is, map(tuple, ss))
@inline isdone(z::Zip) = _zip_any_isdone(z.is, Base.map(_ -> (), z.is))
@inline isdone(z::Zip, ss) = _zip_any_isdone(z.is, Base.map(tuple, ss))
@inline function _zip_any_isdone(is, ss)
d1 = isdone(is[1], ss[1]...)
d1 === true && return true
return d1 | _zip_any_isdone(tail(is), tail(ss))
end
@inline _zip_any_isdone(::Tuple{}, ::Tuple{}) = false

@propagate_inbounds iterate(z::Zip) = _zip_iterate_all(z.is, map(_ -> (), z.is))
@propagate_inbounds iterate(z::Zip, ss) = _zip_iterate_all(z.is, map(tuple, ss))
@propagate_inbounds iterate(z::Zip) = _zip_iterate_all(z.is, Base.map(_ -> (), z.is))
@propagate_inbounds iterate(z::Zip, ss) = _zip_iterate_all(z.is, Base.map(tuple, ss))

# This first queries isdone from every iterator. If any gives true, it immediately returns
# nothing. It then iterates all those where isdone returned missing, afterwards all those
Expand Down Expand Up @@ -388,7 +408,7 @@ _zip_iterator_eltype(::Type{Is}) where {Is<:Tuple} =
_zip_iterator_eltype(tuple_type_tail(Is)))
_zip_iterator_eltype(::Type{Tuple{}}) = HasEltype()

reverse(z::Zip) = Zip(map(reverse, z.is))
reverse(z::Zip) = Zip(Base.map(reverse, z.is))

# filter

Expand Down Expand Up @@ -982,7 +1002,7 @@ end
isdone(P) === true && return nothing
next = _piterate(P.iterators...)
next === nothing && return nothing
return (map(x -> x[1], next), next)
return (Base.map(x -> x[1], next), next)
end

@inline _piterate1(::Tuple{}, ::Tuple{}) = nothing
Expand All @@ -1003,10 +1023,10 @@ end
isdone(P, states) === true && return nothing
next = _piterate1(P.iterators, states)
next === nothing && return nothing
return (map(x -> x[1], next), next)
return (Base.map(x -> x[1], next), next)
end

reverse(p::ProductIterator) = ProductIterator(map(reverse, p.iterators))
reverse(p::ProductIterator) = ProductIterator(Base.map(reverse, p.iterators))

# flatten an iterator of iterators

Expand Down
1 change: 1 addition & 0 deletions doc/src/base/iterators.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Base.Iterators.repeated
Base.Iterators.product
Base.Iterators.flatten
Base.Iterators.partition
Base.Iterators.map
Base.Iterators.filter
Base.Iterators.accumulate
Base.Iterators.reverse
Expand Down
7 changes: 7 additions & 0 deletions test/iterators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ end
@test length(zip(1:3,product(1:7,cycle(1:3)))) == 3
@test length(zip(1:3,product(1:7,cycle(1:3)),8)) == 1

# map
# ----
@testset "Iterators.map" begin
@test collect(Iterators.map(string, 1:3)::Base.Generator) == map(string, 1:3)
@test collect(Iterators.map(tuple, 1:3, 4:6)::Base.Generator) == map(tuple, 1:3, 4:6)
end

# rest
# ----
let s = "hello"
Expand Down

0 comments on commit 1f8b442

Please sign in to comment.