Swift 3中的可选链接:为什么一个例子工作而不是另一个?

以下是我之前关于Swift选项的post后的问题的详细说明。

感谢这里给出的线索, 这里和这里 ,我能够从字符串数组中读取分数( 对于谐波比 )或小数( 对于分数)来计算音阶中音符的频率。

首先测试字符串数组中的每个元素以查看它是否包含a /或a . 然后,两个函数之一使用可选链接识别输入错误,因此小数和十进制数符合此调整文件格式中概述的规则。

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

  1. 使用分数和小数的混合进行缩放

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

问题简而言之? 分数函数报告错误,而十进制数函数无法检测错误输入。

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