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

时间:2016-12-31 09:04:52

标签: ios swift optional

以下是我previous post on Swift optionals后的问题的详细说明。

感谢给定here, herehere的潜在客户,我能够读取分数(代表谐波比率)或小数( for cents) )从字符串数组中计算音阶中音符的频率。

首先测试字符串数组中的每个元素,看它是否包含/.两个函数中的一个然后使用可选链接识别输入错误,因此小数和小数都符合规则在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"]
    
  2. 调试区域中的列显示输入数据(自上而下),行显示输出频率(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]
      
    3. 注意:除了报告,当可选项为零时,行中会显示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)
      
          }
      }
      

1 个答案:

答案 0 :(得分:3)

  

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

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