Любой способ написать мой протокол без использования обобщений или иным способом обхода с использованием связанных типов? - PullRequest
0 голосов
/ 30 мая 2019

У меня есть протокол ScaleDetails, и я хочу, чтобы несколько различных структур Scale соответствовали (например, MajorScale, DiminishedScale и т. Д.).Затем я хочу иметь другой протокол, ComboChord, который имеет переменную scale типа ScaleDetails, которая будет иметь другой масштаб в зависимости от другой переменной в другом месте.Проблема в том, что я не могу присвоить свой scale var в ComboChord типу ScaleDetails, потому что я не могу понять, как написать свой протокол без использования связанных типов.

В каждой разной шкале используетсяenum различных режимов (например, Mode.SevenDeg для MajorScale и Mode.TwoDeg для DiminishedScale).И у каждого масштаба есть функции, которые возвращают экземпляр или экземпляры этой конкретной структуры масштаба.Поэтому я не вижу пути продвижения вперед без использования обобщений в моем протоколе ScaleDetails.Я пытаюсь написать это без использования классов, если это возможно, но я хочу использовать их при необходимости.Я также действительно стараюсь избегать приведения типов для своих весов, потому что не хочу перечислять каждую возможность весов каждый раз, когда я ее называю.Как это исправить?

Протокол ScaleDetails

protocol ScaleDetails {
    associatedtype ScaleMode where ScaleMode: RawRepresentable
    associatedtype T
    //...
    var root: Root { get set }
    var mode: ScaleMode { get set }
    //...
    init(_ root: KeyName.Root, mode: ScaleMode)

    func translated(by offset: Int) -> T

    func enharmSwapped() -> T
    //...
    mutating func switchMode(mode: ScaleMode)

    func getParallelModes(root: KeyName.Root) -> [T]
    func getParentModes() -> [T]
}

Протокол ComboChord

protocol ComboChord {
    var scale: ScaleDetails // doesn't work

    static var validCombos: [[Int]] { get }

    var quality: Suffix { get }
    var uprStrNotes: [Int] { get }
/*
error: "Protocol can only be used as a generic constraint
because it has Self or associatedType requirements"
*/
}

Перечисления режима

enum Mode {

    enum TwoDeg: Int, CaseIterable, ModeProtocol {
        case one = 0, two

        //...
    }

    enum SevenDeg: Int, CaseIterable, ModeProtocol {
        case one = 0, two, three, four, five, six, seven

        //...
    }
}

Major Scale

struct MajorScale: ScaleDetails {
    var root: Root
    var mode: Mode.SevenDeg

    //...

    init(_ root: KeyName.Root, mode: Mode.SevenDeg = .one) {
        self.root = Root(root)
        self.mode = mode
        self.enharm = root.r.enharm
    }

    func translated(by offset: Int) -> MajorScale {
        //...do things to the scale

        return MajorScale(newRootKey, mode: mode)
    }

    //...
    mutating func switchMode(mode: Mode.SevenDeg) {
        self.mode = mode
    }

    //...

    func enharmSwapped() -> MajorScale {
        //...do things to the scale

        return MajorScale(newRootKey, mode: mode)
    }

    func getParallelModes(root: KeyName.Root) -> [MajorScale] {
        return Mode.SevenDeg.allCases.map {MajorScale(root, mode: $0)}
    }

    func getParentModes() -> [MajorScale] {
        //...code to get array of scales
        return parentModes
    }
}

Уменьшенная шкала

struct DiminishedScale: ScaleDetails {
    var root: Root
    var mode: Mode.TwoDeg

    //...

    init(_ root: KeyName.Root, mode: Mode.TwoDeg = .one) {
        self.root = Root(root)
        self.mode = mode
        self.enharm = root.r.enharm
    }

    func translated(by offset: Int) -> DiminishedScale {
        //...do things to the scale

        return MajorScale(newRootKey, mode: mode)
    }

    //...
    mutating func switchMode(mode: Mode.TwoDeg) {
        self.mode = mode
    }

    //...

