Swift 中的可选链接 3:为什么一个示例有效而另一个无效?
optional chaining in Swift 3: why does one example work and not the other?
这是我的 .
之后的一个问题的详细说明
多亏了 and 的引导,我能够阅读分数(谐波比)或小数(美分) 从字符串数组计算音阶中音符的频率。
首先测试字符串数组中的每个元素,看它是否包含 /
或 .
然后两个函数之一使用可选链接识别输入错误,因此小数和小数都符合tuning file format 中概述的规则。
示例 1 和 1a 显示了以两种格式正确输入的数据会发生什么。
使用分数和小数的混合比例
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 显示了两个函数如何对 错误的 输入(即错误输入的数据)作出反应。
报告错误分数(例如缺少分母会打印一条消息)
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]
不报告错误的小数点 (例如,在 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:)
不会调用任何函数来解析字符串。
这是我的
多亏了
首先测试字符串数组中的每个元素,看它是否包含 /
或 .
然后两个函数之一使用可选链接识别输入错误,因此小数和小数都符合tuning file format 中概述的规则。
示例 1 和 1a 显示了以两种格式正确输入的数据会发生什么。
使用分数和小数的混合比例
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 显示了两个函数如何对 错误的 输入(即错误输入的数据)作出反应。
报告错误分数(例如缺少分母会打印一条消息)
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]
不报告错误的小数点 (例如,在 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:)
不会调用任何函数来解析字符串。