2015-04-02 16 views
5

Ich versuche ein SequenceType/GeneratorType-Beispiel zu implementieren und einen Fehler zu erhalten, der nicht ganz Sinn macht.Swift SequenceType funktioniert nicht

Hier ist der Code:

// 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)) 
    } 
} 

Als ich das nennen (in Spielplatz) ist es sehr gut funktioniert:

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()!)") 

So weit, so so gut.

Als nächstes ist der 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 
     } 
    } 

} 

Das ist, was ein Fehler ist zu erzeugen: 'Type RandomNumberSequence' does not conform to protocol 'SequenceType'. (Xcode zeigt diesen Fehler rechts auf dieser ersten Zeile der Erklärung struct RandomNumbersSequence:SequenceType Erklärung.)

Ich glaube tatsächlich, die Logik meiner for Schleife falsch sein kann - was bedeutet, ich werde nicht die Ergebnisse, ich will eigentlich - aber Unabhängig davon, was das Protokoll SequenceType erfordert, glaube ich, dass ich so viel richtig verstanden habe. Also, was verursacht diesen Fehler?

+0

Nur um zu überprüfen - Sie versuchen zu schreiben e eine Sequenz, die x Anzahl von Zufallszahlen liefert? I.e. Wenn Sie 'let seq = RandomNumbersSequence (maxNum: 5)' erstellen, dann tun Sie 'für i in seq {}' Sie erhalten 5 Zufallszahlen? –

Antwort

7

Dies ist nicht ganz so, wie Generatoren arbeiten. Angenommen, Sie möchten eine bestimmte Anzahl von Zufallszahlen bereitstellen, haben Sie die richtige Vorstellung davon, ein Maximum als Parameter zu verwenden, aber Sie müssen das auch im Generator speichern, sowie einen Status für das, was es tun soll.

Die Idee mit einem Generator ist, dass es seinen Zustand speichert, und jedes Mal, wenn Sie next() aufrufen, geben Sie das nächste Element zurück. Wenn Sie also einen Lauf von bis zu n Zahlen erzeugen möchten, können Sie so etwas wie das folgende tun könnte:

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 
     } 
    } 
} 

Hinweis, brauchen Sie keine for Schleife hier.Jedes Mal, wenn next() aufgerufen wird, wird i inkrementiert, bis das Maximum erreicht ist, und der Generator beginnt mit der Rückgabe nil.

Der Zweck SequenceType ist frisch Generatoren zu servieren:

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

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

dies gegeben, haben Sie jetzt eine Folge von einer festen Anzahl von Zufallszahlen zu erzeugen, verwenden können:

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 

Erstellen Diese statusbehafteten Generatoren sind ein ziemlich häufiges Problem, daher hat die Standardbibliothek eine Hilfsstruktur, GeneratorOf, die es erlaubt, diese zu überspringen. Es dauert eine Schließung, die jedes Mal aufgerufen wird, soll den nächsten Wert zurückgeben zu generieren:

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 
      } 
     } 
    } 
} 
+0

Dies sollte die akzeptierte Antwort sein. – nhgrif

+0

nhgrif ok, ich wollte dich nur danach fragen. Aber wird es tun. Sie verdienen beide Kredit. @Airspeed - danke für die Erklärung - es ist sehr gründlich und ich bekomme es jetzt total. Vielen Dank. – sirab333

1

Die Fehlermeldung Sie sehen:

'Type RandomNumberSequence' does not conform to protocol 'SequenceType'

bedeutet immer, dass Ihre Klasse oder Struktur etwas fehlt, dass das Protokoll nach Bedarf erklärt. In diesem Fall fehlt die Methode generate() -> Generator. "Aber, es ist genau dort!" du sagst? Nun, ist es, aber es kompiliert nicht.

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

Das Problem ist, was passiert, wenn Sie Ihre Struktur mit numberOfRandomNumbers weniger als oder gleich 0 initialisieren? Ihre Schleife wird null Mal ausgeführt und generate kann nichts zurückgeben.

Ich bin nicht sicher genau welche Logik Sie versuchen, in dieser Schleife zu tun, aber wir können die Kompilierung Fehler beheben, indem Sie einfach eine return-Anweisung hinzufügen, die eine Generator zurück:

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

Dies wird nicht tun, was Sie erreichen möchten. So sollen Generatoren nicht funktionieren. Aber es wird die generate() -> Generator Methode reparieren und erlauben, dass Ihre Struktur nun dem Protokoll entspricht.

+0

Hmm, das sah wirklich gut aus - aber es funktioniert immer noch nicht. Ich habe die Änderung, die Sie vorgeschlagen haben, aber jetzt bekomme ich das: 'Swift._Sequence_Type ... Hinweis: Protokoll erfordert verschachtelte Typ 'Generator' Typalias Generator: GeneratorType'. Auch das: 'Hinweis: Möglicherweise beabsichtigte Übereinstimmung 'Generator' entspricht nicht den 'GeneratorType' Typalias Generator = RandomNumberGenerator'. Sehr merkwürdig ... – sirab333

+0

Ja, ich denke, es ist eine Sache mit Playgrounds. Es scheint in Ordnung zu sein, wenn es als Xcode-Projekt ausgeführt wird ... immer noch überprüft ... – sirab333

+0

Ja, es ist def. Spielplätze - funktioniert in Xcode - JEDOCH ... Ich bekomme eine Endlosschleife - es erzeugt ständig Zufallszahlen. Was ich hier machen soll, ist einfach eine Sequenz von X Zahlen von Zufallszahlen zu erstellen, wo ich jedes Mal, wenn ich eine Sequenz erstelle, den Wert für X übergeben kann. Aber wenn ich es anrufe - wie folgt: 'var randySequence = RandomNumbersSequence (maxNum: 10)', läuft es und läuft und läuft ... Ich dachte, meine 'for'-Schleife würde es auf die Nummer beschränken, die ich passiere. Offensichtlich bekomme ich nicht gerade etwas von diesem Sequenzierungsgeschäft. Irgendwelche Ideen? – sirab333

0

Mit Swift 3, eine der drei RandomNumbersSequence Implementierungen, um Ihr Problem zu lösen wählen können.


1. eine Struktur verwenden, die Sequence Protokoll und eine Struktur entspricht, die IteratorProtocol Protokoll

Der folgende Spielplatz Code zeigt, wie implementieren eine RandomNumbersSequence Struktur, die zu Sequence entspricht und dass verwendet eine RandomNumbersIterator Struktur entspricht das entspricht IteratorProtocol Protokoll:

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) 
    } 

} 

Usage # 1:

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

/* 
may print: 
5 
7 
3 
*/ 

Usage # 2:

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

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

Usage # 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. eine Struktur verwenden, die

Die folgende Spielplatz Code Sequence und IteratorProtocol Protokollen konform zeigt, wie man eineimplementiertstruct, die Sequence und IteratorProtocol Protokolle entspricht:

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 
    } 

} 

Usage # 1:

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

/* 
may print: 
5 
7 
3 
*/ 

Usage # 2:

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

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

Usage # 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.Mit AnyIterator und einer Struktur, die

Als Alternative zur bisherigen Umsetzung Sequence

entspricht, können Sie AnyIterator<T> als Rückgabetyp der makeIterator Methode in Ihrer Sequence Protokoll entspricht Struktur verwenden. Der folgende Spielplatz Code zeigt, wie es implementieren mit Ihrem RandomNumbersSequence Struktur:

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 
    } 

} 

Usage # 1:

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

/* 
may print: 
5 
7 
3 
*/ 

Usage # 2:

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

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

Usage # 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