I am trying to extend Character
to conform to Strideable
in order to create a CountableClosedRange
of Character
types. In the end, I would like to have something like this which prints the whole alphabet:
("A"..."Z").forEach{
print($0)
}
For the time being, I am using UnicodeScalar
types to calculate the distance between two characters. Because a scalar isn't available from a Character
type, I need to create a String from the Character, get the first scalar's value, and calculate the distance between them:
extension Character: Strideable {
func distance(to other: Character) -> Character.Stride {
return abs(String(self).unicodeScalars.first?.value - String(other).unicodeScalars.first!.value)
}
func advanced(by n: Character.Stride) -> Character {
return Character(UnicodeScalar(String(self).unicodeScalars.first!.value + n))
}
}
Even with this, I get the error that Character
does not conform to protocol Strideable
and _Strideable
. The compiler does not appear to be picking up the Stride
associated type which comes with Strideable
:
public protocol Strideable : Comparable {
/// A type that can represent the distance between two values of `Self`.
associatedtype Stride : SignedNumber
// ...
}
What am I missing?
As already said, because a Character
can be made up of multiple unicode scalars, you cannot accurately determine how many different valid character representations lie between two arbitrary characters, and is therefore not a good candidate for conformance to Stridable
.
One approach to your problem of simply wanting to print out the alphabet is to conform UnicodeScalar
, rather than Character
, to Stridable
– allowing you to work with characters that are represented by a single unicode code point, and advance them based on that code point.
extension UnicodeScalar : Strideable {
public func distance(to other: UnicodeScalar) -> Int {
return Int(other.value) - Int(self.value)
}
/// Returns a UnicodeScalar where the value is advanced by n.
///
/// - precondition: self.value + n represents a valid unicode scalar.
///
public func advanced(by n: Int) -> UnicodeScalar {
let advancedValue = n + Int(self.value)
guard let advancedScalar = UnicodeScalar(advancedValue) else {
fatalError("\(String(advancedValue, radix: 16)) does not represent a valid unicode scalar value.")
}
return advancedScalar
}
}
Now you can form a CountableClosedRange<UnicodeScalar>
, and can freely convert each individual element to a Character
or String
if desired:
("A"..."Z").forEach {
// You can freely convert scalar to a Character or String
print($0, Character($0), String($0))
}
// Convert CountableClosedRange<UnicodeScalar> to [Character]
let alphabetCharacters = ("A"..."Z").map {Character($0)}
This wouldn't work the way you'd expect it to even if you could make it work. How many characters do you believe are between "A" and "Z"? Without defining your encoding, this isn't meaningful. In fact, if you explore how Characters conform to Comparable, they act more like floats than like integers. For example:
"N" < "Ń" // true
"Ń" < "Ñ" // true
"Ń" < "O" // true
Between N and O are many modifications on N, possibly an unbounded number given Unicode's ability to compose characters. (This is the same if you wrap these in Character()
.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With