我想扩展Array类,以便它可以知道它是否已排序(升序)。我想添加一个名为isSorted
的计算属性。如何说明Array的元素可以比较?
我目前在Playground中的实现
extension Array {
var isSorted: Bool {
for i in 1..self.count {
if self[i-1] > self[i] { return false }
}
return true
}
}
// The way I want to get the computed property
[1, 1, 2, 3, 4, 5, 6, 7, 8].isSorted //= true
[2, 1, 3, 8, 5, 6, 7, 4, 8].isSorted //= false
错误 Could not find an overload for '>' that accepts the supplied arguments
当然,我仍然遇到错误,因为Swift不知道如何比较这些元素。如何在Swift中实现此扩展?或者我在这里做错了什么?
答案 0 :(得分:28)
自由函数的替代解决方案是执行Swift的内置Array.sort
和Array.sorted
方法,并要求您将合适的比较器传递给方法:
extension Array {
func isSorted(isOrderedBefore: (T, T) -> Bool) -> Bool {
for i in 1..<self.count {
if !isOrderedBefore(self[i-1], self[i]) {
return false
}
}
return true
}
}
[1, 5, 3].isSorted(<) // false
[1, 5, 10].isSorted(<) // true
[3.5, 2.1, -5.4].isSorted(>) // true
答案 1 :(得分:21)
在Swift 2.0中,您现在可以扩展协议!
extension CollectionType where Generator.Element: Comparable {
public var isSorted: Bool {
var previousIndex = startIndex
var currentIndex = startIndex.successor()
while currentIndex != endIndex {
if self[previousIndex] > self[currentIndex] {
return false
}
previousIndex = currentIndex
currentIndex = currentIndex.successor()
}
return true
}
}
[1, 2, 3, 4].isSorted // true
["a", "b", "c", "e"].isSorted // true
["b", "a", "c", "e"].isSorted // false
[/* Anything not implementing `Comparable` */].isSorted // <~~ Type-error
请注意,因为我们使用Indexable.Index
而不是简单的Int
作为索引,所以我们必须使用while循环,这看起来不那么漂亮和清晰。
答案 2 :(得分:7)
你已经遇到了Swift的泛型问题,这些问题无法按照你现在喜欢的方式解决(可能是未来的Swift版本)。另请参阅Swift Generics issue。
目前,您需要定义一个函数(例如在全局范围内):
func isSorted<T: Comparable>(array: Array<T>) -> Bool {
for i in 1..<array.count {
if array[i-1] > array[i] {
return false
}
}
return true
}
let i = [1, 2, 3]
let j = [2, 1, 3]
let k = [UIView(), UIView()]
println(isSorted(i)) // Prints "true"
println(isSorted(j)) // Prints "false"
println(isSorted(k)) // Error: Missing argument for parameter #2 in call
错误信息具有误导性,恕我直言,因为实际错误类似于&#34; UIView不满足类型约束Comparable&#34;。
答案 3 :(得分:4)
实际上,您可以扩展dice_coef
协议以获得更通用的解决方案:
Sequence
答案 4 :(得分:2)
适应,solution将在Swift 4中运行
extension Array where Iterator.Element: Comparable {
func isSorted(isOrderedBefore: (Iterator.Element, Iterator.Element) -> Bool) -> Bool {
for i in 1 ..< self.count {
if isOrderedBefore(self[i], self[i-1]) {
return false
}
}
return true
}
}
答案 5 :(得分:1)
对我来说,最灵活的解决方案是结合使用NSAddict和Wes Campaigne的答案。即结合了能够扩展协议和将比较器函数作为参数传递的优点。这消除了仅将其用于数组并将其约束为符合Comparable
协议的元素的限制。
extension CollectionType
{
func isSorted(isOrderedBefore: (Generator.Element, Generator.Element) -> Bool) -> Bool
{
var previousIndex = startIndex
var currentIndex = startIndex.successor()
while currentIndex != endIndex
{
if isOrderedBefore(self[previousIndex], self[currentIndex]) == false
{
return false
}
previousIndex = currentIndex
currentIndex = currentIndex.successor()
}
return true
}
}
这可以在任何Collection
类型上使用,并且可以根据您的需要定义排序标准。
答案 6 :(得分:1)
通用函数zip()
可以为实现提供快捷方式。
extension Collection where Element: Comparable {
var isSorted: Bool {
guard count > 1 else {
return true
}
let pairs = zip(prefix(count - 1), suffix(count - 1))
return !pairs.contains { previous, next in
previous > next
}
}
}
[0, 1, 1, 2].isSorted // true
[0, 2, 2, 1].isSorted // false
答案 7 :(得分:0)
这是Swift 4中的一种解决方案,当self.count
等于或小于1时不会崩溃。
extension Array where Element: Comparable {
func isSorted(by isOrderedBefore: (Element, Element) -> Bool) -> Bool {
for i in stride(from: 1, to: self.count, by: 1) {
if !isOrderedBefore(self[i-1], self[i]) {
return false
}
}
return true
}
}
此代码段假定已经对1或0个元素的数组进行了排序。
在for循环范围内以1开头的原因是:如果self.count <= 1,则将跳过循环,性能会有所提高。使用stride
代替..<
可以避免当上限小于范围的下限时发生崩溃。
以下是一些示例:
[1, 2, 3].isSorted(by: >) // true
[3, 2, 2].isSorted(by: >=) // true
[1, 4, 7].isSorted(by: {x, y in
return x + 2 < y * y
}) // true
let a: [Int] = [1]
a.isSorted(by: <) // true
let b: [Int] = []
b.isSorted(by: >) // true
答案 8 :(得分:0)
如果您想要不带参数的简单函数,例如Swift中的sort()或sorted():
extension Array where Element : Comparable {
func isSorted() -> Bool {
guard self.count > 1 else {
return true
}
for i in 1..<self.count {
if self[i-1] > self[i] {
return false
}
}
return true
}
}
答案 9 :(得分:0)
在Swift 4.2及更高版本中,您可以使用一些序列切片将allSatisfy
和zip
拼凑在一起:
extension Array where Element: Comparable{
func isAscending() -> Bool {
return zip(self, self.dropFirst()).allSatisfy(<=)
}
func isDescending() -> Bool {
return zip(self, self.dropFirst()).allSatisfy(>=)
}
}
答案 10 :(得分:0)
@kAzec的答案似乎在元素相等时不起作用。这是因为根据the documentation,areInIncreasingOrder(a,a)必须为false。
以下内容应该可以正常工作。
extension Sequence {
func isSorted(by areInIncreasingOrder: (Element, Element) throws -> Bool)
rethrows -> Bool {
var it = makeIterator()
guard var previous = it.next() else { return true }
while let current = it.next() {
if try !areInIncreasingOrder(previous, current) &&
areInIncreasingOrder(current, previous) {
return false
}
previous = current
}
return true
}
}
extension Sequence where Element: Comparable {
func isSorted() -> Bool {
return isSorted(by: <)
}
}
答案 11 :(得分:0)
总结以前的答案,有一个最小的通用数组扩展需要检查:
extension Array {
func isSorted(_ predicate: (Element, Element) throws -> Bool) -> Bool {
guard count > 1 else { return true }
return (try? zip(self, self.dropFirst()).allSatisfy(predicate)) == true
}
}
// Standard types
[1, 2, 3, 4, 5].isSorted(<) // true
[1, 2, 10, 4, 5].isSorted(<) // false
[10, 5, 4, 3, 1].isSorted(>) // true
[10, 20, 4, 3, 1].isSorted(>) // false
// Custom types
struct MyStruct {
let i: Int
}
let items = [MyStruct(i: 1), MyStruct(i: 2), MyStruct(i: 3), MyStruct(i: 10)]
let sorted = items.isSorted { $0.i < $1.i } // true