Swift SequenceType не работает

Я пытаюсь реализовать пример SequenceType / GeneratorType и получаю ошибку, которая не совсем понятна.

Вот код:

// Here's my GeneratorType - it creates a random-number Generator:
struct RandomNumberGenerator:GeneratorType {
    typealias Element = Int
    mutating func next() -> Element? {
       return Int(arc4random_uniform(100))
    }
} 

Когда я вызываю это (в Playgrounds), он отлично работает:

var randyNum = RandomNumberGenerator()
randyNum.next()   // this shows a valid random number in the Gutter
// And calling it from within a println also works:
println("randyNum = \(randyNum.next()!)")

Пока все хорошо.

Далее идет SequenceType:

struct RandomNumbersSequence:SequenceType {
    typealias Generator = RandomNumberGenerator
    var numberOfRandomNumbers:Int

    init(maxNum:Int) {
        numberOfRandomNumbers = maxNum
    }

    func generate() -> Generator {
        for i in 1...numberOfRandomNumbers {
          var randNum = Generator()
          randNum.next()
          return randNum
       }
    } 

}

Вот что вызывает ошибку: 'Type RandomNumberSequence' does not conform to protocol 'SequenceType'. (Xcode показывает эту ошибку прямо в первой строке объявления struct RandomNumbersSequence:SequenceType.)

Я на самом деле думаю, что логика моего for цикла может быть неправильным - это означает, что я не получу тех результатов, которые действительно хочу, - но, несмотря на это, с точки зрения удовлетворения требований протокола SequenceType, я думаю, что получил это правильно. Так что вызывает эту ошибку?


person sirab333    schedule 02.04.2015    source источник
comment
Просто чтобы проверить - вы пытаетесь написать последовательность, которая обслуживает x случайных чисел? Т.е. если вы создадите let seq = RandomNumbersSequence(maxNum: 5), то for i in seq { } вы получите 5 случайных чисел?   -  person Airspeed Velocity    schedule 03.04.2015


Ответы (3)


Генераторы работают не совсем так. Предполагая, что вы хотите обслуживать заданное количество случайных чисел, у вас есть правильное представление о том, чтобы взять максимум в качестве параметра, но вам также необходимо сохранить его в генераторе, а также некоторое состояние, в котором оно находится.

Идея генератора заключается в том, что он сохраняет свое состояние, и каждый раз, когда вы вызываете next(), вы возвращаете следующий элемент. Поэтому, если вы хотите сгенерировать серию до n чисел, вы можете сделать что-то вроде следующего:

struct RandomNumberGenerator: GeneratorType {
    let n: Int
    var i = 0
    init(count: Int) { self.n = count }

    mutating func next() -> Int? {
        if i++ < n {
            return Int(arc4random_uniform(100))
        }
        else {
            return nil
        }
    }
}

Обратите внимание, что for цикл здесь не нужен. Каждый раз, когда вызывается next(), i увеличивается, пока не достигнет максимума, затем генератор начинает возвращать nil.

Цель SequenceType - обслуживать новые генераторы:

struct RandomNumberSequence: SequenceType {
    let n: Int
    init(count: Int) { self.n = count }

    func generate() -> RandomNumberGenerator {
        return RandomNumberGenerator(count: n)
    }
}

Учитывая это, теперь вы можете использовать его для генерации последовательности из фиксированного числа случайных целых чисел:

let seq = RandomNumberSequence(count: 3)

for x in seq {
     // loops 3 times with x being a new random number each time
}

// every time you use seq, you get a new set of 3 numbers
",".join(map(seq,toString))  // prints 3 comma-separated random nums

// but when you create a generator, it gets “used up”
var gen = seq.generate()
println(gen.next()) // prints a random number
println(gen.next()) // prints another random number
println(gen.next()) // prints the third
println(gen.next()) // prints nil
println(gen.next()) // and will keep printing nil

gen = seq.generate()
println(gen.next()) // will print the first of a new set of 3 numbers

