Swift中Comparable协议为何未提供min属性?自定义数组扩展相关疑问
嘿,我最近在折腾Swift数组的扩展,想实现这么个功能:把数组里的元素两两配对,每组取较大的那个;如果数组长度是奇数,最后剩下的单个元素直接保留。举个例子:
- 输入
[1, 5, 100, 2, 7],要输出[5, 100, 7] - 输入
[1, 2, 18, 19, 5, 4],要输出[2, 19, 5]
最开始我绕了点弯路,后来调整出了能用的扩展代码:
extension Array where Element: Comparable { /// 两两配对取较大值,奇数长度时保留最后一个元素 var biggerOfPairs: [Element] { var result: [Element] = [] // 遍历每一对元素 for i in 0..<(self.count/2) { result.append(Swift.max(self[i * 2], self[(i * 2) + 1])) } // 单独处理奇数长度的末尾元素 if self.count % 2 == 1 { result.append(self[self.count - 1]) } return result } }
不过一开始我是想用那种“补占位元素”的老技巧——比如如果要3个一组处理,就补合适的占位元素让数组长度能被3整除,这样循环时不用处理边界异常。比如如果这个扩展只针对[Int],我可以补个Int.min在末尾,就能统一遍历所有组。但问题来了:Comparable协议里根本没有定义全局的min值!
我当时就纳闷了:怎么会设计Comparable协议却不带最小/最大可表示值的概念呢?文档里提到了NaN这种特殊情况,但也不该完全忽略边界场景啊?难道我漏了什么关键点?
后来仔细琢磨才想通:不是所有遵循Comparable的类型都有“极值”的。比如自定义一个按价格比较的Product类型:
struct Product: Comparable { let name: String let price: Double static func < (lhs: Product, rhs: Product) -> Bool { return lhs.price < rhs.price } }
你说什么是“最小的Product”?是价格为0的?还是空名称的?这完全取决于业务逻辑,根本没有通用的“最小”值。所以Comparable协议只保证类型能比较大小,不强制要求有全局极值——因为这个概念不是所有类型都具备的。
那如果确实需要用到极值的场景怎么办?其实可以自定义一个更具体的协议:
protocol BoundedComparable: Comparable { static var minRepresentable: Self { get } static var maxRepresentable: Self { get } } // 让Int遵循这个协议 extension Int: BoundedComparable { static let minRepresentable = Int.min static let maxRepresentable = Int.max } // 让Double遵循 extension Double: BoundedComparable { static let minRepresentable = Double.greatestFiniteMagnitude * -1 static let maxRepresentable = Double.greatestFiniteMagnitude }
这样数组扩展就可以限制在BoundedComparable上,就能用minRepresentable来补占位元素了。不过回到最开始的需求,其实我后来写的代码已经完美解决问题了——直接处理奇数长度的情况,根本不需要补占位元素,反而更简单直观,还避免了引入无关占位元素可能带来的逻辑问题。
总结一下:
- Comparable没有全局min/max,是因为不是所有可比较类型都具备“极值”概念,协议只保证比较能力
- 如果确实需要极值,可以自定义带极值约束的协议
- 很多时候直接处理边界情况,比补占位元素的技巧更简洁
内容来源于stack exchange

