为什么使用.flatMap的Swift函数返回`Double`而不是`tuple'?

时间:2017-01-03 06:58:16

标签: swift tuples

我想使用包含tupleInt的{​​{1}}来索引函数返回的值。函数返回Double.时工作正常,但是当我读到Double时1}}我收到此错误:

  

'flatMap'产生'[SegmentOfResult.Iterator.Element]',而不是预期的上下文结果类型'(pitchClass:Int,frequency:Double)'

我很困惑为什么tuple返回.flatMapDouble This link tuple.flatMap(_:)有意义但我不能看到与arrays的联系,揭示了这个问题。

解决这个问题的最佳方法是什么?

澄清

根据要求

tuples是一个包含分数和小数的字符串数组

tuning

// test pitches: rational fractions and decimal numbers let tuning = ["1/1", "200.0", "5/4", "500.0", "600.0", "700.0", "1000.0", "2/1"] 是一个处理两种数字类型并返回Double(即scaleFrequencies)的函数。

我已经包含了代码初始化。

frequency

1 个答案:

答案 0 :(得分:0)

该函数目前正常返回frequency.并且已经提供了我需要的变量,即‘x’最好不要更改它。我将创建一个新类来添加更多功能(即将频率映射到MIDI键盘的键)。 感谢Rob和jtbandes的意见。

修改

在不扩大问题空间的情况下解决这个问题更为明智。一旦我质疑是否需要向函数返回一个参数,我就找到了我的解决方案,而是询问如何仅使用可用的参数从函数内部呈现数据。该解决方案解决了由accepted answer to another post标识的相关问题,并解除了可选值,而不会引起其他几个帖子( here {{3}上建议的运行时错误} here 。)

使用可选链接和零合并而不是强制解包来展开值。根据{{​​3}}有效的数字将转换为频率并映射到MIDI键盘。无效的调整值和标有 Octave map 0 Optional("x") 0 : 0.0 1 Optional("35/32") 1 : 286.1534375 2 Optional("x") 2 : 0.0 3 Optional("x") 3 : 0.0 4 Optional("5/4") 4 : 327.0325 5 Optional("21/16") 5 : 343.384125 6 Optional("x") 6 : 0.0 7 Optional("3/2") 7 : 392.439 8 Optional("x") 8 : 0.0 9 Optional("x") 9 : 0.0 10 Optional("7/4") 10 : 457.8455 11 Optional("15/8") 11 : 490.54875 的注释字符串(由here指定)将生成0.0 Hz的频率。

该解决方案允许函数读取的可选比例值更容易与调用其他函数时返回的频率进行比较。

e.g。

    MIDI map

    Octave 0
    0 0 0.0
    1 1 8.942294921875
    2 2 0.0
    3 3 0.0
    4 4 10.219765625
    5 5 10.73075390625
    6 6 0.0
    7 7 12.26371875
    8 8 0.0
    9 9 0.0
    10 10 14.307671875
    11 11 15.3296484375

    Octave 1
    12 0 0.0
    13 1 17.88458984375
    14 2 0.0
    15 3 0.0
    16 4 20.43953125
    17 5 21.4615078125
    18 6 0.0
    19 7 24.5274375
    20 8 0.0
    21 9 0.0
    22 10 28.61534375
    23 11 30.659296875

    Octave 2
    24 0 0.0
    25 1 35.7691796875
    26 2 0.0

    etc

    Octave 9
    108 0 0.0
    109 1 4578.455
    110 2 0.0
    111 3 0.0
    112 4 5232.52
    113 5 5494.146
    114 6 0.0
    115 7 6279.024
    116 8 0.0
    117 9 0.0
    118 10 7325.528
    119 11 7848.78

    Octave 10
    120 0 0.0
    121 1 9156.91
    122 2 0.0
    123 3 0.0
    124 4 10465.04
    125 5 10988.292
    126 6 0.0

它还有助于读取其他八度音阶中的频率

import UIKit

class Tuner         {

    var tuning                      = [String]()    // .scl
    var pitchClassFrequency         = Double()      // .scl
    let centsPerOctave: Double      = 1200.0        // .scl mandated by Scala tuning file format

    let formalOctave: Double        = 2.0           // .kbm/.scl Double for stretched-octave tunings
    var octaveMap                   = [Double]()    // .kbm/.scl
    var midiMap                     = [Double]()    // .kbm

    let sizeOfMap                   = 12            // .kbm
    let firstMIDIKey                = 0             // .kbm
    let lastMIDIKey                 = 127           // .kbm

    let referenceMIDIKey            = 60            // .kbm
    let referenceFrequency: Double  = 261.626       // .kbm frequency of middle C

    var indexMIDIKeys               = Int()
    var indexOctaveKeys             = Int()
    var currentKeyOctave            = Int()
    var index: Int                  = 0


init(tuning: [String]) {
    self.tuning                 = tuning

// SCL file format - create frequency map of notes for one octave
    print("Octave map")
    print("")
    let _                           = tuning.flatMap(scaleToFrequencies)

// KBM file format - create frequency map of MIDI keys 0-127
    print("")
    print("MIDI map")
    let _                           = createMIDIMap()

    }


func createMIDIMap()                                            {

    indexOctaveKeys             = firstMIDIKey   //  set indexOctaveKeys to pitchClass 0
    currentKeyOctave            = firstMIDIKey   //  set currentOctave to octave 0

    for indexMIDIKeys in firstMIDIKey...lastMIDIKey {
        let indexOctaveKeys     =      indexMIDIKeys  % sizeOfMap

        currentKeyOctave        = Int((indexMIDIKeys) / sizeOfMap)
        let frequency           = octaveMap[indexOctaveKeys] * 2**Double(currentKeyOctave)

        //        midiMap[i]              = octaveMap[indexMIDIKeys] * 2**Double(currentKeyOctave)
        if indexOctaveKeys == 0 {
            print("")
            print("Octave \(currentKeyOctave)")
        }
        print(indexMIDIKeys, indexOctaveKeys, frequency)
        }
    }


func scaleToFrequencies(s: String?)                             {

    var frequency: Double       = 0.0

    //  first process non-numerics.
    let numericString           = zapAllButNumbersSlashDotAndX(s: s)

    print(index, numericString as Any)        // eavesdrop on String?

    //  then process numerics.
    frequency                   = (processNumericsAndMap(numericString: numericString)) / Double(2)**Double(referenceMIDIKey / sizeOfMap)
    octaveMap.append(frequency)
    print(index,":",frequency * 2**Double(referenceMIDIKey / sizeOfMap))
    index += 1
    }


func processNumericsAndMap(numericString: String?) -> Double    {
    guard let slashToken    = ((numericString?.contains("/")) ?? nil),
        let dotToken        = ((numericString?.contains(".")) ?? nil),
        let xToken          = ((numericString?.contains("x")) ?? nil),
        slashToken          == false,
        dotToken            == true,
        xToken              == false
        else {
            guard let dotToken = ((numericString?.contains(".")) ?? nil),
                let xToken     = ((numericString?.contains("x")) ?? nil),
                dotToken       == false,
                xToken         == false
                else {
                    guard let xToken    = ((numericString?.contains("x")) ?? nil),
                        xToken          == false
                        else {
                            //  then it must be mapping.
                            let frequency   = 0.0
                            //                      print("[x] \(frequency) Hz")
                            return frequency
                    }
                    //  then process integer.
                    let frequency   = processInteger(s: numericString)
                    return frequency
            }
            //  then process fractional.
            let frequency           = processFractional(s: numericString)
            return frequency
        }
    //  process decimal.
    let frequency                   = processDecimal(s: numericString)
    return frequency
    }


func processFractional(s: String?) -> Double                    {
    let parts = s?.components(separatedBy: "/")
    guard parts?.count      == 2,
        let numerator       = Double((parts?[0])?.digits ?? "failNumerator"),
        let dividend        = Double((parts?[1])?.digits ?? "failDenominator"),
        dividend            != 0
        else {
            let frequency   = 0.0
            print("invalid ratio: frequency now being set to \(frequency) Hz")
            return frequency
        }
    let frequency           = referenceFrequency * (numerator / dividend)
    return frequency
    }


func processDecimal(s: String?) -> Double                       {

    let parts = s?.components(separatedBy: ".")
    guard parts?.count      == 2,
        let intervalValue   = Double(s ?? "failInterval"),
        let _               = Double((parts?[0])?.digits ?? "failDecimal")
        else {
            let frequency   = 0.0
            print("invalid cents value: frequency now being forced to \(frequency) Hz ")
            return frequency
         }
    let power               = intervalValue/centsPerOctave                      // value with explicit remainder
    let frequency           = referenceFrequency * (formalOctave**power)
    return frequency
    }


func processInteger(s: String?) -> Double                       {
    let frequency           = 0.0
    print("not cents, not ratio : frequency now being set to \(frequency) Hz ")
    return frequency
    }


func zapAllButNumbersSlashDotAndX(s: String?) -> String?        {

    var mixedString = s
    if mixedString != nil {
        mixedString = mixedString!
        }
        guard var _ = mixedString?.contains("/"),
        var _   = mixedString?.contains(".")
        else {
            let numberToken = mixedString
            return numberToken
            }
            guard let xToken = mixedString?.contains("x"),
                xToken == false
                else {
                    let xToken = "x"
                    return xToken
        }
    let notNumberCharacters = NSCharacterSet.decimalDigits.inverted
    let numericString = s?.trimmingCharacters(in: notNumberCharacters) ?? "orElse"
    return numericString.stringByRemovingWhitespaces
    }

}


extension String    {
var stringByRemovingWhitespaces: String {
    return components(separatedBy: .whitespaces).joined(separator: "")
    }
}


extension String    {

var digits: String {
    return components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
    }
}


precedencegroup Exponentiative {

    associativity: left
    higherThan: MultiplicationPrecedence

}


infix operator ** : Exponentiative


func ** (num: Double, power: Double) -> Double                      {
return pow(num, power)
}


func pitchClass(pitchClass: Int, _ frequency: Double) -> Int        {
    return pitchClass
}


func frequency(pitchClass: Int, _ frequency: Double) -> Double      {
    return frequency
}

音乐应用的开发者可能会发现这很有用,因为它展示了如何创建重新调整的MIDI地图。该解决方案允许我打开由分数和小数组成的数字字符串,这些数字字符串指定音阶中音符的调音,这超出了标准音乐键盘的范围。访问these rules的任何人都不会失去其重要性。

这是代码

<强> Tuner.swift

import UIKit

class ViewController: UIViewController {

 // Hexany: 6-note scale of Erv Wilson

        let tuning = ["x", "35/32", "x", "x", "5/4", "21/16", "x", "3/2", "x", "x", "7/4", "15/8"]


 // Diatonic scale: rational fractions
 //       let tuning = [ "1/1", "9/8", "5/4", "4/3", "3/2", "27/16", "15/8", "2/1"]

 // Mohajira: rational fractions
 //    let tuning = [ "21/20", "9/8", "6/5", "49/40", "4/3", "7/5", "3/2", "8/5", "49/30", "9/5", "11/6", "2/1"]

 // Diatonic scale: 12-tET
 //    let tuning = [ "0.0", "200.0", "400.0", "500", "700.0", "900.0", "1100.0", "1200.0"]


override func viewDidLoad() {
    super.viewDidLoad()

    _ = Tuner(tuning: tuning)


    }
}

<强> ViewController.swift

DataGrid