1

According to the documentation:

init(_ s: S) where Element == S.Element, S : Sequence
Creates an array containing the elements of a sequence.

struct Test: IteratorProtocol, Sequence {
    let id: Int

    init(_ id: Int) {
        self.id = id
    }

    mutating func next() -> Test? {
        id < 10 ? Test(id + 1) : nil
    }
}

let test = Test(5)
let arr = Array(test)

It compiles. And doesn't even throw any runtime errors.
But instead of getting the array [5, 6, 7, 8, 9] as a result, I get an infinite loop! next() is called infinitely many times.
I thought that nil in next() is a natural indicator of the end of sequence. But apparently it's not.

NicolasElPapu
  • 1,612
  • 2
  • 11
  • 26
Roman
  • 1,309
  • 14
  • 23
  • 1
    This is kind of strange, do you really way `Test` to be a sequence that vends `Test`s? – Alexander May 01 '20 at 01:29
  • @ Alexander - Reinstate Monica Yes. But maybe for this purpose it's more appropriate to use GeneratorType instead of Sequence. I didn't quite catch the difference between this two kinds of type yet. – Roman May 01 '20 at 02:49
  • @ Alexander - Reinstate Monica Why is it strange, by the way?)) – Roman May 01 '20 at 03:11
  • `GeneratorType` was just the name of `IteratorProtocol` before Swift 3. It's strange because `Array(Test(5))` would result in `[Test(5), Test(6), Test(7), Test(8), Test(9)]`, so it has this "unfolding" characteristic. That might be useful, but this is a really strange way to do it. So what exactly is it you're trying to achieve? – Alexander May 01 '20 at 13:35
  • @Alexander - Reinstate Monica Regarding GeneratorType is very useful info, thank you. As for "unfolding characteristic" I'm agree. Now I understand why it indeed was strange, and have managed to implement it properly (in this sense) to achieve my real goal: https://stackoverflow.com/a/61552968/2167345 – Roman May 02 '20 at 00:54
  • @Leo Dabus I've also posted my own solution to the initial question. Take a look, it might be interesting: https://stackoverflow.com/a/61552477/2167345 – Roman May 02 '20 at 01:11

3 Answers3

2

self.id never changes, so it never reaches 10.

Alexander
  • 59,041
  • 12
  • 98
  • 151
  • It does change. Here in next(): "Test(id + 1)". It does reach 10. I tested it in playground before to post. – Roman May 01 '20 at 01:03
  • 1
    I get it. You probably mean the same as @Harsh means. Ok, thanks for the hint. – Roman May 01 '20 at 01:11
  • Yep. Though you can fix it more simply with a defer statement, `defer { self.id += 1 }` – Alexander May 01 '20 at 01:14
  • this is not enough considering OP logic it will result in 6...10 instead of 5...9 besides the fact of creating a new instance on every iteration – Leo Dabus May 01 '20 at 02:24
1

It should be something like this

struct Test: IteratorProtocol, Sequence {
    var id: Int

    init(_ id: Int) {
        self.id = id
    }

    mutating func next() -> Test? {
       defer { id += 1 } 
       return id < 10 ? self : nil
    }
}

print(Array(Test(6)))

Another example

 struct Countdown: Sequence, IteratorProtocol {
     var count: Int

     mutating func next() -> Int? {
         if count == 0 {
             return nil
         } else {
             defer { count -= 1 }
             return count
         }
     }
 }

 let threeToGo = Countdown(count: 3)
 for i in threeToGo {
     print(i)
 }
 // Prints "3"
 // Prints "2"
 // Prints "1"
Harsh
  • 2,852
  • 1
  • 13
  • 27
  • 2
    You are unnecessarily creating a new instance on every iteration. `guard id < 10 else { return nil }` `id += 1` `return self` – Leo Dabus May 01 '20 at 01:59
  • @LeoDabus Updated. – Harsh May 01 '20 at 02:07
  • I think the two examples are good, one using ternary operator and the other just `if else` statement. :) – Harsh May 01 '20 at 02:21
  • Okey guys, thank you for everyone. Very useful comments. Sadly, but I have to mark only a one answer as accepted. – Roman May 01 '20 at 03:01
  • 1
    @LeoDabus Given that it's a struct, a new instance is being returned either way. Although `self` is more concise. – Alexander May 01 '20 at 13:35
  • @Harsh I've also posted my own solution to the initial question. Take a look, it might be interesting: https://stackoverflow.com/a/61552477/2167345 – Roman May 02 '20 at 01:06
0

Appears, there is a built-in function, that completely suits the logic of my initial question in this post.

sequence(first:next:)
Returns a sequence formed from first and repeated lazy applications of next.

struct Test {
    var id: Int

    init(_ id: Int) {
        self.id = id
    }
}

let seq = sequence(first: Test(5), next: { test in
    let id = test.id + 1
    return id < 10 ? Test(id) : nil
})

let arr = Array(seq)
Roman
  • 1,309
  • 14
  • 23
  • I would simplify this by removing `Test` from the sequence entirely. Just generate the numbers, then simply `.map(Test.init)`. Or eeeeeeeven more simplified, just: `(5...9).map(Test.init)`. – Alexander May 02 '20 at 03:34