Swift中的SequenceType无法工作

5

我正在尝试实现一个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 协议的要求而言,我认为我已经做得没错了。那么是什么导致了这个错误呢?

只是想确认一下 - 你正在尝试编写一个生成x个随机数的序列吗?例如,如果你创建了 let seq = RandomNumbersSequence(maxNum: 5),然后执行 for i in seq { },你将获得5个随机数? - Airspeed Velocity
3个回答

6
这并不是生成器的正确使用方法。假设您想提供一组随机数,您对于将最大值作为参数有正确的想法,但您还需要将其存储在生成器中,并存储一些状态以确定当前位置。
生成器的思想是它存储它的状态,并且每次调用 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都会递增,直到达到最大值,然后生成器开始返回nilSequenceType的目的是提供新鲜的生成器:
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
            }
        }
    }
}

nhgrif 好的,我正想问你这个问题。但是没关系,你们两个都值得赞扬。@Airspeed - 感谢你的解释 - 很详细,现在我完全明白了。谢谢。 - sirab333

1
您看到的错误信息如下:
'类型RandomNumberSequence'不符合协议'SequenceType'
这通常意味着您的类或结构体缺少协议声明为必需的内容。
在这种情况下,我们缺少了“generate() -> Generator”方法。您可能会说:“但是它就在那里啊!”是的,它确实在那里,但它无法编译。
func generate() -> Generator {
    for i in 1...numberOfRandomNumbers {
        var randNum = Generator()
        randNum.next()
        return randNum
    }
} 
问题是,如果你用一个小于或等于零的numberOfRandomNumbers初始化你的结构体,那么你的循环将执行零次,generate不能返回任何东西。

我不确定你在这个循环中要实现的逻辑,但我们可以通过添加一个返回语句来修复编译错误,这个返回语句将会返回一个Generator

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

这样做不能实现您想要的目标。这不是生成器应该工作的方式。但这将修复“generate() -> Generator”方法,使您的结构现在符合协议。

嗯,看起来很不错 - 但还是不起作用。我按照你建议的更改了,但现在我得到了这个: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。非常奇怪... - sirab333
是的,我认为这是Playgrounds的问题。当作为Xcode项目运行时似乎没问题...还在检查中... - sirab333
是的,它肯定是Playgrounds - 在Xcode中工作 - 但是...我遇到了一个无限循环 - 它会一直生成随机数。我在这里尝试做的是简单地创建一个由X个随机数组成的序列,每次创建序列时都可以传入X的值。但是当我像这样调用它:var randySequence = RandomNumbersSequence(maxNum: 10),它就一直运行下去了...我以为我的for循环会将它限制在我传入的任何数字上。显然,我对这个序列业务还没有完全理解。有什么想法吗? - sirab333
发布一个关于你的 for 循环出现问题的问题。这个问题是关于遵循协议的。 - nhgrif

0

在Swift 3中,您可以选择三种RandomNumbersSequence实现之一来解决您的问题。


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

以下的 Playground 代码展示了如何实现一个符合 Sequence 协议的 RandomNumbersSequence 结构体,并使用符合 IteratorProtocol 协议的 RandomNumbersIterator 结构体:

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代码展示了如何实现一个符合SequenceIteratorProtocol协议的RandomNumbersSequence结构体:

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协议的结构体

作为先前实现的替代方案,您可以在符合Sequence协议的结构体内部使用AnyIterator<T>作为makeIterator方法的返回类型。以下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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接