使用不同 UnitType 进行测量的最佳方法

Posted

技术标签:

【中文标题】使用不同 UnitType 进行测量的最佳方法【英文标题】:Best way to approach Measurements with different UnitTypes 【发布时间】:2020-06-23 22:12:03 【问题描述】:

上下文:

我正在编写一个应用程序,我想在其中显示每个菜单项的营养成分。 我希望能够灵活地使用 Measurement 类进行转换。

问题:

问题是卡路里是在 Measurement 中测量的,而蛋白质等其他属性是在 Measurement 中测量的。我决定制作 Measurement 类型的变量,以便它可以处理这两种类型。但后来我失去了对变量进行转换或操作的所有可能性。

这个函数会给我错误(这是有道理的,我只是不知道解决这个问题的最佳方法是什么)

二元运算符'/'不能应用于两个'测量' 操作数

func getPercentage() -> Double 
     return value / property.referenceIntake

如果我尝试投射它,我会收到此错误

从“测量”转换为不相关的类型 '测量 ' 总是失败

if let massUnitMeasurement = value as? Measurement<UnitMass>, let referenceMeasurement = property.referenceIntake as? 
....

到目前为止我所拥有的:

struct NutritionInformation 
    let property: NutritionProperties
    let value: Measurement<Unit>

    var formattedValue: String 
        let formatter = MeasurementFormatter()
        formatter.unitStyle = .medium
        formatter.unitOptions = .providedUnit
        return formatter.string(from: value)
    


enum NutritionProperties 
    case calories
    case carbohydrate
    case cholesterol
    case fat
    case saturatedFat
    case fibre
    case protein
    case sodium
    case sugar

    var unit: Unit 
        switch self 
            case .calories: return UnitEnergy.kilocalories
            case .cholesterol, .sodium: return UnitMass.milligrams
            default: return UnitMass.grams
        
    

    var referenceIntake: Measurement<Unit> 
        switch self 
            case .calories: return Measurement(value: 2000, unit: unit)
            case .fat: return Measurement(value: 70, unit: unit)
            case .saturatedFat: return Measurement(value: 20, unit: unit)
            case .carbohydrate: return Measurement(value: 260, unit: unit)
            case .fibre: return Measurement(value: 30, unit: unit)
            case .sugar: return Measurement(value: 90, unit: unit)
            case .protein: return Measurement(value: 50, unit: unit)
            case .sodium: return Measurement(value: 2300, unit: unit)
            case .cholesterol: return Measurement(value: 300, unit: unit)
        
    

【问题讨论】:

让卡路里成为一个独立的属性。 UnitEnergy.kilocalories 的等效 UnitMass.grams 是多少? 我不得不建议你找错地方了; “我要显示”意味着您需要 MeasurementFormatters 此处。不要将值(数据)作为测量对象进行维护;将它们保持为数字并使用 Measurement-plus-MeasurementFormatter 来显示它们。 @matt formattedValue 已经使用 MeasurementFormatter 你为什么不在测量值上得到.value @LeoDabus 问题是我永远不会将 UnitMass.grams 与 UnitEnergy.kilocalories 混合,这就是为什么我也尝试使用它们。也许我将不得不分开一些变量,但它看起来并不干净。我真的不明白,因为 UnitMass 和 UnitEnergy 继承自 Unit 对吗? 【参考方案1】:

最后我所做的是将 Dimension 本身转换为 UnitMass 或 UnitEnergy(而不是将 Measurement 转换为 Measurement ),然后使用转换的值创建一个新的 Measurement。

var percentage: Double 
        if let referenceAsMass = property.unit as? UnitMass, let valueAsMass = value.unit as? UnitMass 
            let valueMeasurement = Measurement(value: value.value, unit: valueAsMass)
            let referenceMeasurement = Measurement(value: property.referenceIntake.value, unit: referenceAsMass)
            return valueMeasurement.converted(to: .grams).value / referenceMeasurement.converted(to: .grams).value
        
        if let referenceAsEnergy = property.unit as? UnitEnergy, let valueAsEnergy = value.unit as? UnitEnergy 
            let valueMeasurement = Measurement(value: value.value, unit: valueAsEnergy)
            let referenceMeasurement = Measurement(value: property.referenceIntake.value, unit: referenceAsEnergy)
            return valueMeasurement.converted(to: .calories).value / referenceMeasurement.converted(to: .calories).value
        
        return value.value / property.referenceIntake.value
    

【讨论】:

【参考方案2】:

PMT 解决方案几乎是唯一的方法。

您不能将 Measurement&lt;Unit&gt; 转换为 Measurement&lt;UnitLength&gt; 或 UnitArea 等...

它们是完全不同的类型。

但是当 Measurement&lt;UnitLength&gt; 被转换为 Measurement&lt;Unit&gt; 时,它仍然保留其值和单位类型信息。因此,您可以重新创建正确类型的测量值。

这是一个示例通用函数,它接受一个测量数组并返回第一个具有 T 类型单位的结果:

import Foundation

let length = Measurement(value: 10, unit: UnitLength.meters) as Measurement<Unit>
let area = Measurement(value: 15, unit: UnitArea.squareMeters) as Measurement<Unit>
let time = Measurement(value: 22, unit: UnitDuration.seconds) as Measurement<Unit>
let temp = Measurement(value: 30, unit: UnitTemperature.celsius) as Measurement<Unit>

var measurements = [
    area,
    time,
    length,
    temp
] as [Measurement<Unit>]

func getMeasurement<T: Unit>(ofType: T.Type, from measurements: [Measurement<Unit>]) -> Measurement<T>? 
    let firstMeasurementsOfSameType: Measurement<T>? = measurements.compactMap 
        guard let unit = $0.unit as? T else  return nil 
        return Measurement(value: $0.value, unit: unit)
    .first
    
    return firstMeasurementsOfSameType


let measurement = getMeasurement(ofType: UnitLength.self, from: measurements) 

print(String(describing: measurement)) // Prints Optional(10.0 m)

【讨论】:

以上是关于使用不同 UnitType 进行测量的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

在 Visual C++ 中测量操作速度的最佳方法

测量 SSIS 数据流的进度

使用 Earcut 对 OpenType.js 中的路径数据进行三角测量

在Python中进行模糊键查找的最佳方法?

高数值维度数据的最佳学习模型? (使用 Rapidminer)

使用 PHPUnit 对具有多种用户类型的网站进行单元测试的最佳方法