Создание этих генераторов с отслеживанием состояния - довольно распространенная проблема, поэтому в стандартной библиотеке есть вспомогательная структура GeneratorOf, которая позволяет вам пропустить их определение. Требуется замыкание, которое при каждом вызове должно возвращать следующее значение для генерации:

struct RandomNumbersSequence: SequenceType {
    let maxNum: Int

    init(maxNum: Int) { self.maxNum = maxNum }

    func generate() -> GeneratorOf<Int> {
        // counter to track how many have been generated
        var n = 0
        return GeneratorOf {
            // the closure “captures” n
            if n++ < self.maxNum {
                return Int(arc4random_uniform(100))
            }
            else {
                return nil
            }
        }
    }
}
person Airspeed Velocity    schedule 02.04.2015
comment
nhgrif хорошо, я просто хотел спросить тебя об этом. Но сойдет. Вы оба заслуживаете похвалы. @Airspeed - спасибо за объяснение - оно очень подробное, и теперь я полностью понял. Спасибо. - person sirab333; 03.04.2015

Сообщение об ошибке, которое вы видите:

Тип RandomNumberSequence не соответствует протоколу SequenceType

Всегда означает, что в вашем классе или структуре отсутствует что-то, что протокол объявляет по мере необходимости.

В этом случае нам не хватает метода generate() -> Generator. "Но это прямо там!" ты говоришь? Ну, это так, но это не компилирование.

func generate() -> Generator {
    for i in 1...numberOfRandomNumbers {
        var randNum = Generator()
        randNum.next()
        return randNum
    }
} 

Проблема в том, что если вы инициализируете свою структуру с numberOfRandomNumbers меньше или равным 0? Ваш цикл выполняется ноль раз, и generate ничего не может вернуть.

Я не уверен точно, какую логику вы пытаетесь реализовать в этом цикле, но мы можем исправить ошибки компиляции, просто добавив оператор return, который вернет Generator:

func generate() -> Generator {
    for i in 1...numberOfRandomNumbers {
        var randNum = Generator()
        randNum.next()
        return randNum
    }
    return Generator()
} 

Это не сделает того, чего вы пытаетесь достичь. Генераторы должны работать не так. Но это исправит метод generate() -> Generator и позволит вашей структуре теперь соответствовать протоколу.

person nhgrif    schedule 02.04.2015
comment
Хм, это выглядело действительно хорошо, но все еще не работает. Я внес предложенное вами изменение, но теперь получаю следующее: Swift._Sequence_Type... note: protocol requires nested type 'Generator' typealias Generator : GeneratorType. Также это: note: possibly intended match 'Generator' does not conform to 'GeneratorType' typealias Generator = RandomNumberGenerator. Очень странный... - person sirab333; 03.04.2015
comment
Да, я думаю, это связано с игровыми площадками. Кажется, все в порядке при запуске в качестве проекта Xcode ... все еще проверяю ... - person sirab333; 03.04.2015
comment
Да, это деф. Playgrounds - работает в Xcode - ОДНАКО ... Я получаю бесконечный цикл - он постоянно генерирует случайные числа. То, что я пытаюсь сделать здесь, - это просто создать последовательность из X случайных чисел, где я могу передавать значение для X каждый раз, когда я создаю последовательность. Но когда я вызываю его - вот так: var randySequence = RandomNumbersSequence(maxNum: 10), он просто запускается, запускается и запускается ... Я думал, что мой цикл for ограничит его тем числом, которое я передаю. Очевидно, я не совсем понимаю этот бизнес по секвенированию. Любые идеи? - person sirab333; 03.04.2015
comment
Задайте вопрос о сломанной for петле. Этот вопрос касался соблюдения протокола. - person nhgrif; 03.04.2015

В Swift 3 вы можете выбрать одну из трех RandomNumbersSequence реализаций, чтобы решить вашу проблему.


1. Использование структуры, соответствующей Sequence протоколу, и структуры, соответствующей IteratorProtocol протоколу

Следующий код игровой площадки показывает, как реализовать структуру RandomNumbersSequence, соответствующую Sequence и использующую структуру RandomNumbersIterator, соответствующую протоколу IteratorProtocol:

