Skip to content
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

RFC: add Base.Iterators module #18839

Merged
merged 3 commits into from
Oct 18, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ Library improvements
One way of doing this is by adding `ENV["JULIA_INFO_COLOR"] = :blue` to the `.juliarc.jl` file.
For more information regarding customizing colors in the REPL, see this [manual section]( http://docs.julialang.org/en/latest/manual/interacting-with-julia/#customizing-colors).

* Iteration utilities that wrap iterators and return other iterators (`enumerate`, `zip`, `rest`,
`countfrom`, `take`, `drop`, `cycle`, `repeated`, `product`, `flatten`, `partition`) have been
moved to the module `Base.Iterators` ([#18839]).

Compiler/Runtime improvements
-----------------------------

Expand Down
4 changes: 2 additions & 2 deletions base/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -376,10 +376,10 @@ function bslash_completions(string, pos)
# return possible matches; these cannot be mixed with regular
# Julian completions as only latex / emoji symbols contain the leading \
if startswith(s, "\\:") # emoji
emoji_names = filter(k -> startswith(k, s), keys(emoji_symbols))
emoji_names = Iterators.filter(k -> startswith(k, s), keys(emoji_symbols))
return (true, (sort!(collect(emoji_names)), slashpos:pos, true))
else # latex
latex_names = filter(k -> startswith(k, s), keys(latex_symbols))
latex_names = Iterators.filter(k -> startswith(k, s), keys(latex_symbols))
return (true, (sort!(collect(latex_names)), slashpos:pos, true))
end
end
Expand Down
1 change: 1 addition & 0 deletions base/asyncmap.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# This file is a part of Julia. License is MIT: http://julialang.org/license

using Base.Iterators.Enumerate

"""
AsyncCollector(f, results, c...; ntasks=0) -> iterator
Expand Down
2 changes: 1 addition & 1 deletion base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ end
else
R = typejoin(eltype(B), S)
new = similar(B, R)
for II in take(iter, count)
for II in Iterators.take(iter, count)
new[II] = B[II]
end
new[I] = V
Expand Down
2 changes: 1 addition & 1 deletion base/client.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ end
# Create a docstring with an automatically generated list
# of colors.
const possible_formatting_symbols = [:normal, :bold]
available_text_colors = collect(filter(x -> !isa(x, Integer), keys(text_colors)))
available_text_colors = collect(Iterators.filter(x -> !isa(x, Integer), keys(text_colors)))
available_text_colors = cat(1,
sort(intersect(available_text_colors, possible_formatting_symbols), rev=true),
sort(setdiff( available_text_colors, possible_formatting_symbols)))
Expand Down
2 changes: 2 additions & 0 deletions base/dates/Dates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ module Dates

importall ..Base.Operators

using Base.Iterators

include("types.jl")
include("periods.jl")
include("accessors.jl")
Expand Down
10 changes: 10 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1023,4 +1023,14 @@ end))

@deprecate is (===)

@deprecate_binding Filter Iterators.Filter
@deprecate_binding Zip Iterators.Zip
@deprecate filter(flt, itr) Iterators.filter(flt, itr)
@deprecate_binding rest Iterators.rest
@deprecate_binding countfrom Iterators.countfrom
@deprecate_binding take Iterators.take
@deprecate_binding drop Iterators.drop
@deprecate_binding cycle Iterators.cycle
@deprecate_binding repeated Iterators.repeated

# End deprecations scheduled for 0.6
12 changes: 3 additions & 9 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export
Docs,
Markdown,
Threads,
Iterators,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this probably shouldn't be exported, won't it conflict with the package name?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I tried both ways, and exporting it didn't change the situation much. The way using works, if you do using Iterators first it loads the package with no warnings. All the other public submodules of Base are exported, so I decided to be consistent. However I'm willing to remove all or most of these exports as part of #5155; some will become top-level modules instead of submodules, and for the remainder we can change the policy on exporting them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you do using Iterators without the package installed will it behave differently than with?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. using can't see Base.Iterators unless you do e.g. import Base.Iterators.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. But if exported from Base, does Iterators refer to something different before you do using Iterators vs after?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case yes, and you get the usual WARNING: imported binding for Iterators overwritten in module Main.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like this is now confusing precompile because it finds the Main.Iterators binding (probably using the wrong logic) and ends up having that shadow the real Iterators package. This shows up as breakage of the Compose package:

julia> using Compose
WARNING: The call to compilecache failed to create a usable precompiled cache file for module Compose. Got:
WARNING: Module Iterators uuid did not match cache file.

julia> using Iterators
WARNING: imported binding for Iterators overwritten in module Main

julia> using Compose

julia> 

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, precompile should use the equivalent of eval_import_path.


# Types
AbstractChannel,
Expand Down Expand Up @@ -61,7 +62,6 @@ export
Enumerate,
Factorization,
FileMonitor,
Filter,
FloatRange,
Future,
Hermitian,
Expand Down Expand Up @@ -124,7 +124,6 @@ export
VersionNumber,
WeakKeyDict,
WorkerConfig,
Zip,

# Ccall types
Cchar,
Expand Down Expand Up @@ -958,16 +957,11 @@ export

# iteration
done,
enumerate,
next,
start,

enumerate, # re-exported from Iterators
zip,
rest,
countfrom,
take,
drop,
cycle,
repeated,

# object identity and equality
copy,
Expand Down
8 changes: 0 additions & 8 deletions base/generator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ result, and algorithms that resize their result incrementally.
iteratorsize(x) = iteratorsize(typeof(x))
iteratorsize(::Type) = HasLength() # HasLength is the default

and_iteratorsize{T}(isz::T, ::T) = isz
and_iteratorsize(::HasLength, ::HasShape) = HasLength()
and_iteratorsize(::HasShape, ::HasLength) = HasLength()
and_iteratorsize(a, b) = SizeUnknown()

abstract IteratorEltype
immutable EltypeUnknown <: IteratorEltype end
immutable HasEltype <: IteratorEltype end
Expand All @@ -81,9 +76,6 @@ values.
iteratoreltype(x) = iteratoreltype(typeof(x))
iteratoreltype(::Type) = HasEltype() # HasEltype is the default

and_iteratoreltype{T}(iel::T, ::T) = iel
and_iteratoreltype(a, b) = EltypeUnknown()

iteratorsize{T<:AbstractArray}(::Type{T}) = HasShape()
iteratorsize{I,F}(::Type{Generator{I,F}}) = iteratorsize(I)
length(g::Generator) = length(g.iter)
Expand Down
56 changes: 19 additions & 37 deletions base/iterator.jl → base/iterators.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# This file is a part of Julia. License is MIT: http://julialang.org/license

module Iterators

import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, indices, ndims

using Base: tuple_type_cons, SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo

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

_min_length(a, b, ::IsInfinite, ::IsInfinite) = min(length(a),length(b)) # inherit behaviour, error
_min_length(a, b, A, ::IsInfinite) = length(a)
_min_length(a, b, ::IsInfinite, B) = length(b)
Expand All @@ -10,6 +18,14 @@ _diff_length(a, b, ::IsInfinite, ::IsInfinite) = 0
_diff_length(a, b, ::IsInfinite, B) = length(a) # inherit behaviour, error
_diff_length(a, b, A, B) = max(length(a)-length(b), 0)

and_iteratorsize{T}(isz::T, ::T) = isz
and_iteratorsize(::HasLength, ::HasShape) = HasLength()
and_iteratorsize(::HasShape, ::HasLength) = HasLength()
and_iteratorsize(a, b) = SizeUnknown()

and_iteratoreltype{T}(iel::T, ::T) = iel
and_iteratoreltype(a, b) = EltypeUnknown()

# enumerate

immutable Enumerate{I}
Expand Down Expand Up @@ -238,42 +254,6 @@ rest_iteratorsize(::IsInfinite) = IsInfinite()
iteratorsize{I,S}(::Type{Rest{I,S}}) = rest_iteratorsize(iteratorsize(I))


"""
head_and_tail(c, n) -> head, tail
Returns `head`: the first `n` elements of `c`;
and `tail`: an iterator over the remaining elements.
```jldoctest
julia> a = 1:10
1:10
julia> b, c = Base.head_and_tail(a, 3)
([1,2,3],Base.Rest{UnitRange{Int64},Int64}(1:10,4))
julia> collect(c)
7-element Array{Any,1}:
4
5
6
7
8
9
10
```
"""
function head_and_tail(c, n)
head = Vector{eltype(c)}(n)
s = start(c)
i = 0
while i < n && !done(c, s)
i += 1
head[i], s = next(c, s)
end
return resize!(head, i), rest(c, s)
end


# Count -- infinite counting

immutable Count{S<:Number}
Expand Down Expand Up @@ -677,7 +657,7 @@ end


"""
partition(collection, n) -> iterator
partition(collection, n)
Iterate over a collection `n` elements at a time.
Expand Down Expand Up @@ -723,3 +703,5 @@ function next(itr::PartitionIterator, state)
end
return resize!(v, i), state
end

end
2 changes: 1 addition & 1 deletion base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ end
# and ensure the value to set is either an AbstractArray or a Repeated scalar
# before redispatching to the _unsafe_batchsetindex!
_iterable(v::AbstractArray) = v
_iterable(v) = repeated(v)
_iterable(v) = Iterators.repeated(v)
@inline function _setindex!{T,N}(l::LinearIndexing, A::AbstractArray{T,N}, x, J::Vararg{Union{Real,AbstractArray,Colon},N})
@boundscheck checkbounds(A, J...)
_unsafe_setindex!(l, A, x, J...)
Expand Down
2 changes: 1 addition & 1 deletion base/pkg/resolve.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function sanity_check(deps::Dict{String,Dict{VersionNumber,Available}},

vers = Array{Tuple{String,VersionNumber,VersionNumber}}(0)
for (p,d) in deps, vn in keys(d)
lvns = VersionNumber[filter(vn2->(vn2>vn), keys(d))...]
lvns = VersionNumber[Iterators.filter(vn2->(vn2>vn), keys(d))...]
nvn = isempty(lvns) ? typemax(VersionNumber) : minimum(lvns)
push!(vers, (p,vn,nvn))
end
Expand Down
44 changes: 39 additions & 5 deletions base/pmap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function pgenerate(p::WorkerPool, f, c)
return AsyncGenerator(f, c; ntasks=()->nworkers(p))
end
batches = batchsplit(c, min_batch_count = length(p) * 3)
return flatten(AsyncGenerator(remote(p, b -> asyncmap(f, b)), batches))
return Iterators.flatten(AsyncGenerator(remote(p, b -> asyncmap(f, b)), batches))
end
pgenerate(p::WorkerPool, f, c1, c...) = pgenerate(p, a->f(a...), zip(c1, c...))
pgenerate(f, c) = pgenerate(default_worker_pool(), f, c)
Expand Down Expand Up @@ -133,7 +133,7 @@ function pmap(p::AbstractWorkerPool, f, c; distributed=true, batch_size=1, on_er
f = wrap_on_error(f, (x,e)->BatchProcessingError(x,e); capture_data=true)
end
f = wrap_batch(f, p, on_error)
results = collect(flatten(AsyncGenerator(f, batches; ntasks=()->nworkers(p))))
results = collect(Iterators.flatten(AsyncGenerator(f, batches; ntasks=()->nworkers(p))))
if (on_error !== nothing) || (retry_n > 0)
process_batch_errors!(p, f_orig, results, on_error, retry_on, retry_n, retry_max_delay)
end
Expand Down Expand Up @@ -213,6 +213,40 @@ function process_batch_errors!(p, f, results, on_error, retry_on, retry_n, retry
nothing
end

"""
head_and_tail(c, n) -> head, tail
Returns `head`: the first `n` elements of `c`;
and `tail`: an iterator over the remaining elements.
```jldoctest
julia> a = 1:10
1:10
julia> b, c = Base.head_and_tail(a, 3)
([1,2,3],Base.Iterators.Rest{UnitRange{Int64},Int64}(1:10,4))
julia> collect(c)
7-element Array{Any,1}:
4
5
6
7
8
9
10
```
"""
function head_and_tail(c, n)
head = Vector{eltype(c)}(n)
s = start(c)
i = 0
while i < n && !done(c, s)
i += 1
head[i], s = next(c, s)
end
return resize!(head, i), Iterators.rest(c, s)
end

"""
batchsplit(c; min_batch_count=1, max_batch_size=100) -> iterator
Expand All @@ -231,14 +265,14 @@ function batchsplit(c; min_batch_count=1, max_batch_size=100)
end

# Split collection into batches, then peek at the first few batches
batches = partition(c, max_batch_size)
batches = Iterators.partition(c, max_batch_size)
head, tail = head_and_tail(batches, min_batch_count)

# If there are not enough batches, use a smaller batch size
if length(head) < min_batch_count
batch_size = max(1, div(sum(length, head), min_batch_count))
return partition(collect(flatten(head)), batch_size)
return Iterators.partition(collect(Iterators.flatten(head)), batch_size)
end

return flatten((head, tail))
return Iterators.flatten((head, tail))
end
2 changes: 1 addition & 1 deletion base/promotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ if isdefined(Core, :Inference)
end
function _promote_op(op, R::ANY, S::ANY)
F = typeof(a -> op(a...))
G = Tuple{Generator{Zip2{Tuple{R},Tuple{S}},F}}
G = Tuple{Generator{Iterators.Zip2{Tuple{R},Tuple{S}},F}}
return Core.Inference.return_type(first, G)
end
else
Expand Down
2 changes: 1 addition & 1 deletion base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ function is_exported_from_stdlib(name::Symbol, mod::Module)
end
mod = parent
end
return isexported(mod, name) && isdefined(mod, name) && getfield(mod, name) === orig
return isexported(mod, name) && isdefined(mod, name) && !isdeprecated(mod, name) && getfield(mod, name) === orig
end

function show(io::IO, f::Function)
Expand Down
4 changes: 3 additions & 1 deletion base/sysimg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ include("intset.jl")
include("associative.jl")
include("dict.jl")
include("set.jl")
include("iterator.jl")
include("iterators.jl")
using .Iterators: zip, enumerate
using .Iterators: Flatten, product # for generators

# Definition of StridedArray
typealias StridedReshapedArray{T,N,A<:DenseArray} ReshapedArray{T,N,A}
Expand Down
Loading