    func enharmSwapped() -> DiminishedScale {
        //...do things to the scale

        return MajorScale(newRootKey, mode: mode)
    }

    func getParallelModes(root: KeyName.Root) -> [DiminishedScale] {
        return Mode.TwoDeg.allCases.map {DiminishedScale(root, mode: $0)}
    }

    func getParentModes() -> [DiminishedScale] {
        //...code to get array of scales
        return parentModes
    }
}

KeyName.Root

enum KeyName: String, CaseIterable {
    case c = "C"
    case d = "D"
    case e = "E"
    case f = "F"
    case g = "G"
    case a = "A"
    case b = "B"

    case cB = "C\u{266D}"
    case dB = "D\u{266D}"
    case eB = "E\u{266D}"
    case fB = "F\u{266D}"
    case gB = "G\u{266D}"
    case aB = "A\u{266D}"
    case bB = "B\u{266D}"

    case cSh = "C\u{266F}"
    case dSh = "D\u{266F}"
    case eSh = "E\u{266F}"
    case fSh = "F\u{266F}"
    case gSh = "G\u{266F}"
    case aSh = "A\u{266F}"
    case bSh = "B\u{266F}"

    case c_bb = "C\u{266D}\u{266D}"
    case d_bb = "D\u{266D}\u{266D}"
    case e_bb = "E\u{266D}\u{266D}"
    case f_bb = "F\u{266D}\u{266D}"
    case g_bb = "G\u{266D}\u{266D}"
    case a_bb = "A\u{266D}\u{266D}"
    case b_bb = "B\u{266D}\u{266D}"

    case cX = "Cx"
    case dX = "Dx"
    case eX = "Ex"
    case fX = "Fx"
    case gX = "Gx"
    case aX = "Ax"
    case bX = "Bx"

    enum Root {
        case c, d, e, f, g, a, b
        case cB, dB, eB, fB, gB, aB, bB
        case cSh, dSh, eSh, fSh, gSh, aSh, bSh

        var r: KeyName {
            switch self {
            case .c:
                return .c
            case .d:
                return .d
            case .e:
                return .e
            case .f:
                return .f
            case .g:
                return .g
            case .a:
                return .a
            case .b:
                return .b
            case .cB:
                return .cB
            case .dB:
                return .dB
            case .eB:
                return .eB
            case .fB:
                return .fB
            case .gB:
                return .gB
            case .aB:
                return .aB
            case .bB:
                return .bB
            case .cSh:
                return .cSh
            case .dSh:
                return .dSh
            case .eSh:
                return .eSh
            case .fSh:
                return .fSh
            case .gSh:
                return .gSh
            case .aSh:
                return .aSh
            case .bSh:
                return .bSh
            }
        }

        init(_ key: KeyName) {
            switch key {
            case .c:
                self = .c
            case .d:
                self = .d
            case .e:
                self = .e
            case .f:
                self = .f
            case .g:
                self = .g
            case .a:
                self = .a
            case .b:
                self = .b
            case .cB:
                self = .cB
            case .dB:
                self = .dB
            case .eB:
                self = .eB
            case .fB:
                self = .fB
            case .gB:
                self = .gB
            case .aB:
                self = .aB
            case .bB:
                self = .bB
            case .cSh:
                self = .cSh
            case .dSh:
                self = .dSh
            case .eSh:
                self = .eSh
            case .fSh:
                self = .fSh
            case .gSh:
                self = .gSh
            case .aSh:
                self = .aSh
            case .bSh:
                self = .bSh
            default:
                self = .c
            }
        }
    }
    //...
}

Root

struct Root: Note {
    var noteName: String {
        return key.name
    }

    var rootKey: KeyName.Root
    var noteNum: NoteNum
    var num: Int {return noteNum.num}
    var enharm: Enharmonic

    var degName: (name: DegName.Name, short: DegName.Short, long: DegName.Long) {
        return (name: DegName.Name(.root), short: DegName.Short(.root), long: DegName.Long(.root))
    }

    var key: KeyName

