# Define iterator

**URL:** <https://discourse.julialang.org/t/define-iterator/76630>\
**Category:** New to Julia\
**Tags:** iterators\
**Created:** [February 17, 2022, 2:29pm UTC](https://discourse.julialang.org/t/define-iterator/76630 "2022-02-17T14:29:00Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![rukh79](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rukh79/32/33245_2.png) [@rukh79](https://discourse.julialang.org/u/rukh79)\
**Post date:** [February 17, 2022, 2:29pm UTC](https://discourse.julialang.org/t/define-iterator/76630/1 "2022-02-17T14:29:00Z")

</div>

Hi all,

I am following the manual to learn to define a customized iterator, but couldn’t figure out what I did wrong.

If I use for loop, it gave error “MethodError: no method matching length”.  
If I use while loop, it gave error “MethodError: no method matching iterate(::Nothing)”.

Could someone please point me where the problem is?  
Thanks!

```julia
struct Farey_Sequence
    N :: Int
    descending :: Bool
    Farey_Sequence(n; descending = false) = new(n, descending)
end

function Base.iterate(f :: Farey_Sequence) :: Union{Nothing, Tuple{Rational{Int}, NTuple{4, Int}}}
    if f.descending
        a, b, c, d = 1, 1, f.N - 1, f.N
    else
        a, b, c, d = 0, 1, 1, f.N
    end
    a // b, (a, b, c, d)
end

function Base.iterate(f :: Farey_Sequence, state :: NTuple{4, Int}) :: Union{Nothing, Tuple{Rational{Int}, NTuple{4, Int}}}
    a, b, c, d = state
    if (c <= f.N && !f.descending) || (a > 0 && f.descending)
        k = (f.N + b) ÷ d
        (a, b, c, d) = (c, d, k * c - a, k * d - b)
        return a // b, (a, b, c, d)
    else
        return nothing
    end
end

```

---

<div class="post-metadata">

**Author:** ![sostock](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sostock/32/5546_2.png) [@sostock](https://discourse.julialang.org/u/sostock)\
**Post date:** [February 17, 2022, 2:46pm UTC](https://discourse.julialang.org/t/define-iterator/76630/2 "2022-02-17T14:46:50Z")

</div>

I can use your iterator in a `for` loop without problem:

```julia
julia> for i = Farey_Sequence(5,descending=false)
           println(i)
       end
0//1
1//5
1//4
1//3
2//5
1//2
3//5
2//3
3//4
4//5
1//1

```

In order to receive better help, you might want to show how you use the iterator in `for`/`while` loops to get those errors. But the problem is probably the following:

There is a trait `Base.IteratorSize` for determining whether an iterator has a known length/shape or not. The default is `Base.HasLength()`, so the `for` loop expects that the length (i.e., the number of values the iterator produces) is known and that `Base.length(::Farey_Sequence)` is implemented.

You can either implement `Base.length(::Farey_Sequence)` or set the `Base.IteratorSize` trait to `Base.SizeUnknown()` (this means that it is not known in advance how many values the iterator will produce):

```julia
Base.IteratorSize(::Type{Farey_Sequence}) = Base.SizeUnknown()

```

---

<div class="post-metadata">

**Author:** ![rukh79](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rukh79/32/33245_2.png) [@rukh79](https://discourse.julialang.org/u/rukh79)\
**Post date:** [February 17, 2022, 3:07pm UTC](https://discourse.julialang.org/t/define-iterator/76630/3 "2022-02-17T15:07:08Z")

</div>

Thank you! I stand corrected. It indeed works in loop.

It gave the error in array comprehension.  
[a for a in Farey\_Sequence(5,descending=false)]  
ERROR: MethodError: no method matching length(::Farey\_Sequence)

guess I have to define length function in order to use comprehension?

---

<div class="post-metadata">

**Author:** ![rukh79](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rukh79/32/33245_2.png) [@rukh79](https://discourse.julialang.org/u/rukh79)\
**Post date:** [February 17, 2022, 3:15pm UTC](https://discourse.julialang.org/t/define-iterator/76630/4 "2022-02-17T15:15:39Z")

</div>

> [@sostock](#):
>
> `Base.IteratorSize(::Type{Farey_Sequence}) = Base.SizeUnknown()`

actually with this, array comprehension is working as well! thank you!

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [February 17, 2022, 3:33pm UTC](https://discourse.julialang.org/t/define-iterator/76630/5 "2022-02-17T15:33:16Z")

</div>

I believe comprehensions check if length exists to try to pre-allocate and speed-up the process.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [February 17, 2022, 6:50pm UTC](https://discourse.julialang.org/t/define-iterator/76630/6 "2022-02-17T18:50:27Z")

</div>

> [@rukh79](#):
>
> ```julia
> :: Union{Nothing, Tuple{Rational{Int}, NTuple{4, Int}}}
> 
> ```

The output type assertion doesn’t really do anything, and is so unreadable it’s not very enlightening for the reader either (I had to look at the actual return statement to help me parse it.)

It also looks wrong, since `iterate(::Farey_Sequence)` will, as far as I can tell, never return `nothing` anyway (sorry about the double negation…)

---

<div class="post-metadata">

**Author:** ![rukh79](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rukh79/32/33245_2.png) [@rukh79](https://discourse.julialang.org/u/rukh79)\
**Post date:** [February 17, 2022, 8:52pm UTC](https://discourse.julialang.org/t/define-iterator/76630/7 "2022-02-17T20:52:01Z")

</div>

I agree that specifying the return type isn’t very useful. I added that in the hope to get rid of the error I had. Apparently, it was due to the problem answered by sostock. I have since removed the return type.

It will indeed return nothing when farey sequence reaches the end, so I think I got that right.

---

<div class="post-metadata">

**Author:** ![rukh79](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rukh79/32/33245_2.png) [@rukh79](https://discourse.julialang.org/u/rukh79)\
**Post date:** [February 17, 2022, 8:54pm UTC](https://discourse.julialang.org/t/define-iterator/76630/8 "2022-02-17T20:54:04Z")

</div>

below is the new implementation, which I think served my need at the moment. I have removed descending from struct, but added reverse iterator instead. welcome any comments!

```julia
struct Farey_Sequence
    N :: Int
end

function Base.iterate(f :: Farey_Sequence)
    a, b, c, d = 0, 1, 1, f.N
    a // b, (a, b, c, d)
end
function Base.iterate(f :: Iterators.Reverse{Farey_Sequence})
    a, b, c, d = 1, 1, f.itr.N - 1, f.itr.N
    a // b, (a, b, c, d)
end

function next_farey(state, n, descending)
    a, b, c, d = state
    if (c <= n && !descending) || (a > 0 && descending)
        k = (n + b) ÷ d
        (a, b, c, d) = (c, d, k * c - a, k * d - b)
        return a // b, (a, b, c, d)
    else
        return nothing
    end
end
function Base.iterate(f :: Farey_Sequence, state :: NTuple{4, Int})
    next_farey(state, f.N, false)
end
function Base.iterate(f :: Iterators.Reverse{Farey_Sequence}, state :: NTuple{4, Int})
    next_farey(state, f.itr.N, true)
end

Base.IteratorSize(::Type{Farey_Sequence}) = Base.SizeUnknown()

```

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [February 17, 2022, 10:24pm UTC](https://discourse.julialang.org/t/define-iterator/76630/9 "2022-02-17T22:24:42Z")

</div>

> [@rukh79](#):
>
> It will indeed return nothing when farey sequence reaches the end, so I think I got that right.

I don’t think so. The second method, `iterate(::Farey_Sequence, ::NTuple{4, Int})` can return `nothing`, but not `iterate(::Farey_Sequence)`.

---

<div class="post-metadata">

**Author:** ![rukh79](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rukh79/32/33245_2.png) [@rukh79](https://discourse.julialang.org/u/rukh79)\
**Post date:** [February 17, 2022, 10:38pm UTC](https://discourse.julialang.org/t/define-iterator/76630/10 "2022-02-17T22:38:40Z")

</div>

I saw your point now. I thought Julia needs two iterate functions have the same return type. thanks for pointing that out.

---

<div class="post-metadata">

**Author:** ![sostock](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sostock/32/5546_2.png) [@sostock](https://discourse.julialang.org/u/sostock)\
**Post date:** [February 18, 2022, 8:27am UTC](https://discourse.julialang.org/t/define-iterator/76630/11 "2022-02-18T08:27:12Z")

</div>

For better performance, you should also implement `eltype` for your Iterator, i.e.,

```julia
Base.eltype(::Type{Farey_Sequence}) = Rational{Int}

```

Without that method, the `eltype` defaults to `Any`, which means that, for example, `collect` will return a `Vector{Any}` instead of a `Vector{Rational{Int}}`

```julia
julia> collect(Farey_Sequence(1))
2-element Vector{Any}:
 0//1
 1//1

```

---

<div class="post-metadata">

**Author:** ![rukh79](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rukh79/32/33245_2.png) [@rukh79](https://discourse.julialang.org/u/rukh79)\
**Post date:** [February 18, 2022, 2:10pm UTC](https://discourse.julialang.org/t/define-iterator/76630/12 "2022-02-18T14:10:10Z")

</div>

great point! thanks!
