Skip to content

Commit

Permalink
Change iteration protocol
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
Keno committed May 18, 2018
1 parent a9d08b0 commit 5d33ee0
Showing 1 changed file with 17 additions and 12 deletions.
29 changes: 17 additions & 12 deletions base/statistics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down

0 comments on commit 5d33ee0

Please sign in to comment.