はじめに
- 前回の投稿につづいて、swiftの不甲斐なさのあまり、怒りにまかせて一工夫してしまった。
Character型の特徴
- 競技プログラミングしてると、文字列(String)を扱うことが頻繁にあるけど、文字列を分解するため配列にすると、その要素はCharacter型になる。
let a = "a"
print(type(of:a)) // String
let abc = "abc"
let ary = Array(abc)
print(ary) // ["a", "b", "c"]
print(type(of:ary)) // Array<Character>
print(String(ary)) // "abc"
let c:Character = "c"
print(type(of:c)) // Character
// let cd:Character = "cd" -- error 二文字は駄目!
- Character型の特徴については、上記コードのとおり。
- で、Character型は、"a"の次は"b"という順序もあるから、こんなコードが可能かな?と思ったところ、イケたんだよ!
let az = "a" as Character ... "z" // -- この書き方は最新のswiftでは駄目みたい(詳細は、後述)
print(type(of:az)) // ClosedRange<Character>
print(az.contains("d")) // true
print(az.contains("D")) // false
- ここまで来たら、当然、これイキますわ
let az = "a" as Character ... "z" // -- この書き方は最新のswiftでは駄目みたい(詳細は、後述)
for c in az {
print(c)
}
-- error : protocol 'Sequence' requires that 'Character' conform to 'Strideable'
- はい、出ました、クソ仕様によるエラー!Character型で作ったレンジはStrideableプロトコルに準拠してないからfor文に使えません!!!
- 大事なことなので、もう一回言います。Character型で作ったレンジはStrideableプロトコルに準拠してないからfor文に使えません!!!
- ふざけんな〜〜〜!!!(全国50人くらいの swiftで競技プログラムしている人達 の声)
- なので、Character型で作ったレンジをfor文で使いたい人のために、このコードを捧げます。まぁ、swiftで競技プログラミングしてる人なら、...以下略
swiftで苦労している君に捧げるコード
- Strideableは次の2つのメソッドが必須です。
- advanced(by n: Int)
- nズラした値を返す。例えば、
"c".advanced(by:2) -> "e"のイメージ。
- nズラした値を返す。例えば、
- distance(to other: Self)
- 2つの値の差を返す。例えば、
"c".distance(to:"f") -> 3のイメージ。
- 2つの値の差を返す。例えば、
- advanced(by n: Int)
- じゃあ、つくります。
extension Character: Strideable {
public func advanced(by n: Int) -> Self {
return Character(UnicodeScalar(Int(self.asciiValue!) + n)!)
}
public func distance(to other: Self) -> Int {
return Int(other.asciiValue!) - Int(self.asciiValue!)
}
}
let a_z = "a" as Character ... "z" // -- この書き方は最新のswiftでは駄目みたい(詳細は、後述)
for c in a_z {
print(c,terminator:"") // abcdefghijklmnopqrstuvwxyz
}
print(Character("a").advanced(by:2)) // c
print(Character("a").distance(to:"d")) // 3
- うむ、成功!!
- でも、extensionの実装内容は意外と分かりづらいよね。文句はswift君に言ってくれ。一応説明すると、
- .asciiValue
- ASCIIコードを返します。オプショナル型のUInt8が返るので!を付けて、Int化が必要。
- ちなみに、"あ".asciiValueはnilとなります。"あ"はascii文字じゃないからね。
- UnicodeScalar
- Characterのイニシャライザが、asciiコードを受け付けないから、一度、UnicodeScalarをかます必要がある。本当に無駄よね。でもまぁ、Characterさんの事情もあるから許してあげよう。
- ちなみに、CharacterとUnicodeScalarの関係は、次を見て貰えば、なんとなく分かるだろうか。
- そういえば、
typealias UnicodeScalar = Unicode.Scalarですlet ka = "\u{304B}" as Unicode.Scalar let daku = "\u{3099}" as Unicode.Scalar // let ka_daku = "\u{304B}\u{3099}" as Unicode.Scalar -- エラー let ga = "\u{304C}" as Unicode.Scalar print(ka,daku) // か ゙ let ka_c = "\u{304B}" as Character let daku_c = "\u{3099}" as Character let ka_daku_c = "\u{304B}\u{3099}" as Character let ga_c = "\u{304C}" as Character print(ka_c,daku_c,ka_daku_c,ga_c) // か ゙ が が print(ka_daku_c == ga_c) // true let ka_num = Unicode.Scalar(0x304b) // let ka_c_num = Character(0x304b) -- エラー let ka_c_num = Character(Unicode.Scalar(0x304b)!) print(ka_num!) // か print(ka_c_num) // か - Characterは、"\u{304B}\u{3099}"と"\u{304C}" が一致するのがポイント高いよね。だからこそ、Characterは数値でイニシャライズできず、UnicodeScalarをかまさないと駄目なんだろうね。糞面倒。
- ちなみに、このextensionは、a-zやA-Zではつかえるけど、ascii文字じゃない「あ〜ん」では使えないから注意してね。
- .asciiValue
注意!!!
Characterのレンジ生成で、最新のswiftでは、
× let a_z = "a" as Character ... "z"
の表記は駄目になったみたいです。
「paiza.io」ではイケたけど、「atcoderの提出」ではアウト。
○let a_z = ("a" as Character) ... "z"
○let a_z = Character("a")..."z"
ならイケます。
駄目になった書き方の方が、スッキリしてて、オシャレで好きなのに...
さいごに
- a-zをレンジに出来るなら、forのレンジでも使わせてくれ!
- [a,c,e,...]のような、一つ飛ばしみたいな使い方をするつもりもないのに、Strideableに強制的に準拠させようとしないで欲しいです。