39

我已经定义了一个枚举来代表一个“站”的选择;站由唯一的正整数定义,因此我创建了以下枚举以允许负值表示特殊选择:

enum StationSelector : Printable {
    case Nearest
    case LastShown
    case List
    case Specific(Int)

    func toInt() -> Int {
        switch self {
        case .Nearest:
            return -1
        case .LastShown:
            return -2
        case .List:
            return -3
        case .Specific(let stationNum):
            return stationNum
        }
    }

    static func fromInt(value:Int) -> StationSelector? {
        if value > 0 {
            return StationSelector.Specific(value)
        }
        switch value {
        case -1:
            return StationSelector.Nearest
        case -2:
            return StationSelector.LastShown
        case -3:
            return StationSelector.List
        default:
            return nil
        }
    }

    var description: String {
    get {
        switch self {
        case .Nearest:
            return "Nearest Station"
        case .LastShown:
            return "Last Displayed Station"
        case .List:
            return "Station List"
        case .Specific(let stationNumber):
            return "Station #\(stationNumber)"
        }
    }
    }
}

我想将这些值用作字典中的键。声明 Dictionary 会产生 StationSelector 不符合 Hashable 的预期错误。使用简单的哈希函数很容易符合 Hashable:

var hashValue: Int {
get {
    return self.toInt()
}
}

但是,Hashable需要符合Equatable,并且我似乎无法在我的枚举上定义 equals 运算符以满足编译器的要求。

func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
    return lhs.toInt() == rhs.toInt()
}

编译器抱怨这是在一行中的两个声明,并且想要放一个;after func,这也没有意义。

有什么想法吗?

4

4 回答 4

25

关于枚举作为字典键的信息:

从斯威夫特书中:

默认情况下,没有关联值的枚举成员值(如枚举中所述)也是可散列的。

但是,您的 Enumeration 确实有一个具有关联值的成员值,因此Hashable必须由您手动添加一致性。

解决方案

您的实现问题在于 Swift 中的运算符声明必须在全局范围内。

只需移动:

func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
    return lhs.toInt() == rhs.toInt()
}

enum定义之外,它将起作用。

检查文档以获取更多信息。

于 2014-07-03T20:22:50.640 回答
6

我努力尝试使enum与关联的值符合Hashable.

这是我使我enum的关联值符合,Hashable因此它可以被排序或用作Dictionary键,或者做任何其他Hashable可以做的事情。

您必须使您的关联值enum符合,Hashable因为关联值enums不能具有原始类型。

public enum Components: Hashable {
    case None
    case Year(Int?)
    case Month(Int?)
    case Week(Int?)
    case Day(Int?)
    case Hour(Int?)
    case Minute(Int?)
    case Second(Int?)

    ///The hashValue of the `Component` so we can conform to `Hashable` and be sorted.
    public var hashValue : Int {
        return self.toInt()
    }

    /// Return an 'Int' value for each `Component` type so `Component` can conform to `Hashable`
    private func toInt() -> Int {
        switch self {
        case .None:
            return -1
        case .Year:
            return 0
        case .Month:
            return 1
        case .Week:
            return 2
        case .Day:
            return 3
        case .Hour:
            return 4
        case .Minute:
            return 5
        case .Second:
            return 6
        }

    }

}

还需要重写相等运算符:

/// Override equality operator so Components Enum conforms to Hashable
public func == (lhs: Components, rhs: Components) -> Bool {
    return lhs.toInt() == rhs.toInt()
}
于 2015-07-03T17:03:17.910 回答
3

为了提高可读性,让我们StationSelector用 Swift 3 重新实现:

enum StationSelector {
    case nearest, lastShown, list, specific(Int)
}

extension StationSelector: RawRepresentable {

    typealias RawValue = Int

    init?(rawValue: RawValue) {
        switch rawValue {
        case -1: self = .nearest
        case -2: self = .lastShown
        case -3: self = .list
        case (let value) where value >= 0: self = .specific(value)
        default: return nil
        }
    }

    var rawValue: RawValue {
        switch self {
        case .nearest: return -1
        case .lastShown: return -2
        case .list: return -3
        case .specific(let value) where value >= 0: return value
        default: fatalError("StationSelector is not valid")
        }
    }

}

Apple 开发人员 API 参考说明了有关Hashable协议:

当您定义一个没有关联值的枚举时,它会Hashable自动获得一致性,并且您可以Hashable通过添加单个hashValue属性来将一致性添加到您的其他自定义类型。

因此,因为StationSelector实现了关联值,所以必须手动使StationSelector符合协议。Hashable


第一步是实现==操作符并StationSelector符合Equatable协议:

extension StationSelector: Equatable {

    static func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
        return lhs.rawValue == rhs.rawValue
    }

}

用法:

let nearest = StationSelector.nearest
let lastShown = StationSelector.lastShown
let specific0 = StationSelector.specific(0)

// Requires == operator
print(nearest == lastShown) // prints false
print(nearest == specific0) // prints false

// Requires Equatable protocol conformance
let array = [nearest, lastShown, specific0]
print(array.contains(nearest)) // prints true

实现Equatable协议后,您可以使StationSelector符合Hashable协议:

extension StationSelector: Hashable {

    var hashValue: Int {
        return self.rawValue.hashValue
    }

}

用法:

// Requires Hashable protocol conformance
let dictionnary = [StationSelector.nearest: 5, StationSelector.lastShown: 10]

以下代码显示了使用 Swift 3StationSelector使其符合协议所需的实现:Hashable

enum StationSelector: RawRepresentable, Hashable {

    case nearest, lastShown, list, specific(Int)

    typealias RawValue = Int

    init?(rawValue: RawValue) {
        switch rawValue {
        case -1: self = .nearest
        case -2: self = .lastShown
        case -3: self = .list
        case (let value) where value >= 0: self = .specific(value)
        default: return nil
        }
    }

    var rawValue: RawValue {
        switch self {
        case .nearest: return -1
        case .lastShown: return -2
        case .list: return -3
        case .specific(let value) where value >= 0: return value
        default: fatalError("StationSelector is not valid")
        }
    }

    static func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
        return lhs.rawValue == rhs.rawValue
    }

    var hashValue: Int {
        return self.rawValue.hashValue
    }

}
于 2017-02-26T23:02:50.650 回答
0

只是为了强调 Cezar 之前所说的话。如果您可以避免使用成员变量,则无需实现等于运算符来使枚举可散列 - 只需给它们一个类型!

enum StationSelector : Int {
    case Nearest = 1, LastShown, List, Specific
    // automatically assigned to 1, 2, 3, 4
}

这就是你所需要的。现在,您还可以使用 rawValue 启动它们或稍后检索它。

let a: StationSelector? = StationSelector(rawValue: 2) // LastShown
let b: StationSelector = .LastShown

if(a == b)
{
    print("Selectors are equal with value \(a?.rawValue)")
}

有关详细信息,请查看文档

于 2016-07-20T11:24:09.170 回答