将单个整数值视为Swift中的一个范围

问题描述:

我需要验证字符串的长度。为字符计数的允许值包括:将单个整数值视为Swift中的一个范围

  • 6 - 9个字符
  • 12个字符
  • 15个字符

所有字符串与不同的字符计数是无效的。因此,我想创建接受一个数范围,并评估串的夫特功能:

extension String { 

    func evaluateLength(validCharacterCounts: Range<Int>...) -> Bool { 
     // Implementation 
    } 

} 

现在我可以调用该函数用于单个Int范围:

"Live long and prosper".evaluateLength(validCharacterCounts: 6..<10) 

和多个Int范围:

"Live long and prosper".evaluateLength(validCharacterCounts: 6..<10, 15..<20) 

但我不能用单一的,分离的整数值调用函数:

"Live long and prosper".evaluateLength(validCharacterCounts: 6..<10, 12, 15) 

因为1215被分类为Int而不是Range<Int>

Swift compile error: Cannot convert value of type 'Int' to expected argument type 'Range'

有没有一种方式来对待一个整数,如斯威夫特一个Range,像自动浇铸成Range<Int>

(毕竟5相当于5..<6,所以从数学上讲5是一个范围为好。)

+0

另一种方法是传递一个'IndexSet'类型的参数。 –

+0

你的意思是像'evaluateLength(validCharacterCounts:IndexSet(6 .. Mischa

我想出了3个解决方案,但我也不都是那样令人开心的是,如果该方法接受IntRange<Int>但我们在这里?

1 - 你有类型安全,而且必须使用一些变通方法:

extension String { 
    func evaluateLength(validCharacterCounts: [Range<Int>]? = nil) -> Bool { 
     if let validCharCounts = validCharacterCounts { 
      for validCharCount in validCharCounts { 
       if validCharCount.contains(characters.count) { 
        return true 
       } 
      } 
     } 

     return false 
    } 
} 

extension Int { 
    var asRange: Range<Int> { 
     return self..<self+1 as Range<Int> 
    } 
} 

"Hello, playground".evaluateLength(validCharacterCounts: [17.asRange, 1, 25, 1..<45]) // true 

2 - 您没有类型安全:

extension String { 
    func evaluateLength(validCharacterCounts: [Any]? = nil) -> Bool { 
     if let validCharCounts = validCharacterCounts { 
      for validCharCount in validCharCounts { 
       if let range = validCharCount as? Range<Int> { 
        if range.contains(characters.count) { 
         return true 
        } 
       } else if let range = validCharCount as? CountableRange<Int> { 
        if range.contains(characters.count) { 
         return true 
        } 
       } else if let range = validCharCount as? CountableClosedRange<Int> { 
        if range.contains(characters.count) { 
         return true 
        } 
       } else if let int = validCharCount as? Int { 
        if int.asRange.contains(characters.count) { 
         return true 
        } 
       } else { 
        fatalError("Unexpected type: \(type(of: validCharCount))") 
       } 
      } 
     } 

     return false 
    } 
} 

extension Int { 
    var asRange: Range<Int> { 
     return self..<self+1 as Range<Int> 
    } 
} 

"Hello, playground".evaluateLength(validCharacterCounts: [12, 1, 4, 6, 14...18]) // true 

3 - 迄今最好的,但仍不完美的解决方案:

extension String { 
    func evaluateLength(validCharacterCounts: [Int]? = nil) -> Bool { 
     guard let validCharCounts = validCharacterCounts else { 
      return false 
     } 

     return validCharCounts.contains(characters.count) 

    } 
} 

"Hello, playground".evaluateLength(validCharacterCounts: [12, 1, 4, 6] + Array(14...18)) // true 
+0

我很喜欢类型安全,所以我不太欣赏2。 3几乎是我最终实现自己,但它不能解决问题,因为你仍然需要以不同的方式处理范围和单个整数。谢谢你的想法! :) – Mischa

这是我该怎么做。而不是处理范围语义,我只是用相关的初始化工具创建一个Set扩展。

extension Set where Element == Int { 
    init(with elements: [Int] = [], and ranges: Range<Int>...) { 
     var allElements = [elements] 
     ranges.forEach { 
      allElements.append(Array($0.lowerBound..<$0.upperBound)) 
     } 
     self.init(allElements.joined()) 
    } 
} 

let newSet = Set(with: [1, 3, 328], and: 6..<10, 15..<20) 
newSet.contains(3) //true 
newSet.contains(4) //false 
newSet.contains(16) //true 

这允许您仅传递单个值,仅传递范围或两者。唯一的注意事项是命名的参数将它们分开。

Set语义也意味着你可以创建静态常量,如果需要,也应该明显更快。

+0

我一直在尝试使用Sets。但是,这种方法在两个不同整数之间引入了“单个整数范围”和“实际”间隔之间的分隔。这就是为什么我问这个问题的原因,因为它们在语义上是_both_范围,所以我想以相同的方式对待它们。 – Mischa

+0

'evaluateLength(validCharacterCounts:5,7,10 ... 20,25)'比'evaluateLength(validCharacterCounts:5,7,25,validCharacterCountRanges:10 ... 20)'更直观。 – Mischa

+0

@Mischa我同意这样做很好,我试图找到一种协议的方式,但它可能不是类型安全目前可能的。 (这可能会在Swift 4中改变) – PeejWeej