在Swift中将任何数字分类为类别范围

时间:2018-03-13 03:03:55

标签: swift range

是否有一种简洁的结构或方法在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.minInt.max的所有值都会传递给{{1}应该返回“北极”。

注意:我不一定要求有人写.classify(_:);更多的检查是没有内置的,我错过了。

2 个答案:

答案 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"

但仍然会欣赏任何内置的做同样事情的方式。