From 8b2681da87d968f6cbb2ecfe13c15a4fd96cf65d Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 22 Dec 2017 13:37:18 +0100 Subject: [PATCH] Change iteration protocol This changes the iteration protocol from `start`/`next`/`done` to `iterate`. The new lowering of a for loop is as follows: ``` for x in itr ... end ``` becomes ``` next = iterate(itr) while next !== nothing x, state = next::Tuple{Any, Any} ... next = iterate(itr, state) end ``` The semantics are as apparent from the above lowering. `iterate` returns either `nothing` or a tuple of value and state. The state is passed to any subsequent operation. The first iteration is indicated, by not passing the second, state argument to the `iterate` method. Adaptors in both directions are provided to keep the legacy iteration protocol working for now. However, performance of the legacy iteration protocol will be severely pessimized. As an optional add-on for mutable iterators, a new `isdone` function is provided. This function is intended as an O(1) approximate query for iterator completion, where such a calculation is possible without mutation and/or is significantly faster than attempting to obtain the element itself. The function makes use of 3-value logic. `missing` is always an acceptable answer, in which case the caller should go ahead and attempt the iteration to obtain a definite result. If the result is not `missing`, it must be exact (i.e. if true, the next call to iterate must return `nothing`, if false it must not return nothing). --- base/statistics.jl | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/base/statistics.jl b/base/statistics.jl index 3a75670d..a5310c3f 100644 --- a/base/statistics.jl +++ b/base/statistics.jl @@ -16,18 +16,20 @@ julia> mean([√1, √2, √3]) ``` """ function mean(f::Callable, iterable) - state = start(iterable) - if done(iterable, state) + y = iterate(iterable) + if y == nothing throw(ArgumentError("mean of empty collection undefined: $(repr(iterable))")) end count = 1 - value, state = next(iterable, state) + value, state = y f_value = f(value) total = reduce_first(add_sum, f_value) - while !done(iterable, state) - value, state = next(iterable, state) + y = iterate(iterable, state) + while y !== nothing + value, state = y total += f(value) count += 1 + y = iterate(iterable, state) end return total/count end @@ -86,19 +88,21 @@ realXcY(x::Complex, y::Complex) = real(x)*real(y) + imag(x)*imag(y) var(iterable; corrected::Bool=true, mean=nothing) = _var(iterable, corrected, mean) function _var(iterable, corrected::Bool, mean) - state = start(iterable) - if done(iterable, state) + y = iterate(iterable) + if y === nothing throw(ArgumentError("variance of empty collection undefined: $(repr(iterable))")) end count = 1 - value, state = next(iterable, state) + value, state = y + y = iterate(iterable, state) if mean === nothing # Use Welford algorithm as seen in (among other places) # Knuth's TAOCP, Vol 2, page 232, 3rd edition. M = value / 1 S = real(zero(M)) - while !done(iterable, state) - value, state = next(iterable, state) + while y !== nothing + value, state = y + y = iterate(iterable, state) count += 1 new_M = M + (value - M) / count S = S + realXcY(value - M, value - new_M) @@ -112,8 +116,9 @@ function _var(iterable, corrected::Bool, mean) # Department of Computer Science, Stanford University, # because user can provide mean value that is different to mean(iterable) sum2 = abs2(value - mean::Number) - while !done(iterable, state) - value, state = next(iterable, state) + while y !== nothing + value, state = y + y = iterate(iterable, state) count += 1 sum2 += abs2(value - mean) end