用于在多个单位之间转换值的动态数学

dynamical math for coverting values between multiple units

这是我现在用来将一个单位的值转换为另一个单位的函数。我正在寻找一个更动态的选择来做到这一点。我想让这个函数能够从例如 1 千克转换为盎司,但是我现在这样做的原因是我已经定义了每个单位与另一个单位相比的数量。有更简单的方法吗?

    func weightMath(_ input: UITextField, _ textField: UITextField) {
    //The math for doing weight calculations. This will also round up the answer to the third decimal.
    switch input.tag {
        /*
         case 0 is for when the user want to calculate from kilogram
         case 1 is for when the user want to calculate from ounces
         case 2 is for when the user want to calculate from pounds
         */
    case 0:
        switch textField.tag {
            /*
            case 0 is for when the user want to calculate to kilogram
            case 1 is for when the user want to calculate to ounces
            case 2 is for when the user want to calculate to pounds
            */
        case 0:
            textField.text = String(format: "%.3f", toFloat(input.text!) * 1)
        case 1:
            textField.text = String(format: "%.3f", toFloat(input.text!) * 35.274)
        case 2:
            textField.text = String(format: "%.3f", toFloat(input.text!) * 2.205)
        default:
            break
        }
    case 1:
        switch textField.tag {
            /*
            case 0 is for when the user want to calculate to kilogram
            case 1 is for when the user want to calculate to ounces
            case 2 is for when the user want to calculate to pounds
            */
        case 0:
            textField.text = String(format: "%.3f", toFloat(input.text!) * 0.028)
        case 1:
            textField.text = String(format: "%.3f", toFloat(input.text!) * 1)
        case 2:
            textField.text = String(format: "%.3f", toFloat(input.text!) * 0.063)
        default:
            break
        }
    case 2:
        switch textField.tag {
            /*
            case 0 is for when the user want to calculate to kilogram
            case 1 is for when the user want to calculate to ounces
            case 2 is for when the user want to calculate to pounds
            */
        case 0:
            textField.text = String(format: "%.3f", toFloat(input.text!) * 0.454)
        case 1:
            textField.text = String(format: "%.3f", toFloat(input.text!) * 16)
        case 2:
            textField.text = String(format: "%.3f", toFloat(input.text!) * 1)
        default:
            break
        }
    default:
        break
    }
}

所以正如人们提到的那样,使用 Measurement API 会简化事情,但我认为这不是您的代码的真正问题。您似乎意识到,随着您转换的单位数量 to/from 的增加,您的 weightMath 功能很快就会失控(如果您有 N 个单位,它将与 N^2 成比例增长) .很好的是,即使是初学者,您也会看到这段代码并思考 "surely there is a better way".

您缺少的关键见解是,与其直接从输入单元到输出单元,不如将其分成两个步骤。确定一些内部单位(比如千克),然后将输入转换为这些内部单位并从这些内部单位转换为输出单位。

让我们看一下在代码中的样子。尽管您的函数名为 weightMath,但它实际上确实有点搞乱了 UI 的东西。让我们通过将其分解为两个函数来解决这个问题:处理 UI 的 updateUI 和执行数学运算的 convert

这里是 updateUI 的代码:

func updateUI(_ input: UITextField, _ textField: UITextField) {
    guard let inputString = input.text,
          let inputValue = Double(inputString) else {
        // You may want to do more than just return.
        return
    }
    let convertedValue = convert(mass: inputValue, inputUnitIndex: input.tag, outputUnitIndex: textField.tag)
    textField.text = String(format: "%.3f", convertedValue)
}

现在 convert 可以只专注于数学。即使不使用 Measurement API 我们也可以相当简单地实现它:

private func convert(mass: Double, inputUnitIndex: Int, outputUnitIndex: Int) -> Double {
    let conversionTable = [ 1.0, 0.0283495, 0.453592 ]
    let massKg = mass * conversionTable[inputUnitIndex]
    return massKg / conversionTable[outputUnitIndex]
}

这里有几点说明。换算 table 包含将千克、盎司和磅 乘以 千克所需的因数。这组相同的值可用于通过除法将千克换算。值 massKg 是我们 内部单位 的值,我选择的是千克。

但是这段代码有点难看:它有 魔法常量 ,如果你想支持更多的单位,你必须查找转换因子。使用 Measurement API 会使它更整洁。这就是它的样子。

private func convert(mass: Double, inputUnitIndex: Int, outputUnitIndex: Int) -> Double {
    let unitLookupTable = [ UnitMass.kilograms, UnitMass.ounces, UnitMass.pounds ]
    let massKg = Measurement(value: mass, unit: unitLookupTable[inputUnitIndex])
    return massKg.converted(to: unitLookupTable[outputUnitIndex]).value
}

还有一些事情可以做,让它变得更好:在这里传递整数不是很好……但那是另一回事了。希望这会有所帮助。