I need a class to handle sequences of the following Symbols: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.
Digits, lowercase and uppercase symbols will be handled differently.
I will be doing large numbers of operations and so want my implementation to be lightning fast.
So I’m looking at the best way to define Symbol to be as fast as posisble.
To be Swifty I would have done something like:
enum Symbol {
case number(Int)
case lowercase(Character)
case uppercase(Character)
}
but some simple operations such as implementing Equatable become rather cumbersome.
I concluded that an enum with an Int type would be the way to go for efficiency.
I’ve currently ended up with the following:
// Symbol.swift
import Foundation
enum Symbol: Int {
case digit0 = 0, digit1, digit2, digit3, digit4, digit5, digit6, digit7, digit8, digit9
case chara, charb, charc, chard ,chare, charf, charg, charh, chari, charj, chark, charl, charm, charn, charo, charp, charq, charr, chars, chart, charu, charv, charw, charx, chary, charz
case charA, charB, charC, charD, charE, charF, charG, charH, charI, charJ, charK, charL, charM, charN, charO, charP, charQ, charR, charS, charT, charU, charV, charW, charX, charY, charZ
static func fromCharacter(_ char: Character) -> Symbol {
if let symbol = CHARTOSYMBOL[char] {
return symbol
} else {
fatalError("ERROR : Can't convert character '\(char)' into a symbol.")
}
}
static func fromString(_ string: String) -> Symbol {
return fromCharacter(string.characters.first!)
}
func toCharacter() -> Character {
if let char = SYMBOLTOCHAR[self] {
return char
} else {
fatalError("ERROR : Can't convert symbol '\(self.rawValue)' into a character.")
}
}
func toString() -> String {
return String(toCharacter())
}
init(_ char: Character) {
self = Symbol.fromCharacter(char)
}
init(_ string: String) {
self = Symbol.fromString(string)
}
}
let DIGIT0 = 0
let DIGIT9 = 9
let DIGIT = DIGIT0...DIGIT9
let CHARa = Symbol.chara.rawValue
let CHARz = Symbol.charz.rawValue
let CHARLOWER = CHARa...CHARz
let CHARA = Symbol.charA.rawValue
let CHARZ = Symbol.charZ.rawValue
let CHARUPPER = CHARA...CHARZ
let SYMBOL = DIGIT0...CHARZ
let ALPHABETLOWER = [Symbol.chara, Symbol.charb, Symbol.charc, Symbol.chard, Symbol.chare,Symbol.charf,Symbol.charg, Symbol.charh, Symbol.chari, Symbol.charj, Symbol.chark, Symbol.charl, Symbol.charm, Symbol.charn, Symbol.charo, Symbol.charp, Symbol.charq, Symbol.charr, Symbol.chars, Symbol.chart, Symbol.charu, Symbol.charv, Symbol.charw, Symbol.charx, Symbol.chary, Symbol.charz]
let ALPHABETUPPER = [Symbol.charA, Symbol.charB, Symbol.charC, Symbol.charD, Symbol.charE,Symbol.charF,Symbol.charG, Symbol.charH, Symbol.charI, Symbol.charJ, Symbol.charK, Symbol.charL, Symbol.charM, Symbol.charN, Symbol.charO, Symbol.charP, Symbol.charQ, Symbol.charR, Symbol.charS, Symbol.charT, Symbol.charU, Symbol.charV, Symbol.charW, Symbol.charX, Symbol.charY, Symbol.charZ]
let CHARTOSYMBOL: [Character:Symbol] = ["0":Symbol.digit0, "1":Symbol.digit1, "2":Symbol.digit2, "3":Symbol.digit3, "4":Symbol.digit4, "5":Symbol.digit5, "6":Symbol.digit6, "7":Symbol.digit7, "8":Symbol.digit8, "9":Symbol.digit9, "a":Symbol.chara, "b":Symbol.charb, "c":Symbol.charc, "d":Symbol.chard, "e":Symbol.chare, "f":Symbol.charf, "g":Symbol.charg, "h":Symbol.charh, "i":Symbol.chari, "j":Symbol.charj, "k":Symbol.chark, "l":Symbol.charl, "m":Symbol.charm, "n":Symbol.charn, "o":Symbol.charo, "p":Symbol.charp, "q":Symbol.charq, "r":Symbol.charr, "s":Symbol.chars, "t":Symbol.chart, "u":Symbol.charu, "v":Symbol.charv, "w":Symbol.charw, "x":Symbol.charx, "y":Symbol.chary, "z":Symbol.charz, "A":Symbol.charA, "B":Symbol.charB, "C":Symbol.charC, "D":Symbol.charD, "E":Symbol.charE, "F":Symbol.charF, "G":Symbol.charG, "H":Symbol.charH, "I":Symbol.charI, "J":Symbol.charJ, "K":Symbol.charK, "L":Symbol.charL, "M":Symbol.charM, "N":Symbol.charN, "O":Symbol.charO, "P":Symbol.charP, "Q":Symbol.charQ, "R":Symbol.charR, "S":Symbol.charS, "T":Symbol.charT, "U":Symbol.charU, "V":Symbol.charV, "W":Symbol.charW, "X":Symbol.charX, "Y":Symbol.charY, "Z":Symbol.charZ]
let SYMBOLTOCHAR: [Symbol:Character] = [Symbol.digit0:"0", Symbol.digit1:"1", Symbol.digit2:"2", Symbol.digit3:"3", Symbol.digit4:"4", Symbol.digit5:"5", Symbol.digit6:"6", Symbol.digit7:"7", Symbol.digit8:"8", Symbol.digit9:"9", Symbol.chara:"a", Symbol.charb:"b", Symbol.charc:"c", Symbol.chard:"d", Symbol.chare:"e", Symbol.charf:"f", Symbol.charg:"g", Symbol.charh:"h", Symbol.chari:"i", Symbol.charj:"j", Symbol.chark:"k", Symbol.charl:"l", Symbol.charm:"m", Symbol.charn:"n", Symbol.charo:"o", Symbol.charp:"p", Symbol.charq:"q", Symbol.charr:"r", Symbol.chars:"s", Symbol.chart:"t", Symbol.charu:"u", Symbol.charv:"v", Symbol.charw:"w", Symbol.charx:"x", Symbol.chary:"y", Symbol.charz:"z", Symbol.charA:"A", Symbol.charB:"B", Symbol.charC:"C", Symbol.charD:"D", Symbol.charE:"E", Symbol.charF:"F", Symbol.charG:"G", Symbol.charH:"H", Symbol.charI:"I", Symbol.charJ:"J", Symbol.charK:"K", Symbol.charL:"L", Symbol.charM:"M", Symbol.charN:"N", Symbol.charO:"O", Symbol.charP:"P", Symbol.charQ:"Q", Symbol.charR:"R", Symbol.charS:"S", Symbol.charT:"T", Symbol.charU:"U", Symbol.charV:"V", Symbol.charW:"W", Symbol.charX:"X", Symbol.charY:"Y", Symbol.charZ:"Z"]
extension Symbol: CustomStringConvertible {
var description: String { return toString() }
}
//Needed in earlier versions of Swift
//extension Symbol: Equatable {
//}
//To make Symbol Equatable
func ==(lhs: Symbol, rhs: Symbol) -> Bool {
return lhs.rawValue == rhs.rawValue
}
//Needed in earlier versions of Swift
//extension Symbol: Hashable {
// var hashValue : Int {
// return self.rawValue
// }
//}
extension Symbol : Comparable {
}
func < (lhs: Symbol, rhs: Symbol) -> Bool {
return lhs.hashValue < rhs.hashValue
}
The Global range definitions are so I can do things like:
switch symbol.rawValue {
case CHARLOWER: doOneThing
case DIGIT: doAnotherThing
default: break
}
But I’m unsatisfied:
- Don’t like there being many Global constants
- Don’t like the cumbersome mapping
- Don’t like some of the inefficiencies (at least they are in the translation of Characters into Symbols and vice-versa rather than in comparisons.)
- Don’t like using Character as it’s a Grapheme cluster so will be less efficient than an Int.
Any help on making this class definition more succinct while keeping the code super fast would be much appreciated.
1 Answer 1
Half of computer science problems boil down to introducing a level of abstraction and throwing values into a hash map. Your problem appears to be in that half.
I recommend putting all relevant values in a symbol -> symbol_type lookup dictionary.
That said, I really don't understand why range checking and functions like isdigit and islower are not sufficient for your task. You speak of "lightning fast" but you posted no timings, and were vague on what operations matter in the doOneThing
routine. To evaluate if one implementation approach is more suitable than another, you'll want objective measurements.
-
\$\begingroup\$ Thanks J H, a symbol -> symbol_type dictionary sounds good and with hashing should be fast. I take your comments about speed. When I get further along with the code I'll do some speed analysis and if seems relevant will post back here. \$\endgroup\$Cortado-J– Cortado-J2017年08月31日 17:39:13 +00:00Commented Aug 31, 2017 at 17:39