首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在一个较大的整数数组中找到3位数字的一组特定组合的Swift函数

在一个较大的整数数组中找到3位数字的一组特定组合的Swift函数
EN

Code Review用户
提问于 2019-05-13 16:07:12
回答 1查看 1.3K关注 0票数 3

我问了这个问题的堆栈溢出,并指示到这里。我正在做一个函数,它将帮助我快速找到所有上层结构的三音(3音符和弦),我可以添加到一个4音符第7和弦,以创建一个更大的复合和弦,以及每个三位一体的根和名字。我现在正在测试的例子是一个13#11 chord,它在一个12音符的八度音阶(当根数为0时)达到了以下程度:[0, 2, 4, 6, 7, 9, 10]*

*附带注意:[0, 4, 6, 7, 9, 10]也将匹配为13#11。

基础的第七和弦是占主导地位的第七和弦:[0, 4, 7, 10].I已经知道哪个三合唱完成了和弦:第九和弦是一个主要的三音,第十一和弦是减少的,0, 6, 9 (只有6 ( #11 )和9 (第13)才是真正必要的13#11和弦;2 (9)是可选的)。

实际上,我已经有了一个函数,它可以给出这些结果,我只是想知道是否有一种更快/更有效的方法来做到这一点?现在感觉有点笨重/笨重。

RobNapier建议我使用Enum代替String、Int和。我肯定知道Enum将如何帮助我的triadQualities数组。我不知道数字组合会有什么改善。或者我还能做什么其他的改进(与枚举相关的或者其他的)。有人能帮我启发一下吗?任何帮助都将不胜感激。

代码

代码语言:javascript
复制
extension Int {
    func degreeInOctave() -> Int {
        switch self {
        case 0...11:
            return self
        case 12...:
            return self - 12
        default:
            return self + 12
        }
    }
}

var ust: [Int] = [0, 2, 4, 6, 7, 9, 10]

let maj = [0, 4, 7]
let min = [0, 3, 7]
let aug = [0, 4, 8]
let dim = [0, 3, 6]
let sus4 = [0, 5, 7]
let sus2 = [0, 2, 7]
let triadDegs = [maj, min, aug, dim, sus4, sus2]

var triadRoots: [Int] = []
var triadQualities: [String] = []

func findUpperStructureTriads(degs: [Int]) {
    let degCount = degs.count

    var firstIndex = 0

    while firstIndex < (degCount - 2) {
        var secondIndex = firstIndex + 1

        while secondIndex < (degCount - 1) {
            var thirdIndex = secondIndex + 1

            while thirdIndex < (degCount) {
                var threeNoteGroup = [degs[firstIndex], degs[secondIndex], degs[thirdIndex]]

                func checkForTriad(triad: [Int]) -> Bool {
                    if triadDegs.contains(triad) {
                        switch triad {
                        case maj:
                            triadQualities.append("major")
                        case min:
                            triadQualities.append("minor")
                        case aug:
                            triadQualities.append("augmented")
                        case dim:
                            triadQualities.append("diminished")
                        case sus4:
                            triadQualities.append("sus4")
                        case sus2:
                            triadQualities.append("sus2")
                        default:
                            ()
                        }
                        return true
                    } else {
                        return false
                    }
                }
                if threeNoteGroup.contains(6), threeNoteGroup.contains(9){
                    var inversionCount = 0
                    var newGroup = threeNoteGroup.map {$0 - threeNoteGroup[0]}

                    while inversionCount < 3 {

                        func invert() {
                            newGroup = newGroup.map {($0 - newGroup[1]).degreeInOctave()}
                            let newlast = newGroup.remove(at: 0)
                            newGroup.append(newlast)
                        }
                        if checkForTriad(triad: newGroup) {
                            print(threeNoteGroup, threeNoteGroup[inversionCount])
                            triadRoots.append(threeNoteGroup[inversionCount])
                            break
                        }
                        invert()
                        inversionCount += 1
                    }
                }
                thirdIndex += 1
            }
            secondIndex += 1
        }
        firstIndex += 1
    }

    for i in 0...(triadRoots.count - 1) {
        print(triadRoots[i], triadQualities[i])
    }
}

findUpperStructureTriads(degs: ust)

输出

代码语言:javascript
复制
[0, 6, 9] 6
[2, 6, 9] 2
6 diminished
2 major

clarifications

组合的ust和弦总是从最低到最高排序。它的元素可以在0到11之间,而且它们从不重复。下面的4音符和弦和上部的3音符和弦被合并成一个大和弦,下和弦第一和弦。如果上和弦中的任何数字已经在合并和弦中,则不会增加两次。所以,如果下和弦是[0, 4, 7, 10],上和弦是[2, 7, 10],那么ust就是[0, 2, 4, 7, 10]。对于该和弦,组合函数的输出(理想情况下)如下:

代码语言:javascript
复制
[0, 2, 7] 0
[2, 7, 10] 2
0 sus2
7 minor

注意:有一件事我意识到,这个函数可能实际上对我的更大的程序没有帮助-它将取代我已经使用的一个函数,该函数对每个组合的和弦度数组都采用硬编码的Int数组,并将它们转换为字符串公式(即=“Maj 2上的min三合会”)。所以我已经完成了这项工作。为了使新功能发挥作用,我需要找出一个if语句,说明完成工作需要哪个学位,这对每个可能的和弦质量都有影响。所以这一切都是毫无意义的

EN

回答 1

Code Review用户

回答已采纳

发布于 2019-05-14 08:25:28

声明:我不是音乐理论方面的专家,英语不是我的第一语言。因此,我可能会在下面的检讨中使用错误的字眼。

审查当前代码

I/O应与计算分开。在您的例子中,findUpperStructureTriads()函数应该返回一些东西,而不是打印它。这使得该函数更好地使用和测试,并增加了程序的清晰度。

此外,应该避免全局变量(triadRootstriadQualities):调用findUpperStructureTriads()两次将显示意外的结果。

嵌套循环写得更好、更简单,因为在(半开)范围内的循环:

代码语言:javascript
复制
for firstIndex in 0 ..< degs.count {
    for secondIndex in firstIndex+1 ..< degs.count {
        for thirdIndex in secondIndex+1 ..< degs.count {
            // ...
        }
    }
}

和类似的

代码语言:javascript
复制
for inversionCount in 0 ..< 2 { ... }

代码语言:javascript
复制
for i in 0..

请注意,您的版本

(triadRoots.count- 1) {.}

如果upperBound

对于从未发生变异的变量,请使用let。

代码语言:javascript
复制
let threeNoteGroup = [degs[firstIndex], degs[secondIndex], degs[thirdIndex]]

checkForTriad()中的默认情况是“不应该发生”的情况--除非您犯了编程错误。若要及早检测此类错误,可以使用

代码语言:javascript
复制
default:
    fatalError("Should never come here")

但实际上,函数可以被更有效的字典查找所取代:

代码语言:javascript
复制
let qualities: [[Int]: String] = [
    maj : "major",
    min : "minor",
    dim : "diminished",
]

// ...

if let quality = qualities[newGroup] {
    print(threeNoteGroup, threeNoteGroup[inversionCount])
    triadRoots.append(threeNoteGroup[inversionCount])
    triadQualities.append(quality)
    break
}

将整数音符降为八度可以通过模运算来完成:

代码语言:javascript
复制
extension Int {
    var degreeInOctave: Int {
        let mod12 = self % 12
        return mod12 >= 0 ? mod12 : mod12 + 12
    }
}

一种可供选择的方法

你的程序测试给定程度的三个音符的每一个子集,如果它是已知的三位一体,则测试它的三个反转。如果只遍历给定的列表一次,并将每个音符视为每个三合会的根音,可能会更有效。然后你只需测试三合会的其他音符是否在名单上。

多结构

使用类型给出了我们在名称上操作的对象,允许将功能与对象进行分组,提供初始化方法等等。

例如,不是普通的音符数组(或者是度吗?)我们可以定义一个Chord结构:

代码语言:javascript
复制
struct Chord {
    let notes: [Int] // Increasing array of degrees in the range 0...11

    init(notes: [Int]) {
        // Reduce module 12 and sort:
        self.notes = notes.map { $0.degreeInOctave }.sorted()
    }

    func translated(by offset: Int) -> Chord {
        return Chord(notes: notes.map { $0 + offset })
    }
}

init方法确保按递增顺序和在适当范围内对数字进行排序。translated(by:)方法通过移动所有的度来计算新的和弦。如果需要,可以在以后添加更多的方法。

代码语言:javascript
复制
struct Chord {
    mutating func invert() { ... }
}

用于和弦倒置。

三合会可以定义为枚举:

代码语言:javascript
复制
enum Triad: String, CaseIterable {
    case major
    case minor
    case augmented
    case diminished
    case sus4
    case sus2

    var chord: Chord {
        switch self {
        case .major:      return Chord(notes: [ 0, 4, 7 ])
        case .minor:      return Chord(notes: [ 0, 3, 7 ])
        case .augmented:  return Chord(notes: [ 0, 4, 8 ])
        case .diminished: return Chord(notes: [ 0, 3, 6 ])
        case .sus4:       return Chord(notes: [ 0, 5, 7 ])
        case .sus2:       return Chord(notes: [ 0, 2, 7 ])
        }
    }
}

而不是全局变量(maj,min,.)我们现在可以将三合会称为枚举的值,例如

代码语言:javascript
复制
let triad = Triad.major
print(triad)            // major
print(triad.chord)      // Chord(notes: [0, 4, 7])

在符合CaseIterable的情况下,我们免费获得所有三合会的列表:

代码语言:javascript
复制
for triad in Triad.allCases { ... }

最后,我们需要一个结果类型,即在某个位置的三合会,例如:

代码语言:javascript
复制
struct UpperStructure: CustomStringConvertible {
    let triad: Triad
    let root: Int

    var description: String {
        return "\(root) \(triad.rawValue)"
    }
}

description方法提供了值的文本表示,可以根据需要进行调整。

有了这些准备,我们就可以定义一个函数,在给定的和弦中找到所有上层结构的三位一体。这可以是Chord类型的方法,而不是全局函数,因此我们现在有

代码语言:javascript
复制
struct Chord {
    let notes: [Int]

    init(notes: [Int]) {
        self.notes = notes.map { $0.degreeInOctave }.sorted()
    }

    func translated(by offset: Int) -> Chord {
        return Chord(notes: notes.map { $0 + offset })
    }

    func upperStructureTriads() -> [UpperStructure] {
        let notesSet = Set(notes)
        var result: [UpperStructure] = []

        for rootNote in notes {
            for triad in Triad.allCases {
                let chordNotes = triad.chord.translated(by: rootNote).notes
                if chordNotes.contains(6) && chordNotes.contains(9)
                    && notesSet.isSuperset(of: chordNotes) {
                    result.append(UpperStructure(triad: triad, root: rootNote))
                }
            }
        }

        return result
    }
}

所有可能的根注释和三叉组合都会被测试,并且使用Set来提高包容测试的效率。

用法示例:

代码语言:javascript
复制
let chord = Chord(notes: [0, 2, 4, 6, 7, 9, 10])
let upperStructures = chord.upperStructureTriads()
for us in upperStructures {
    print(us)
}

// 2 major
// 6 diminished
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/220187

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档