import Darwin // required for arc4random_uniform

struct RandomNumbersIterator: IteratorProtocol {
    
    let maxNum: Int
    var n = 0

    init(maxNum: Int) {
        self.maxNum = maxNum
    }

    mutating func next() -> Int? {
        n += 1
        return n <= self.maxNum ? Int(arc4random_uniform(10)) : nil
    }

}

struct RandomNumbersSequence: Sequence {
    
    let maxNum: Int
    
    init(maxNum: Int) {
        self.maxNum = maxNum
    }
    
    func makeIterator() -> RandomNumbersIterator {
        return RandomNumbersIterator(maxNum: maxNum)
    }
    
}

Использование №1:

for value in RandomNumbersSequence(maxNum: 3) {
    print(value)
}

/*
 may print:
 5
 7
 3
 */

Использование № 2:

let randomArray = Array(RandomNumbersSequence(maxNum: 3))
print(randomArray)

/*
 may print: [7, 6, 1]
 */

Использование № 3:

let randomSequence = RandomNumbersSequence(maxNum: 3)
var randomGenerator = randomSequence.makeIterator()

randomGenerator.next() // may return: 4
randomGenerator.next() // may return: 8
randomGenerator.next() // may return: 3
randomGenerator.next() // will return: nil

2. Использование структуры, соответствующей протоколам Sequence и IteratorProtocol.

Следующий код игровой площадки показывает, как реализовать структуру RandomNumbersSequence, соответствующую протоколам Sequence и IteratorProtocol:

import Darwin // required for arc4random_uniform

struct RandomNumbersSequence: Sequence, IteratorProtocol {
    
    let maxNum: Int
    var n = 0
    
    init(maxNum: Int) {
        self.maxNum = maxNum
    }
    
    mutating func next() -> Int? {
        n += 1
        return n <= self.maxNum ? Int(arc4random_uniform(10)) : nil
    }
    
}

Использование №1:

for value in RandomNumbersSequence(maxNum: 3) {
    print(value)
}

/*
 may print:
 5
 7
 3
 */

Использование № 2:

let randomArray = Array(RandomNumbersSequence(maxNum: 3))
print(randomArray)

/*
 may print: [7, 6, 1]
 */

Использование № 3:

var randomSequence = RandomNumbersSequence(maxNum: 3)

randomSequence.next() // may return: 4
randomSequence.next() // may return: 8
randomSequence.next() // may return: 3
randomSequence.next() // will return: nil

3. Использование AnyIterator и структуры, соответствующей Sequence

В качестве альтернативы предыдущей реализации вы можете использовать AnyIterator<T> как возвращаемый тип makeIterator метода внутри вашей Sequence структуры, соответствующей протоколу. Следующий код игровой площадки показывает, как реализовать это с помощью вашей RandomNumbersSequence структуры:

import Darwin // required for arc4random_uniform

struct RandomNumbersSequence: Sequence {
    
    let maxNum: Int
    
    init(maxNum: Int) {
        self.maxNum = maxNum
    }
    
    func makeIterator() -> AnyIterator<Int> {
        var n = 0
        let iterator: AnyIterator<Int> = AnyIterator {
            n += 1
            return n <= self.maxNum ? Int(arc4random_uniform(10)) : nil
        }
        return iterator
    }
    
}

Использование №1:

for value in RandomNumbersSequence(maxNum: 3) {
    print(value)
}

/*
may print:
5
7
3
*/

Использование № 2:

let randomArray = Array(RandomNumbersSequence(maxNum: 3))
print(randomArray)

/*
may print: [7, 6, 1]
*/

Использование № 3:

let randomSequence = RandomNumbersSequence(maxNum: 3)
let randomGenerator = randomSequence.makeIterator()

randomGenerator.next() // may return: 4
randomGenerator.next() // may return: 8
randomGenerator.next() // may return: 3
randomGenerator.next() // will return: nil
person Imanou Petit    schedule 19.12.2015