Swift Generics и вопросы протокола для многоуровневых обобщений / протоколов - PullRequest
0 голосов
/ 29 июня 2018

Я планирую создать приложение для флеш-карт для детей из Чикаго, которое может отвечать на самые разные вопросы, если они могут соответствовать одному и тому же протоколу. В идеале должно быть много разных игр различной сложности, и представление сможет использовать все игры, потому что все игры будут соответствовать одним и тем же протоколам. Я хочу, чтобы каждый ход обладал способностью воспринимать совершенно различную структуру, пока она уравниваема.

Я чувствую, что подхожу ближе, но я не могу понять, как заставить игру воспринимать любые данные без ошибок ... Я чувствую, что подхожу, но могу не пройти этот скачок скорости.

Я продолжаю получать сообщения об ошибках, такие как «TurnDefinable может использоваться только как общее ограничение, потому что оно имеет Self или требования связанного типа»

protocol GameDefinable {
    associatedtype TurnType: TurnDefinable
    var turns: [TurnType] { get }
}

class Game<T: TurnDefinable>: GameDefinable {
    var turns: [T]

    init(turns: [T]) {
        self.turns = turns
    }
}

class Turn<A: AnswerDefinable>: TurnDefinable {
    var question: String
    var correctAnswer: A
    var answers: [A]

    init(question: String, answers: [A], correctAnswer: A) {
        self.question = question
        self.answers = answers
        self.correctAnswer = correctAnswer
    }
}

protocol TurnDefinable {
    associatedtype AnswerType
    /// Localized string to ask the user a question they must answer
    var question: String { get }
    /// Array of possible answers
    var answers: [AnswerType] { get }
    /// Correct answer per turn
    var correctAnswer: AnswerType { get }
}

protocol AnswerDefinable: Equatable {
    // Will have more stuff here like localized formatted string, etc
}

// Just created this test pretending our answers will be Ints
struct ExampleOfAnAnswerStruct: AnswerDefinable {
    static func == (lhs: ExampleOfAnAnswerStruct, rhs: ExampleOfAnAnswerStruct) -> Bool {
        return lhs.testInteger == rhs.testInteger
    }

    // Just created this to get the equatable
    var testInteger = 0
}


struct ExampleOfAnAnswerStruct2: AnswerDefinable {
    var string: String
    static func == (lhs: ExampleOfAnAnswerStruct2, rhs: ExampleOfAnAnswerStruct2) -> Bool {
        return lhs.string == rhs.string
    }
}

Любая помощь будет очень признательна ...

РЕДАКТИРОВАТЬ: я сейчас намного ближе, мне просто нужно выяснить, как можно использовать два хода с разными типами ответов

let turnExample1 = Turn<ExampleOfAnAnswerStruct>(question: "Which is the lonliest number?", answers: [ExampleOfAnAnswerStruct(testInteger: 1), ExampleOfAnAnswerStruct(testInteger: 2), ExampleOfAnAnswerStruct(testInteger: 3)], correctAnswer: ExampleOfAnAnswerStruct(testInteger: 1))
let turnExample2 = Turn<ExampleOfAnAnswerStruct2>(question: "You say goodbye, and i say ...", answers: [ExampleOfAnAnswerStruct2(string: "hello"), ExampleOfAnAnswerStruct2(string: "goodbye")], correctAnswer: ExampleOfAnAnswerStruct2(string: "hello"))

let testGame = Game(turns: [turnExample1, turnExample2])

Я так близко! Спасибо за помощь!

Ответы [ 2 ]

0 голосов
/ 29 июня 2018

Вы не можете иметь массив типа TurnDefinable, потому что это протокол с ассоциированным типом, как вы видели.

Вам необходимо настроить протокол GameDefinable, чтобы он имел собственный связанный тип, который вы можете использовать в качестве типа для массива:

protocol GameDefinable {
    associatedtype TurnType: TurnDefinable
    var turns: [TurnType] { get }
}

Затем вы можете добавить универсальный параметр в ваш класс Game, который будет использоваться в качестве ассоциированного типа для протокола GameDefinable (вы также можете пропустить универсальный параметр и объявить массивы с определенным типом, таким как Turn<ExampleOfAnAnswerStruct> если вы хотите ограничить вашу игру определенным типом):

class Game<T: TurnDefinable>: GameDefinable {
    var turns: [T]

    init(turns: [T]) {
        self.turns = turns
    }
}
0 голосов
/ 29 июня 2018

У меня есть идея, но я не уверен в этом.

Два экземпляра класса Generics имеют один и тот же тип, если все они используют один и тот же тип, например, Integer. Наличие ассоциированного типа в протоколе может означать, что один экземпляр не может использоваться так же, как другой экземпляр.

Использование TurnDefinable означает, что два экземпляра могут иметь разные типы, и компилятору может не нравиться иметь разные типы в массиве (неприменимо).
Непригодно для использования, потому что вы не можете обрабатывать все очереди одинаково, вызывать одни и те же функции, обращаться к одним и тем же переменным.
Функция с другой сигнатурой больше не является той же функцией.

Как бы вы написали следующее, если бы компилятор просто проигнорировал это?

for turn in turns {
    turn.answers.first!.X // ???
}

Вы можете использовать пустой протокол ответа и набрать на нем переключатель. Может быть, это поможет.

protocol Answer {}

for turn in turns {
    for answer in turn.answers {
        switch answer {
        case let type1 = answer as AnswerType1:
            break //
        case let type2 = answer as AnswerType2:
            break
        default:
            fatalError("Answer type unknown \(type(of: Answer))")
        }
    }
}
...