Swift 中的可选链接 3:为什么一个示例有效而另一个无效?

optional chaining in Swift 3: why does one example work and not the other?

这是我的 .

之后的一个问题的详细说明

多亏了 and 的引导,我能够阅读分数(谐波比)或小数(美分) 从字符串数组计算音阶中音符的频率。

首先测试字符串数组中的每个元素,看它是否包含 /. 然后两个函数之一使用可选链接识别输入错误,因此小数和小数都符合tuning file format 中概述的规则。

示例 1 和 1a 显示了以两种格式正确输入的数据会发生什么。

  1. 使用分数和小数的混合比例

                    C      D            E      F            G            Ab       B             C’      
    let tuning = [ "1/1", "193.15686", "5/4", "503.42157", "696.57843", "25/16", "1082.89214", "2/1"]
    

调试区的列显示输入数据(自上而下),行显示输出频率(l-to-r)。

    Optional("1/1")
    Optional("193.15686")
    Optional("5/4")
    Optional("503.42157")
    Optional("696.57843")
    Optional("25/16")
    Optional("1082.89214")
    Optional("2/1")
    [261.62599999999998, 292.50676085897425, 327.03249999999997, 349.91970174951047, 391.22212058238728, 408.79062499999998, 489.02764963627084, 523.25199999999995]

示例 2 和 3 显示了两个函数如何对 错误的 输入(即错误输入的数据)作出反应。

  1. 报告错误分数(例如缺少分母会打印一条消息)

    Optional("1/1")
    Optional("5/")
    User input error - invalid fraction: frequency now being set to 0.0 Hertz 
    Optional("500.0")
    Optional("700.0")
    Optional("2/1")
    [261.62599999999998, 0.0, 349.22881168708938, 391.99608729493866, 523.25199999999995]
    
  2. 不报告错误的小数点 (例如,在 700 之后没有 .0 - 这应该会产生一条消息)

    Optional("1/1")
    Optional("5/4")
    Optional("500.0")
    Optional("700")
    Optional("2/1")
    [261.62599999999998, 327.03249999999997, 349.22881168708938, 0.0, 523.25199999999995]
    

注意:除了报告之外,当可选项为零时,行中会出现 0.0 (Hz)。这被插入到代码的其他地方(在上下文中用注释对其进行了解释)

问题简述?分数函数报告错误,而小数函数无法检测到错误输入。

这两个函数都使用带有保护语句的可选链接。这适用于错误的分数,但我所做的任何事情都不会使函数报告小数的错误输入条件。在彻底检查代码后,我确信问题出在我为 guard 语句设置的条件上。但我就是做错了。谁能解释一下我做错了什么?

Tuner.swift

import UIKit

class Tuner {

    var tuning                      = [String]()
    let tonic: Double               = 261.626   // frequency of middle C
    var index                       = -1
    let centsPerOctave: Double      = 1200.0    // mandated by Scala tuning file format
    let formalOctave: Double        = 2.0       // Double for stretched-octave tunings

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

    let frequency                   = tuning.flatMap(doubleFromDecimalOrFraction)
    print(frequency)

}


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

    index                           += 1
    let whichNumericStringType      = s
    print(whichNumericStringType as Any)        // eavesdrop on String?

    var possibleFrequency: Double?

    //  first process decimal.
    if (whichNumericStringType?.contains("."))!             {
        possibleFrequency           = processDecimal(s: s)
    }

    //  then process fractional.
    if (whichNumericStringType?.contains("/"))!             {
        possibleFrequency           = processFractional(s: s)
    }

    // Insert "0.0" marker. Remove when processDecimal works
    let noteFrequency               = possibleFrequency
    let zeroFrequency               = 0.0
    // when noteFrequency? is nil, possibleFrequency is set to zeroFrequency
    let frequency                   = noteFrequency ?? zeroFrequency

    return frequency    // TO DO let note: (index: Int, frequency: Double)

    }


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

    var fractionArray               = s?.components(separatedBy: "/")

    guard let numerator             = Double((fractionArray?[0])!.digits),
        let denominator             = Double((fractionArray?[1])!.digits),
        numerator                   > 0,
        denominator                 != 0,
        fractionArray?.count        == 2
        else
    {
        let possibleFrequency       = 0.0
        print("User input error - invalid fraction: frequency now being set to \(possibleFrequency) Hertz ")
        return possibleFrequency
        }
    let possibleFrequency           = tonic * (numerator / denominator)
    return possibleFrequency
        }


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

    let decimalArray                = s?.components(separatedBy: ".")
    guard let _                     = s,
        decimalArray?.count         == 2
        else
    {
        let denominator             = 1
        let possibleFrequency       = 0.0
        print("User input error (value read as \(s!.digits)/\(denominator) - see SCL format, http://www.huygens-fokker.org/scala/scl_format.html): frequency now being forced to \(possibleFrequency) Hertz ")
        return possibleFrequency
        }
    let power                       = Double(s!)!/centsPerOctave
    let possibleFrequency           = tonic * (formalOctave**power)
    return possibleFrequency
        }
    }


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)
    }

ViewController.swift

import UIKit

class ViewController: UIViewController {

    // test pitches: rational fractions and decimal numbers (currently 'good')
    let tuning = ["1/1", "5/4", "500.0", "700.0", "2/1"]

    // 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"]

    // Diatonic scale: mixed 12-tET and rational fractions
    //    let tuning = [ "0.0", "9/8", "400.0", "4/3", "700.0", "27/16", "1100.0", "2/1"]

    // Diatonic scale: 19-tET
    //     let tuning = [ "0.0", "189.48", "315.8", "505.28", "694.76", "884.24", "1073.72", "1200.0"]

    // Diatonic 1/4-comma meantone scale. Pietro Aaron's temperament (1523) : mixed cents and rational fractions
    //    let tuning = [ "1/1", "193.15686", "5/4", "503.42157", "696.57843", "25/16", "1082.89214", "2/1"]

override func viewDidLoad() {
    super.viewDidLoad()

    _ = Tuner(tuning: tuning)

    }
}

The problem in a nutshell ? the function for fractions reports a fault whereas the function for decimal numbers fails to detect bad input.

十进制数函数确实检测“错误”输入。但是,"700" 不包含 ".",如果字符串包含 ".",您只调用 processDecimal(s:)。如果字符串不包含 ".",也不包含 "/"doubleFromDecimalOrFraction(s:) 不会调用任何函数来解析字符串。