Swift SequenceType不起作用

时间:2015-04-02 21:47:42

标签: swift foreach generator sequence for-in-loop

我正在尝试实现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循环的逻辑可能是错误的 - 意味着我不会得到我真正想要的结果 - 但无论如何,在满足{{{{ 1}}协议要求,我认为我做得那么好。 是什么导致了这个错误?

3 个答案:

答案 0 :(得分:7)

这不是发电机的工作原理。假设您想要提供一定数量的随机数,那么您有一个正确的想法,即将最大值作为参数,但您需要将其存储在生成器中,还需要将其存储在某个状态。< / p>

使用生成器的想法是它存储其状态,每次调用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
            }
        }
    }
}

答案 1 :(得分:1)

您看到的错误消息:

  

&#39;输入RandomNumberSequence&#39;不符合协议&#39; SequenceType&#39;

始终意味着您的类或结构缺少协议声明所需的内容。

在这种情况下,我们错过了generate() -> Generator方法。 &#34;但是,它就在那里!&#34;你说?嗯,确实如此,但它没有编译。

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方法并允许您的结构现在符合协议。

答案 2 :(得分:0)

使用Swift 3,您可以选择三个RandomNumbersSequence实现中的一个来解决您的问题。

1。使用符合Sequence协议的结构和符合IteratorProtocol协议的结构

以下Playground代码显示了如何实现符合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。使用符合SequenceIteratorProtocol协议

的结构

以下Playground代码显示了如何实现符合RandomNumbersSequenceSequence协议的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方法的返回类型。以下Playground代码显示了如何使用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