Итератор, возвращающий Generic Collection - Swift 4.2 - PullRequest
0 голосов
/ 07 ноября 2018

Я могу написать Итераторы следующим образом:

enum Stage { case a, ab, end }

struct SetMaker<Input: Hashable>: Sequence, IteratorProtocol {
  var a,b: Input
  var stage = Stage.a

  init(a: Input, b: Input) {
    self.a = a
    self.b = b
  }

  mutating func next() -> Set<Input>? {
    switch stage {
    case .a:    stage = .ab;   return Set<Input>([a])
    case .ab:   stage = .end;  return Set<Input>([a,b])
    case .end:                 return nil
    }
  }
}

let setMaker = SetMaker(a: "A", b: "B")
for x in setMaker {
  print(x)
}

struct ArrayMaker<Input: Hashable>: Sequence, IteratorProtocol {
  var a: Input
  var b: Input
  var stage = Stage.a

  init(a: Input, b: Input) {
    self.a = a
    self.b = b
  }

  mutating func next() -> Array<Input>? {
    switch stage {
    case .a:    stage = .ab;   return Array<Input>([a])
    case .ab:   stage = .end;  return Array<Input>([a,b])
    case .end:                 return nil
    }
  }
}

let arrayMaker = ArrayMaker(a: "A", b: "B")
for x in arrayMaker {
  print(x)
}

Первый возвращает последовательность наборов, а второй возвращает последовательность массивов.

Они оба работают нормально, но мне нравится держать мой код "СУХИМ" (т.е. не повторять себя).

Так что я хотел бы написать что-то общее, что позволит создать любой из них. Моя попытка:

struct AnyMaker<Input: Hashable, CollectionType>: Sequence, IteratorProtocol {
  var a,b: Input
  var stage = Stage.a

  init(a: Input, b: Input) {
    self.a = a
    self.b = b
  }

  mutating func next() -> CollectionType<Input>? {
    switch stage {
    case .a:    stage = .ab;   return CollectionType<Input>([a])
    case .ab:   stage = .end;  return CollectionType<Input>([a,b])
    case .end:                 return nil
    }
  }
}

Но это не компилируется. Любая помощь приветствуется: -)

Редактировать ...

@ Роб сделал хорошее предложение, которое помогло мне отойти оттуда - см. Его ответ. Но если я хочу, чтобы коллекция иногда была множеством, тогда возникает проблема, потому что Set не RangeReplaceable.

Другими словами, я создал немного другой код:

struct Pairs<C>: Sequence, IteratorProtocol
where C: RangeReplaceableCollection {

  var collection: C
  var index: C.Index

  init(_ collection: C) {
    self.collection = collection
    index = self.collection.startIndex
  }

  mutating func next() -> C? {
    guard index < collection.endIndex else { return nil }
    let element1 = collection[index]
    index = collection.index(after: index)
    guard index < collection.endIndex else { return nil }
    let element2 = collection[index]
    let pair = [element1,element2]
    return C(pair)
  }
}

do {
  print("Pairs from array")
  let array = ["A","B","C"]
  let pairs = Pairs(array) //This line is fine
  for pair in pairs {
    print(pair)
  }
}

do {
  print("Pairs from set")
  let set = Set(["A","B","C"])
  let pairs = Pairs(set) // This line causes error
  for pair in pairs {
    print(pair)
  }
}

Строка let let = Pairs (set) генерирует ошибку: «Тип аргумента« Set »не соответствует ожидаемому типу« RangeReplaceableCollection »»

Так что мне нужно разобраться, как создать экземпляр коллекции без использования RangeReplaceableCollection?

Ответы [ 2 ]

0 голосов
/ 08 ноября 2018

Несмотря на то, что для Collection не требуется инициализатор, Array и Set имеют нужный инициализатор:

init<S : Sequence>(_ elements: S) where S.Element == Element

поэтому, создав новый procotol, который требует этого, а затем расширив Array и Set с этим протоколом, мы можем затем предположить, что UsableCollections будет иметь этот инициализатор и все будет работать как требуется:

protocol UsableCollection: Collection {
  init<S : Sequence>(_ elements: S) where S.Element == Element
}

extension Array: UsableCollection { }
extension Set: UsableCollection { }

struct Pairs<C: UsableCollection>: Sequence, IteratorProtocol {
  var collection: C
  var index: C.Index

  init(_ collection: C) {
    self.collection = collection
    index = self.collection.startIndex
  }

  mutating func next() -> C? {
    guard index < collection.endIndex else { return nil }
    let element1 = collection[index]
    index = collection.index(after: index)
    guard index < collection.endIndex else { return nil }
    let element2 = collection[index]
    let pair = [element1,element2]
    return C(pair)
  }
}

do {
  print("Pairs from array")
  let array = ["A","B","C"]
  let pairs = Pairs(array)
  for pair in pairs {
    print(pair)
  }
}

do {
  print("Pairs from set")
  let set = Set(["A","B","C"])
  let pairs = Pairs(set)
  for pair in pairs {
    print(pair)
  }
}
0 голосов
/ 07 ноября 2018

Вы никогда не ограничивали тип CollectionType, поэтому Swift не знает, что вы можете создать его вообще, не говоря уже о создании одного, передавая массив. Collection сам по себе не обещает никаких init методов. Нам нужно перейти к RangeReplaceableCollection, чтобы получить:

struct AnyMaker<CollectionType>: Sequence, IteratorProtocol
where CollectionType: RangeReplaceableCollection {
    typealias Input = CollectionType.Element

    ...
}

Как только вы это сделаете, next() выглядит так:

mutating func next() -> CollectionType? {
    switch stage {
    case .a:    stage = .ab;   return CollectionType([a])
    case .ab:   stage = .end;  return CollectionType([a,b])
    case .end:                 return nil
    }
}

Обратите внимание, что это возвращает CollectionType?, а не CollectionType<Input>?. Ничто в CollectionType не требует, чтобы он принимал параметр типа, поэтому мы не можем его передать. Невозможно выразить «принимает параметр типа», даже если мы этого хотели, но мы этого не хотим. CollectionType просто нужно немного Element, и это обещано RangeReplaceableCollection.

let anyMaker = AnyMaker<[String]>(a: "A", b: "B")
for x in arrayMaker {
    print(x)
}

Полный код:

struct AnyMaker<CollectionType>: Sequence, IteratorProtocol
where CollectionType: RangeReplaceableCollection {
    typealias Input = CollectionType.Element
    var a,b: Input
    var stage = Stage.a

    init(a: Input, b: Input) {
        self.a = a
        self.b = b
    }

    mutating func next() -> CollectionType? {
        switch stage {
        case .a:    stage = .ab;   return CollectionType([a])
        case .ab:   stage = .end;  return CollectionType([a,b])
        case .end:                 return nil
        }
    }
}
...