    init(noteNum: NoteNum = .zero, enharm: Enharmonic = .flat) {
        self.noteNum = noteNum
        self.enharm = enharm

        var ks: KeySwitch {
            return KeySwitch(enharm: enharm)
        }

        var rs: RootSwitch {
            return RootSwitch(enharm: enharm)
        }

        switch noteNum {
        case .zero:
            self.key = ks.pickKey(.c, .bSh)
            self.rootKey = rs.pickRoot(.c, .bSh)
        case .one:
            self.key = ks.pickKey(.dB, .cSh)
            self.rootKey = rs.pickRoot(.dB, .cSh)
        case .two:
            self.key = .d
            self.rootKey = .d
        case .three:
            self.key = ks.pickKey(.eB, .dSh)
            self.rootKey = rs.pickRoot(.eB, .dSh)
        case .four:
            self.key = ks.pickKey(.fB, .e)
            self.rootKey = rs.pickRoot(.fB, .e)
        case .five:
            self.key = ks.pickKey(.f, .eSh)
            self.rootKey = rs.pickRoot(.f, .eSh)
        case .six:
            self.key = ks.pickKey(.gB, .fSh)
            self.rootKey = rs.pickRoot(.gB, .fSh)
        case .seven:
            self.key = .g
            self.rootKey = .g
        case .eight:
            self.key = ks.pickKey(.aB, .gSh)
            self.rootKey = rs.pickRoot(.aB, .gSh)
        case .nine:
            self.key = .a
            self.rootKey = .a
        case .ten:
            self.key = ks.pickKey(.bB, .aSh)
            self.rootKey = rs.pickRoot(.bB, .aSh)
        case .eleven:
            self.key = ks.pickKey(.cB, .b)
            self.rootKey = rs.pickRoot(.cB, .b)
        }
    }

    init(_ key: KeyName.Root) {
        self.key = key.r
        self.rootKey = key
        self.noteNum = self.key.noteNum
        self.enharm = self.key.enharm
    }

    mutating func kSW(ks: KeySwitch) {
        switch noteNum {
        case .zero:
            self.key = ks.pickKey(.c, .bSh)
        case .one:
            self.key = ks.pickKey(.dB, .cSh)
        case .two:
            self.key = .d
        case .three:
            self.key = ks.pickKey(.eB, .dSh)
        case .four:
            self.key = ks.pickKey(.fB, .e)
        case .five:
            self.key = ks.pickKey(.f, .eSh)
        case .six:
            self.key = ks.pickKey(.gB, .fSh)
        case .seven:
            self.key = .g
        case .eight:
            self.key = ks.pickKey(.aB, .gSh)
        case .nine:
            self.key = .a
        case .ten:
            self.key = ks.pickKey(.bB, .aSh)
        case .eleven:
            self.key = ks.pickKey(.cB, .b)
        }
    }

    mutating func swapEnharm() {
        enharm = enharm == .flat ? .sharp : .flat
        kSW(ks: KeySwitch(enharm: enharm))
        rootKey = KeyName.Root(key)
    }

    func enharmSwapped() -> Root {
        return Root(noteNum: noteNum, enharm: enharm == .flat ? .sharp : .flat)
    }
}

DetailChord

(сильно сокращено длядлина ... оригинал около 1400 строк)

struct DetailChord {
    enum DCType {
        case dom7, ma7, mi7, mi7b5, dim7, error
    }

    init(resultChord: ResultChord?, verdict: Verdict = .goodToGo) {
        self.verdict = verdict
        self.dRC = resultChord
        if let rc = dRC {
            switch rc.baseChord.lowerQual {
            case .dom7:
                dct = .dom7
            case .ma7:
                dct = .ma7
            case .mi7:
                dct = .mi7
            case .mi7_b5:
                dct = .mi7b5
            case .dim7:
                dct = .dim7
            }
        } else {
            dct = .error
        }
    }

    var dct: DCType

    var dRC: ResultChord?

    var verdict: Verdict

