Возьмите первые N элементов из бесконечного итератора или генератора в Swift - PullRequest
2 голосов
/ 06 июля 2019

Я часто использую генераторы в Python. Сейчас я пытаюсь сделать нечто подобное в Swift.

Из бесконечного факториального генератора в Python.

def gen_factorial():
  current = 1
  N = 1
  while True:
    yield current
    N += 1
    current *= N

Версия Swift будет

struct gen_factorial: Sequence, IteratorProtocol 
{
  var current = 1
  var N = 1
  mutating func next()-> Int?{
    defer {
      N += 1
      current *= N
    }
    return current
  }
}

Я проверяю это, беря первые 4 элемента с

zip(1...4, 
    gen_factorial()
   ).map{$1}

И получите 1, 2, 6, 24, как и ожидалось.

Но когда я хочу написать вспомогательную функцию take(n, gen), чтобы упростить ее, я не могу.

func take(_ n: Int, _ it: AnyIterator<Int>) -> [Int]{
  return zip(1...n, it).map {$1}
}

take(4, gen_factorial())

Сообщение об ошибке:

error: cannot convert value of type 'gen_factorial' to expected argument type 'AnyIterator<Int>'

Какого типа должен быть it, если не AnyIterator?

Я все еще новичок в Swift. Пожалуйста, помогите.

1 Ответ

2 голосов
/ 06 июля 2019

AnyIterator - это тип бетона , соответствующий протоколу IteratorSequence).Чтобы передать свой итератор этой функции, вам нужно заключить его в AnyIterator:

print(take(4, AnyIterator(gen_factorial())))
// [1, 2, 6, 24]

Лучшим решением было бы сделать функцию generic так, чтобы онапроизвольный тип последовательности в качестве аргумента:

func take<S: Sequence>(_ n: Int, _ it: S) -> [S.Element]{
    return zip(1...n, it).map {$1}
}

print(take(4, gen_factorial()))
// [1, 2, 6, 24]

Примечания:

  • Вместо вспомогательной функции вы можете использовать существующую prefix(_ maxLength:) метод Sequence:

    print(Array(gen_factorial().prefix(4)))
    // [1, 2, 6, 24]
    
  • Соглашения об именах Swift для типов в верхнем верблюжьем регистре.

...