Как перечислить перечисление со строковым типом?
enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
Например, как я могу сделать что-то вроде:
for suit in Suit {
// do something with suit
print(suit.rawValue)
}
Результирующий пример:
♠
♥
♦
♣
41 ответ
С некоторыми изменениями в ответе ( /questions/21807433/kak-perechislit-perechislenie-so-strokovyim-tipom/21807557#21807557) и поддержке Swift 4.
enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
static func iterateEnum() -> AnyIterator<Suit> {
var i = 0
return AnyIterator {
let next = withUnsafeBytes(of: &i) { $0.load(as: Suit.self) }
if next.hashValue != i { return nil }
i += 1
return next
}
}
}
for f in Suit.iterateEnum() {
print(f.rawValue)
}
Результат:
♠
♥
♦
♣
(Улучшение Karthik Кумар ответ)
Это решение использует компилятор, чтобы гарантировать, что вы не пропустите дело.
enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
static var enumerate: [Suit] {
switch Suit.spades {
// make sure the two lines are identical ^_^
case .spades, .hearts, .diamonds, .clubs:
return [.spades, .hearts, .diamonds, .clubs]
}
}
}
Иногда вы можете иметь дело с перечислимым типом с базовым целочисленным типом, который изменяется на протяжении всего жизненного цикла разработки программного обеспечения. Вот пример, который хорошо работает для этого случая:
public class MyClassThatLoadsTexturesEtc
{
//...
// Colors used for gems and sectors.
public enum Color: Int
{
// Colors arranged in order of the spectrum.
case First = 0
case Red, Orange, Yellow, Green, Blue, Purple, Pink
// --> Add more colors here, between the first and last markers.
case Last
}
//...
public func preloadGems()
{
// Preload all gems.
for i in (Color.First.toRaw() + 1) ..< (Color.Last.toRaw())
{
let color = Color.fromRaw(i)!
loadColoredTextures(forKey: color)
}
}
//...
}
Я использовал метод ниже, предполагая, что я знаю, какое значение является последним в перечислении рангов, и все ранги имеют инкрементные значения после туза
Я предпочитаю этот путь, так как он чистый и маленький, легкий для понимания
func cardDeck() -> Card[] {
var cards: Card[] = []
let minRank = Rank.Ace.toRaw()
let maxRank = Rank.King.toRaw()
for rank in minRank...maxRank {
if var convertedRank: Rank = Rank.fromRaw(rank) {
cards.append(Card(rank: convertedRank, suite: Suite.Clubs))
cards.append(Card(rank: convertedRank, suite: Suite.Diamonds))
cards.append(Card(rank: convertedRank, suite: Suite.Hearts))
cards.append(Card(rank: convertedRank, suite: Suite.Spades))
}
}
return cards
}
Есть умный способ, и разочарование, как оно есть, иллюстрирует разницу между двумя различными типами перечислений.
Попробуй это:
func makeDeck() -> Card[] {
var deck: Card[] = []
var suits: Suit[] = [.Hearts, .Diamonds, .Clubs, .Spades]
for i in 1...13 {
for suit in suits {
deck += Card(rank: Rank.fromRaw(i)!, suit: suit)
}
}
return deck
}
Дело в том, что перечисление, подкрепленное числами (необработанными значениями), неявно явно упорядочено, тогда как перечисление, не подкрепленное числами, явно неявно неупорядочено.
Например, когда мы присваиваем значения перечислимых значений, язык достаточно хитр, чтобы выяснить, в каком порядке находятся числа. Если, с другой стороны, мы не задаем ему порядок, когда мы пытаемся перебрать значения, которые выбрасывает язык поднимает руки и говорит: "Да, но какой из них вы хотите сделать первым?"
Другие языки, которые могут сделать это (перебирая неупорядоченные перечисления), могут быть теми же языками, где все находится "под капотом" на самом деле карта или словарь, и вы можете перебирать ключи карты, независимо от того, есть ли логическое упорядочение или нет.
Таким образом, хитрость заключается в том, чтобы предоставить ему что-то, что явно упорядочено, в этом случае экземпляры мастей в массиве в нужном нам порядке. Как только вы даете это, Свифт похож на "ну, почему вы так не сказали?"
Другой краткий трюк заключается в использовании оператора forcing в функции fromRaw. Это иллюстрирует еще одну "погрешность" относительно перечислений, что диапазон возможных значений для передачи часто больше, чем диапазон перечислений. Например, если мы скажем Rank.fromRaw(60), значение не будет возвращено, поэтому мы используем опциональную функцию языка, и там, где мы начнем использовать опциональные значения, вскоре последует форсирование. (Или поочередно конструкция if let, которая все еще кажется мне немного странной)
Адаптируем ответ @rintaro к Swift 3, в котором rintaro:
- написал функцию полезности
iterateEnum()
для итерации случаев для произвольных типов перечислений - реализованы идеи кастинга из ответа @Kametrixom
Swift3
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
var i = 0
return AnyGenerator {
let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
let value : T?
if next.hashValue == i {
value = next
} else {
value = nil
}
i = i + 1
return value
}
}
enum Rank: Int
{
case Ace = 0
case Two, Three, Four, Five, Six, Seve, Eight, Nine, Ten
case Jack, Queen, King
case Count
}
enum Suit : Int
{
case Spades = 0
case Hearts, Diamonds, Clubs
case Count
}
struct Card
{
var rank:Rank
var suit:Suit
}
class Test
{
func makeDeck() -> Card[]
{
let suitsCount:Int = Suit.Count.toRaw()
let rankCount:Int = Rank.Count.toRaw()
let repeatedCard:Card = Card(rank:Rank.Ace, suit:Suit.Spades)
let deck:Card[] = Card[](count:suitsCount*rankCount, repeatedValue:repeatedCard)
for i:Int in 0..rankCount
{
for j:Int in 0..suitsCount
{
deck[i*suitsCount+j] = Card(rank: Rank.fromRaw(i)!, suit: Suit.fromRaw(j)!)
}
}
return deck
}
}
На основании ответа Рика: это в 5 раз быстрее
Это заняло у меня немного больше, чем один метод в структуре, такой как быстрая книга, но я настроил следующие функции в enum. Я бы использовал протокол, я не уверен, почему, но с присвоенным ему рангом int он испортил
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seve, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self{
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "Queen"
case .King:
return "King"
default:
return String(self.toRaw())
}
}
mutating func next() -> Rank {
var rank = self
var rawrank = rank.toRaw()
var nrank:Rank = self
rawrank = rawrank + 1
if let newRank = Rank.fromRaw(rawrank) {
println("\(newRank.simpleDescription())")
nrank = newRank
} else {
return self
}
return nrank
}
}
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func color() -> String {
switch self{
case .Spades, .Clubs:
return "black"
default:
return "red"
}
}
func simpleDescription() -> String {
switch self{
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
mutating func next() -> Suit {
switch self{
case .Spades:
return Hearts
case .Hearts:
return Diamonds
case .Diamonds:
return Clubs
case .Clubs:
return Spades
}
}
}
struct Card {
var rank:Rank
var suit:Suit
func deck() -> Card[] {
var tRank = self.rank
var tSuit = self.suit
let tcards = 52 // we start from 0
var cards: Card[] = []
for i in 0..tcards{
var card = Card(rank: tRank, suit: tSuit)
cards.append(card)
tRank = tRank.next()
tSuit = tSuit.next()
}
return cards
}
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}
var card = Card(rank: .Ace, suit: .Spades)
var deck = card.deck()
надеюсь, это поможет в основном, я использовал немного общих знаний, но это может быть легко исправлено путем умножения мастей на ранг (если вы не используете стандартную колоду карт, и вам нужно будет соответственно изменить перечисления, а если, по сути, просто пройти через различные перечисления, чтобы сэкономить время, я использовал значения raw рядов, вы можете сделать то же самое для костюмов, если хотите, однако в примере его не было, поэтому я решил выяснить это, не меняя значения raw костюмов
Я добавил функцию count() и перебрал значения:
public enum MetricType : Int {
case mvps = 0
case allNBA = 1
case championshipRings = 2
case finalAppearances = 3
case gamesPlayed = 4
case ppg = 5
static func count() -> Int {
return (ppg.rawValue) + 1
}
static var allValues: [MetricType] {
var array: [MetricType] = Array()
var item : MetricType = MetricType.mvps
while item.rawValue < MetricType.count() {
array.append(item)
item = MetricType(rawValue: (item.rawValue + 1))!
}
return array
}
}
Я обнаружил несколько странное чувство, но гораздо более безопасный способ сделать это, не требуя ввода значений дважды или обращения к памяти значений перечисления, что делает его очень маловероятным.
По сути, вместо использования enum создайте структуру с одним экземпляром и задайте все константы значений enum. Переменные могут быть запрошены с помощью Mirror
public struct Suit{
// the values
let spades = "♠"
let hearts = "♥"
let diamonds = "♦"
let clubs = "♣"
// make a single instance of the Suit struct, Suit.instance
struct SStruct{static var instance: Suit = Suit()}
static var instance : Suit{
get{return SStruct.instance}
set{SStruct.instance = newValue}
}
// an array with all of the raw values
static var allValues: [String]{
var values = [String]()
let mirror = Mirror(reflecting: Suit.instance)
for (_, v) in mirror.children{
guard let suit = v as? String else{continue}
values.append(suit)
}
return values
}
}
Если вы используете этот метод, чтобы получить одно значение, вам нужно использовать Suit.instance.clubs
или же Suit.instance.spades
Но все это так скучно... Давайте сделаем некоторые вещи, которые делают это больше похожим на настоящее перечисление!
public struct SuitType{
// store multiple things for each suit
let spades = Suit("♠", order: 4)
let hearts = Suit("♥", order: 3)
let diamonds = Suit("♦", order: 2)
let clubs = Suit("♣", order: 1)
struct SStruct{static var instance: SuitType = SuitType()}
static var instance : SuitType{
get{return SStruct.instance}
set{SStruct.instance = newValue}
}
// a dictionary mapping the raw values to the values
static var allValuesDictionary: [String : Suit]{
var values = [String : Suit]()
let mirror = Mirror(reflecting: SuitType.instance)
for (_, v) in mirror.children{
guard let suit = v as? Suit else{continue}
values[suit.rawValue] = suit
}
return values
}
}
public struct Suit: RawRepresentable, Hashable{
public var rawValue: String
public typealias RawValue = String
public var hashValue: Int{
// find some integer that can be used to uniquely identify
// each value. In this case, we could have used the order
// variable because it is a unique value, yet to make this
// apply to more cases, the hash table address of rawValue
// will be returned, which should work in almost all cases
//
// you could also add a hashValue parameter to init() and
// give each suit a different hash value
return rawValue.hash
}
public var order: Int
public init(_ value: String, order: Int){
self.rawValue = value
self.order = order
}
// an array of all of the Suit values
static var allValues: [Suit]{
var values = [Suit]()
let mirror = Mirror(reflecting: SuitType.instance)
for (_, v) in mirror.children{
guard let suit = v as? Suit else{continue}
values.append(suit)
}
return values
}
// allows for using Suit(rawValue: "♦"), like a normal enum
public init?(rawValue: String){
// get the Suit from allValuesDictionary in SuitType, or return nil if that raw value doesn't exist
guard let suit = SuitType.allValuesDictionary[rawValue] else{return nil}
// initialize a new Suit with the same properties as that with the same raw value
self.init(suit.rawValue, order: suit.order)
}
}
Теперь вы можете делать такие вещи, как
let allSuits: [Suit] = Suit.allValues
или же
for suit in Suit.allValues{
print("The suit \(suit.rawValue) has the order \(suit.order)")
}
Однако, чтобы получить сингл, вам все равно нужно использовать SuitType.instance.spades
или же SuitType.instance.hearts
, Чтобы сделать это немного более интуитивным, вы можете добавить код Suit
что позволяет вам использовать Suit.type.*
вместо SuitType.instance.*
public struct Suit: RawRepresentable, Hashable{
// ...your code...
static var type = SuitType.instance
// ...more of your code...
}
Теперь вы можете использовать Suit.type.diamonds
вместо SuitType.instance.diamonds
, или же Suit.type.clubs
вместо SuitType.instance.clubs
Мое решение состоит в том, чтобы объявить массив со всеми возможностями enum, чтобы он мог пройти через все из них.
//Function inside struct Card
static func generateFullDeck() -> [Card] {
let allRanks = [Rank.Ace, Rank.Two, Rank.Three, Rank.Four, Rank.Five, Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten, Rank.Jack, Rank.Queen, Rank.King]
let allSuits = [Suit.Hearts, Suit.Diamonds, Suit.Clubs, Suit.Spades]
var myFullDeck: [Card] = []
for myRank in allRanks {
for mySuit in allSuits {
myFullDeck.append(Card(rank: myRank, suit: mySuit))
}
}
return myFullDeck
}
//actual use:
let aFullDeck = Card.generateFullDeck() //Generate the desired full deck
var allDesc: [String] = []
for aCard in aFullDeck {
println(aCard.simpleDescription()) //You'll see all the results in playground
}
На Свифте, enum
типы могут быть доступны как EnumType.Case
:
let tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.Plain)
Большую часть времени вы собираетесь использовать только enum
типы, когда у вас есть несколько вариантов для работы, и точно знаете, что вы собираетесь делать с каждым.
Было бы бессмысленно использовать for-in
структура при работе с enum
типы.
Вы можете сделать это, например:
func sumNumbers(numbers : Int...) -> Int {
var sum = 0
for number in numbers{
sum += number
}
return sum
}
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self {
case .Ace: return "ace"
case .Jack: return "jack"
case .Queen: return "queen"
case .King: return "king"
default: return String(self.toRaw())
}
}
}
enum Suit: Int {
case Spades = 1
case Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
case .Spades: return "spades"
case .Hearts: return "hearts"
case .Diamonds: return "diamonds"
case .Clubs: return "clubs"
}
}
func color() -> String {
switch self {
case .Spades, .Clubs: return "black"
case .Hearts, .Diamonds: return "red"
}
}
}
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
static func createPokers() -> Card[] {
let ranks = Array(Rank.Ace.toRaw()...Rank.King.toRaw())
let suits = Array(Suit.Spades.toRaw()...Suit.Clubs.toRaw())
let cards = suits.reduce(Card[]()) { (tempCards, suit) in
tempCards + ranks.map { rank in
Card(rank: Rank.fromRaw(rank)!, suit: Suit.fromRaw(suit)!)
}
}
return cards
}
}
Вот менее загадочный пример, если вы все еще хотите использовать перечисления для Rank
А также Suit
, Просто соберите их в массив, если вы хотите использовать for-in loop
перебирать каждый.
Пример стандартной колоды из 52 карт:
enum Rank: Int {
case Ace = 1, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
func name() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.toRaw())
}
}
}
enum Suit: Int {
case Diamonds = 1, Clubs, Hearts, Spades
func name() -> String {
switch self {
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
case .Hearts:
return "hearts"
case .Spades:
return "spades"
default:
return "NOT A VALID SUIT"
}
}
}
let Ranks = [
Rank.Ace,
Rank.Two,
Rank.Three,
Rank.Four,
Rank.Five,
Rank.Six,
Rank.Seven,
Rank.Eight,
Rank.Nine,
Rank.Ten,
Rank.Jack,
Rank.Queen,
Rank.King
]
let Suits = [
Suit.Diamonds,
Suit.Clubs,
Suit.Hearts,
Suit.Spades
]
class Card {
var rank: Rank
var suit: Suit
init(rank: Rank, suit: Suit) {
self.rank = rank
self.suit = suit
}
}
class Deck {
var cards = Card[]()
init() {
for rank in Ranks {
for suit in Suits {
cards.append(Card(rank: rank, suit: suit))
}
}
}
}
var myDeck = Deck()
myDeck.cards.count // => 52