    var validCombos: [[[Int]]] {
        switch dct {
        case .dom7:
            return Dom7.validCombos
        case .ma7:
            return Maj7.validCombos
        case .mi7:
            return Min7.validCombos
        case .mi7b5:
            return Min7_b5.validCombos
        case .dim7:
            return Dim7.validCombos
        default:
            return [[[]]]
        }
    }

    var tension: [[Int]] {
        switch dct {
        case .dom7:
            return Dom7.tension
        case .ma7:
            return Maj7.tension
        case .mi7:
            return Min7.tension
        case .mi7b5:
            return Min7_b5.tension
        case .dim7:
            return Dim7.tension
        default:
            return [[]]
        }
    }

    func qualSwitcher(validCombos: [[[Int]]], degrees: [Int]) -> Int {
        var validCombosIndex = Int()
        for (index, combos) in validCombos.enumerated() {
            if combos.contains(degrees) {
                validCombosIndex = index
                break
            }
        }

        return validCombosIndex
    }

    var chord: ComboChord {
        mutating get {
            if let rc = dRC {
                let degrees = rc.degrees
                let degreesUnsorted = rc.degreesUnsorted
                let baseChord = rc.baseChord
                let degSet = rc.degSet

                var validCombosIndex = Int()

                func tensionOrGoodToGo(getComboChord: GetComboChord) -> ComboChord {
                    if !tension.contains(degrees) { // good to go!
                        verdict = .goodToGo
                        validCombosIndex = qualSwitcher(validCombos: validCombos, degrees: degrees)
                        return getComboChord(degSet, validCombosIndex)
                    } else { // tension!
                        verdict = .tension
                        if let tensionQual = rc.baseChord.qualSuffix as? Suffix.Lower {
                            return TensionChord(tensionQual, rc.degreesUnsorted)
                        } else {
                            return ErrorChord()
                        }
                    }
                }

                switch dct {
                case .dom7:
                    return tensionOrGoodToGo(getComboChord: Dom7.getComboChord(degSet:validCombosIndex:))
                case .ma7:
                    return tensionOrGoodToGo(getComboChord: Maj7.getComboChord(degSet:validCombosIndex:))
                case .mi7:
                    return tensionOrGoodToGo(getComboChord: Min7.getComboChord(degSet:validCombosIndex:))
                case .mi7b5:
                    return tensionOrGoodToGo(getComboChord: Min7_b5.getComboChord(degSet:validCombosIndex:))
                case .dim7:
                    return tensionOrGoodToGo(getComboChord: Dim7.getComboChord(degSet:validCombosIndex:))
                case .error:
                    return ErrorChord()
                }
            } else {
                print("rc not found!")
                dct = .error
                return ErrorChord()
            }
        }
    }

    struct Dom7: DetailChordType {
        static let matches = Mixolydian.validCombos + LydianDom.validCombos + HalfWholeDim.validCombos + AltP5.validCombos + MelodicMinor_Chord.validCombos + HexatonicSh9Sh11_Chord.validCombos + HalfWholeDimFlat13_Chord.validCombos

        static let validCombos: [[[Int]]] = [Mixolydian.validCombos, LydianDom.validCombos, HalfWholeDim.validCombos, AltP5.validCombos, MelodicMinor_Chord.validCombos, HexatonicSh9Sh11_Chord.validCombos, HalfWholeDimFlat13_Chord.validCombos]

        static let tension = [[0, 1, 4, 5, 7, 8, 10], [0, 4, 7, 8, 10, 11], [0, 4, 5, 7, 9, 10], [0, 2, 4, 7, 10, 11], [0, 2, 4, 5, 7, 10], [0, 3, 4, 6, 7, 10, 11], [0, 2, 4, 5, 7, 9, 10], [0, 4, 7, 10, 11], [0, 4, 5, 7, 8, 10], [0, 3, 4, 7, 8, 10, 11], [0, 1, 4, 5, 7, 10], [0, 2, 4, 6, 7, 10, 11], [0, 1, 4, 5, 7, 9, 10], [0, 3, 4, 7, 10, 11], [0, 2, 4, 5, 7, 8, 10], [0, 4, 5, 7, 8, 10, 11], [0, 2, 4, 7, 8, 10, 11], [0, 2, 4, 5, 7, 10, 11], [0, 3, 4, 5, 7, 10], [0, 4, 5, 7, 10], [0, 4, 6, 7, 10, 11], [0, 4, 7, 9, 10, 11], [0, 1, 4, 6, 7, 10, 11]]

