diff --git a/NEWS.md b/NEWS.md index 71a2d66d7946c..38aea956d9fb0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -35,6 +35,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 -------------------- diff --git a/base/iterators.jl b/base/iterators.jl index 366791be94e8e..fe5146f7585ab 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -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, @@ -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) @@ -322,8 +342,8 @@ 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 @@ -331,8 +351,8 @@ _zip_eltype(::Type{Tuple{}}) = Tuple{} 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 @@ -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 @@ -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 @@ -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 diff --git a/doc/src/base/iterators.md b/doc/src/base/iterators.md index 00655d977d785..8afc54b3bd11b 100644 --- a/doc/src/base/iterators.md +++ b/doc/src/base/iterators.md @@ -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 diff --git a/test/iterators.jl b/test/iterators.jl index a487f2721c2da..7137e70b5958f 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -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"