是否有一种简洁的结构或方法在Swift中使用范围来将范围兼容类型的任何值(例如:Double)分类为离散的,用户/程序员定义的类别?
例如:有人可能想用摄氏温度来对地球区域进行分类。..<-10
- 北极-10..<10
- 感冒10..<25
- 温和25...
- 热门鉴于已存在通用结构,程序员可能会写:
let temperatureClassifier = MagicClassifier<Int, String>(
lowestCategory: "Arctic",
aboveValueCategoryIs: [
-10: "Cold",
10: "Temperate",
25: "Hot"
]
)
// ...more code...
print(temperatureClassifier.classify(12))
并期望结果“温和”。
(这个例子对于一些if / else(或switch)块和这样的静态范围是微不足道的,但是如果用户/程序员想要定义更多的类别,if / else策略会变得难以处理,或者如果类别是用户定义的,不能事先写出来)
如果可能的话,我想避免选项和try-catch函数,因为这些类别的分类没有歧义。即在上面的MagicClassifier
中,如果aboveValueCategoryIs:
字典为空(替换为[:]
),则Int.min
到Int.max
的所有值都会传递给{{1}应该返回“北极”。
注意:我不一定要求有人写.classify(_:)
;更多的检查是没有内置的,我错过了。
答案 0 :(得分:1)
没有内置的构造,但它可以通过一个看起来像这样的简短通用函数来实现:
func clasify<T, R>(value: T, with table: [(Range<T>,R)], defaultValue: R) -> R {
return table.first { $0.0 ~= value }?.1 ?? defaultValue
}
您可以像这样使用它:
let temperatureMapping: [(Range<Int>, String)] = [
(Int.min ..< -10, "Arctic"),
(-10 ..< 10, "Cold"),
(10 ..< 25, "Temperate"),
(25 ..< Int.max, "Hot")]
let temperatureClass = clasify(value: 12, with: temperatureMapping, defaultValue: "Invalid")
// Temperate
如果您确定查找表是详尽无遗的,您可以删除“defaultValue”参数并在first
上使用force unwrap,但是我不推荐这样的内容。
答案 1 :(得分:0)
我已经在下面写了MagicClassifier
版本:
struct ClassifiableRange<Category, Value> where Value: Comparable, Value: Hashable {
private let lowestCategory: Category
private let lowestValueInRanges: Value?
private let ranges: [ Range<Value> ]
private let categoryDictionary: [ Value : Category ]
private let highestCategory: Category
init(lowestCategory: Category, aboveValueCategoryIs: [Value : Category]) {
self.lowestCategory = lowestCategory
var sortedKeys = aboveValueCategoryIs.keys.sorted()
self.lowestValueInRanges = sortedKeys.removeFirst()
self.highestCategory = sortedKeys.last.map({ aboveValueCategoryIs[$0]! }) ?? lowestCategory
var ranges: [ Range<Value> ] = []
if
let lowestValueInRanges = self.lowestValueInRanges,
!sortedKeys.isEmpty
{
var lowerBound = lowestValueInRanges
for upperBoundExclusive in sortedKeys {
ranges.append(lowerBound..<upperBoundExclusive)
lowerBound = upperBoundExclusive
}
}
self.ranges = ranges
self.categoryDictionary = aboveValueCategoryIs
}
func classify(_ value: Value) -> Category {
guard
let lowestValueInRanges = self.lowestValueInRanges,
value >= lowestValueInRanges // There is more than one category, AND the value is not in the lowest category
else { return self.lowestCategory }
for range in ranges {
if range.contains(value) {
return self.categoryDictionary[range.lowerBound]!
}
}
return self.highestCategory
}
}
用法:
let temperatureClassifier = ClassifiableRange<String, Int>(
lowestCategory: "Arctic",
aboveValueCategoryIs: [
-10: "Cold",
10: "Temperate",
25: "Hot"
]
)
temperatureClassifier.classify(12) // Returns "Temperate"
但仍然会欣赏任何内置的做同样事情的方式。