        static let tensionTones = [5, 11]

        static func getComboChord(degSet: Set<Int>, validCombosIndex: Int) -> ComboChord {
            switch validCombosIndex {
            case 0: // mixolydian
                switch degSet {
                case let degs where degs.isSuperset(of: Dom7.Mixolydian.XIII.uprStrNotes): // 13
                    return Dom7.Mixolydian.xiii
                // sus4/sus2 on 2nd, sus2 on 5th, min or sus 4 triad on maj6
                case let degs where degs.isSuperset(of: Dom7.Mixolydian.Nine.uprStrNotes): // 9
                    // sus2 on root, min or sus4 triad on p5
                    return Dom7.Mixolydian.nine
                default: // 7
                    //maj triad on root, dim triad on 3rd
                    return Dom7.Mixolydian.seven
                }
            case 1: // lydian dominant
                // similar structure to case 0
            case 2: // half-whole dim
                // similar structure to case 0
            case 3:
            // similar structure to case 0
            case 4: // 5th mode melodic minor
                // similar structure to case 0
            case 5: // hexatonicSh9Sh11
            // similar structure to case 0
            default: // half-whole dim(b13)
                // similar structure to case 0
            }
        }

        var chordType: ComboChord = Mixolydian.seven

        enum Mixolydian: ComboChord {

            case seven, nine, xiii

            var scale: ScaleDetails {
                switch self {
                default:
                    return Major(mode: .five, modeOffset: 0)
                }
            }

            static let validCombos: [[Int]] = Seven.validCombos + Nine.validCombos + XIII.validCombos

            struct Seven {
                static let validCombos = [[0, 4, 7, 10]]
                static let uprStrNotes: [Int] = []
            }

            struct Nine {
                static let validCombos: [[Int]] = [[0, 2, 4, 7, 10]]
                static let uprStrNotes: [Int] = [2]
            }

            struct XIII {
                static let validCombos: [[Int]] = [[0, 2, 4, 7, 9, 10], [0, 4, 7, 9, 10]]
                static let uprStrNotes: [Int] = [9]
            }

            var quality: Suffix  {
                switch self {
                case .seven:
                    return .sev
                case .nine:
                    return .nine
                case .xiii:
                    return .xiii
                }
            }

            var uprStrNotes: [Int] {
                switch self {
                case .seven:
                    return Seven.uprStrNotes
                case .nine:
                    return Nine.uprStrNotes
                case .xiii:
                    return XIII.uprStrNotes
                }
            }
        }

        enum LydianDom: ComboChord {
            // same structure as Mixolydian
        }

        enum HalfWholeDim: ComboChord {
            // same structure as Mixolydian
        }

        enum AltP5: ComboChord {
            // same structure as Mixolydian
        }

        enum HexatonicSh9Sh11_Chord: ComboChord {
            // same structure as Mixolydian
        }

        enum MelodicMinor_Chord: ComboChord {
            // same structure as Mixolydian
        }

        enum HalfWholeDimFlat13_Chord: ComboChord {
            // same structure as Mixolydian
        }
    }

    struct Maj7: DetailChordType {
        // same structure as Dom7
    }

    struct Min7: DetailChordType {
        // same structure as Dom7
    }

    struct Min7_b5: DetailChordType {
        // same structure as Dom7
    }

    struct Dim7: DetailChordType {
        // same structure as Dom7
    }
}

1 Ответ

1 голос
/ 30 мая 2019

Не уверен, что он подойдет для вашего использования, но, возможно, вы также можете сделать ComboChord универсальным

protocol ComboChord {
    associatedtype Scale: ScaleDetails
    var scale: Scale { get }
}

И затем использовать его вот так

struct Chord: ComboChord {

    var scale: DiminishedScale

}
...