2015-04-02 25 views
5

Próbuję zaimplementować przykład SequenceType/GeneratorType i uzyskać błąd, który nie ma sensu.Swift SequenceType nie działa

Oto kod:

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

Kiedy nazywają to (na placach zabaw) działa doskonale:

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

Jak dotąd tak dobre.

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

} 

To co generuje błąd: 'Type RandomNumberSequence' does not conform to protocol 'SequenceType'. (Xcode jest pokazywanie tego prawa błędu na tej pierwszej linii oświadczenie oświadczenie struct RandomNumbersSequence:SequenceType.)

I rzeczywiście myśleć logika mojego for pętli może być źle - co oznacza, że ​​nie dostanie wyniki faktycznie chcę - ale niezależnie od tego, jeśli chodzi o spełnienie wymagań protokołu, uważam, że mam rację. Co powoduje ten błąd?

+0

Tylko do sprawdzenia - próbujesz napisać e sekwencja, która podaje x liczbę liczb losowych? To znaczy. jeśli utworzysz 'let seq = RandomNumbersSequence (maxNum: 5)', to czy 'for i in seq {}' dostaniesz 5 liczb losowych? –

Odpowiedz

7

To nie tak działa generatory. Zakładając, że chcesz obsłużyć określoną liczbę losowych liczb, masz rację biorąc pod uwagę przyjmowanie maksimum jako parametru, ale musisz również zachować to w generatorze, a także pewien stan, w którym jest on przeznaczony.

Pomysł z generatorem polega na tym, że przechowuje on swój stan i za każdym razem, gdy wywołujesz next(), zwracasz następny element. Więc jeśli chcesz, aby wygenerować przebieg do n numerów, można zrobić coś jak następuje:

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

uwaga, nie trzeba tu for pętlę.Za każdym razem, gdy wywoływana jest next(), i jest inkrementowana, aż osiągnie maksimum, następnie generator zaczyna zwracać nil.

Celem SequenceType ma służyć świeże generatorów up:

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

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

Biorąc to pod uwagę, można go używać do generowania sekwencji ustalonej liczby losowych liczb całkowitych:

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 

Tworzenie te generatory stanowe są dość powszechnym problemem, więc biblioteka standardowa ma strukturę pomocniczą, GeneratorOf, która pozwala pominąć jej definiowanie. To trwa zamknięcie że za każdym razem to się nazywa powinien wrócić następną wartość do generowania:

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

To powinna być zaakceptowana odpowiedź. – nhgrif

+0

nhgrif ok, chciałem cię o to zapytać. Ale zrobi to. Oboje zasługują na kredyt. @Airspeed - dziękuję za wyjaśnienie - jest bardzo dokładny i całkowicie go rozumiem. Dziękuję Ci. – sirab333

1

Komunikat o błędzie widzisz:

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

zawsze oznacza, że ​​klasa lub struktura czegoś brakuje, że protokół deklaruje jako wymagane.

W tym przypadku brakuje nam metody generate() -> Generator. "Ale, jest tam!" mówisz? Cóż, jest, ale nie kompiluje.

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

Problem polega na tym, co zrobić, jeśli zainicjować struct z numberOfRandomNumbers mniejsza lub równa 0? Twoja pętla wykonuje zero razy i generate nie może niczego zwrócić.

nie jestem pewien dokładnie co logika starasz się zrobić w tej pętli, ale możemy naprawić błędy kompilacji po prostu przez dodanie instrukcji return, która będzie zwracać Generator:

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

Nie zrobi to, co chcesz osiągnąć. Nie jest tak, jak powinny działać generatory. Ale naprawi on metodę generate() -> Generator i pozwoli twojej strukturze na dostosowanie się do protokołu.

+0

Hmm, to wyglądało naprawdę dobrze - ale wciąż nie działa. Wprowadziłem zmianę, którą zasugerowałeś, ale teraz otrzymuję to: 'Swift._Sequence_Type ... note: protokół wymaga typu zagnieżdżonego 'Generator' Typalias Generator: GeneratorType'. Również to: 'uwaga: ewentualnie zamierzony mecz 'Generator' nie jest zgodny z typem GeneratorType Generator = RandomNumberGenerator'. Bardzo dziwne ... – sirab333

+0

Tak, myślę, że to coś na Plac zabaw. Wydaje się, że jest OK, gdy uruchamiany jest jako projekt Xcode ... wciąż sprawdzam ... – sirab333

+0

Tak, to jest def. Place zabaw - działa w Xcode - JEDNAK ... Dostaję nieskończoną pętlę - na bieżąco generuję losowe numery na zawsze. To, co tu robię, to po prostu utworzyć ciąg liczb X liczb losowych, w których mogę podać wartość X za każdym razem, gdy tworzę sekwencję. Ale kiedy to nazywam - w ten sposób: 'var randySequence = RandomNumbersSequence (maxNum: 10)', po prostu działa i działa i działa ... Myślałem, że moja pętla 'for' ograniczy go do liczby, którą podaję. Najwyraźniej nie mam pojęcia o tym biznesie sekwencyjnym. Jakieś pomysły? – sirab333

0

Z Swift 3, można wybrać jeden z trzech RandomNumbersSequence wdrożeń w celu rozwiązania problemu.


1. Korzystanie z struct, który jest zgodny z protokołem Sequence i struct zgodnego z IteratorProtocol protokołu

Poniższy kod zabaw pokazuje jak zaimplementować RandomNumbersSequence struct zgodnego z Sequence i że używa RandomNumbersIterator struct który jest zgodny z protokołem: 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) 
    } 

} 

Wykorzystanie # 1:

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

/* 
may print: 
5 
7 
3 
*/ 

Zastosowanie 2:

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

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

Zastosowanie 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. Za pomocą zgodnego z struct Sequence i IteratorProtocol protokołów

Następujący kod zabaw pokazuje, jak zaimplementować RandomNumbersSequence struktura zgodnych Sequence i IteratorProtocol protokołów:

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 
    } 

} 

Zastosowanie # 1:

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

/* 
may print: 
5 
7 
3 
*/ 

Zastosowanie 2:

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

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

Zastosowanie 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.Korzystanie AnyIterator i struct zgodnego z Sequence

Jako alternatywę do poprzedniej realizacji, można użyć AnyIterator<T> jako typ zwracanej metody makeIterator wewnątrz swojej struktury protokołu Sequence zgodnego. Poniższy kod zabaw pokazuje jak wdrożyć go z RandomNumbersSequence struct:

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 
    } 

} 

Wykorzystanie # 1:

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

/* 
may print: 
5 
7 
3 
*/ 

Wykorzystanie # 2:

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

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

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