使用不同 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<Unit>
转换为 Measurement<UnitLength>
或 UnitArea 等...
它们是完全不同的类型。
但是当 Measurement<UnitLength>
被转换为 Measurement<Unit>
时,它仍然保留其值和单位类型信息。因此,您可以重新创建正确类型的测量值。
这是一个示例通用函数,它接受一个测量数组并返回第一个具有 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 进行测量的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
使用 Earcut 对 OpenType.js 中的路径数据进行三角测量