https://youtu.be/ZUZRo5Kmn98
今回は話題を再び オプショナル
に戻して、その基本的な特徴について具体的に眺めていきます。内容的にはその雰囲気を掴む感じのものになっていて捉えにくい感じもありますけれど、まずは書籍の内容に沿ってイメージを描いてみながら、それを見ていく上で必要になる周辺視野を広げて行けたらいいなと思ってます。よろしくお願いしますね。
—————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #140
00:00 開始 01:29 前回の、文字列を数値に変換する例 03:09 文字列から数値への変換で基数を加味する 04:21 進数表現でよくある接頭辞は考慮されない 04:47 文字列からの数値変換で一般的ではない基数を指定してみる 05:50 標準では 36 進数まで 06:03 アルファベット文字列を数値化できる 06:20 シーザー暗号 07:25 文字に対する数値演算 09:10 Character.asciiValue 11:50 Base64 への符号化 13:46 変換できるかどうかをシンプルかつ確実に表現 14:26 オプショナルな Int は Int? と表記 15:29 Int? は Optional<Int> 16:00 Int? と書くか Optional<Int> と書くか 17:39 可読性も考慮して選んでいく 18:22 try? によるオプショナルのフラット化 20:21 ごちうさが何期まで続くか Swift に聞いてみた? 21:26 オプショナルの多段ネストは活かせる? 23:11 Brainfuck というプログラミング言語 24:33 入れ子にしたオプショナル型の構造 25:11 オプショナル型のメモリーレイアウト 26:09 オプショナルな Void 型 28:49 オプショナル型のバイトデータを眺めてみる 30:53 参照型を扱うときのオプショナル型 32:11 参照型における入れ子になったオプショナル型の表現方法 34:28 所感と次回の展望 ——————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #140
さて、今日はオプショナルについて話します。前々回から続いているテーマで、論理的な概要についても触れながら、実際の活用法に進んでいきましょう。前回はフェイラブルイニシャライザーに関する部分で脱線しましたが、今回はオプショナルの具体的な基礎について見ていきます。
スライドに沿って話を進めますが、流れが細かくない部分については広い視点でオプショナルがどういうものかを掴んでもらえたらと思います。
例えば、次のコードを見てください:
let number = Int("123")
このコードでは、文字列 "123" を整数に変換しています。変換が成功すると整数が返され、失敗すると nil
が返されます。これがオプショナルの基本的な考え方です。
また次に、コンバーティブナンバーがありましたね。このフォーシブルナンバーを使うことで、文字列から整数への変換が確実にできる場合、123が取れるようになっています。
let forcedNumber = Int("123")!
オプショナルのポイントは、変換が成功するか失敗するかわからないものがある場合、それを安全に処理できるようになることです。
例えば、次のように文字列 "123D" を変換しようとすると、結果は nil
になります:
let number = Int("123D")
このnumber
はオプショナル型 Int?
となり、失敗した場合は nil
が返されます。これにより、不正なデータを安全に処理できるようになります。
文字列から数字への変換にはいろいろな表現が可能です。例えば、16進数の文字列 "12A" を整数に変換することもできます:
let hexNumber = Int("12A", radix: 16)
この場合、radix
パラメータを使用して進数を指定します。もし無効な文字が含まれていると、やはり nil
が返されることになります。
同様に、2進数や8進数の場合も同様です:
let binaryNumber = Int("101", radix: 2)
let octalNumber = Int("17", radix: 8)
これらのコードも、無効な形式の場合には nil
を返します。
さらに進数の話を進めると、32進数、36進数といったものもサポートされており、対応する範囲内のアルファベットも変換に使用できます。ただし、仕様として36進数までしかサポートされていないという制限があります。
例えば、次のコードを使って32進数を変換することができます:
let base32Number = Int("1fa", radix: 32)
もし36以上の進数を使おうとすると、サポートされていないので変換は行われません。
最後に、古典的な暗号化の例として、文字のシフトを使った暗号化があります。これは、文字を一定の個数だけシフトして新しい文字列を生成するものです。例えば、次のコードは文字を2つシフトして暗号化します:
func caesarCipher(_ input: String, shift: Int) -> String {
let letters = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
return String(input.map {
if let index = letters.firstIndex(of: $0) {
let newIndex = (index + shift) % letters.count
return letters[newIndex]
}
return $0
})
}
let encrypted = caesarCipher("HELLO", shift: 2)
このように、基本的な文字変換から少し応用的な暗号化まで、オプショナルを活用することで、安全にかつ柔軟に対応できる場面が多くあります。今回はここまでとしますが、次回はさらに具体例を用いて進めていきましょう。 とりあえず、テキストをマップしてキャラクターを設定するところから始めますね。現状では何かが文字列に変わっていないようです。整数型になっていますね。なぜ整数型になっているのでしょうか。これは演算子の影響かもしれませんが、ユニコードの文字に変換する必要がありますよね。
どうしたらいいでしょうかね。例えば、キャラクターの x
に 1
を足すとどうなるか試してみましょう。しかし、この方法ではエラーになりますね。確かに、キャラクターに 1
を足すことはできません。代わりに、ユニコードのスカラー値に変換して足す方法があります。
例えば、スキーバリューをストリングのエクステンションで実装するコードが書かれています。こうすることで、ユニコードスカラーとして操作することが可能になります。ABC
のように文字列を操作すると、次の文字が B
や C
といった形でインプリメントできます。このような実装方法はなかなか面白いですね。
ただし、ユニコードスカラーの値はオプショナルになることが多いので、この点は注意が必要です。また、文字列を Int
型に変換する際も、スキーバリューを使うと意図した結果が得られることがあります。
しかし、空白やアンダースコアのような文字を処理するのは少々難しいです。たとえば、Base64でエンコードするときには、その中に特定の記号も含まれますが、純粋に一対一の文字変換ではありません。ですので、特定のルールに従った処理が求められます。
話が変わりますが、Base64エンコードもファンデーションに組み込まれており、データのエンコーディングとして時折使われます。NSデータを用いると、Base64形式でのエンコードが可能になりますが、これは暗号化というよりは文字列変換処理の一部として捉えられます。
さて、モビルスから数値に変換するという話も出ていました。これもオプショナルな Int
型として扱われることになります。前回の勉強会で触れたとおり、オプショナルを使うことで、変換に失敗する可能性を含む値を安全に扱うことができるのです。
では、次のスライドに進んでみましょう。前述のように、オプショナルな Int
を使って、扱えない場合を除外する方法などを確認していきます。イントの値が存在するかもしれないし、存在しないかもしれない、というオプショナルの概念を再度確認することが重要です。 なので、イント以外のプールの値やストリングの値などは含まれない、というのが一つ大きな特徴です。これはわざわざ挙げるほどではないかもしれませんが、さりげなく大事な特徴です。
ここでイントのカテナ(連鎖)について見ていきます。プレイグラウンドで確認します。ここで「イントのカテナ」として扱われていますが、これは便宜上の書き方で、正式にはオプショナル(Optional)として「イント(Int)」のように、パラメーターを伴うジェネリックな形として提示されています。どちらの書き方でも問題はありませんが、一般的には「イントのカテナ」の表記が多いかもしれません。レビューにおいても、「オプショナルイント」と書いたら直されるかどうかはプロジェクトによると思いますが、大抵は揃えて書いた方が良いでしょう。
次に、オプショナルが複数になった場合についてです。例えば、4行目と5行目のような書き方をするのか、または6行目のような書き方をするのか、意見が分かれるところです。配列の場合や型エイリアスの使用についても考慮する必要があります。ある程度の量になってくると、型エイリアスを使いたくなるという意見もあります。
例えば、トライクエスチョン(try?
)でオプショナルのオプショナル(二重オプショナル)になるケースもあります。こういうときはオプショナルが必要です。例えば、if let value = try? someFunction()
のように書くケースがありますね。
ちなみに皆さんはオプショナルを何段階までネストさせたことがありますか?自分は2段階くらいまでかなという気がしますが、さっきのtry?
のような都合で、どうしてもネストしてしまうことがあります。早々にコンパクト化して解消することが多いですよね。
こうしたオプショナルの使い方によって、多段にすることが可能だという話もありますが、どのように生かすかは難しい問題です。オプショナルが多段になっても、もう少し概念的に広がりを見せるかもしれませんが、現実的に表現できるのは「あるかないか」の話にとどまるでしょう。
このように、オプショナルの扱い方や使い方について考えていくと新しい発見があるかもしれませんね。想像力を働かせて、より良いコードを書いていきましょう。 無駄な使い方をすると「Brainf*ck」のようなものを作るかもしれませんね。そういった言語は、あえて複雑にしており、作る人が思いもよらないところで難しくなります。しかし、それを通じて試練を乗り越えれば、人間の知能をレベルアップするような何かが得られるかもしれません。ちょっとわかりませんが、研究する価値はありますね。
「Brainf*ck」というプログラミング言語の一つがあります。その言語は、意図的にシンプルかつ難解に設計されており、一般的なプログラミング言語よりも複雑です。ただ、論理的に作られているため、チューニングや調整をすれば何かしらのプログラムを作ることは可能です。
プログラミング言語「Swift」にも多くの興味深い要素があります。例えば、「Optional」という機能があります。Optional
は、値があるかもしれないし、ないかもしれないという状態を表現します。これは、enum
型として定義されており、値を持つ場合と持たない場合の二択です。このため、Optional
のメモリ空間は、そのための追加のフラグも必要になります。
Optional
のサイズとメモリレイアウトは、値が存在する場合としない場合で異なります。例えば、Void
型のOptional
の場合、値が存在するかどうかを示すために1バイトのフラグが必要になります。これにより、Optional
にするたびにメモリが増えることになります。このような挙動は非常に興味深いです。
Optional
かどうかの状態は、コードにおいて非常に重要です。例えば、次のコードです。
if let v = optionalValue {
// vがnilではない場合の処理
}
ここでは、optionalValue
がnil
であるかどうかをチェックし、nil
でない場合に実行する処理を記述しています。このように、Optional
の状態をチェックするためのフラグが必要になります。
このように、Optional
の扱いはプログラミングにおいて重要な要素の一つです。興味のある方は、このような言語仕様やメモリ管理についてさらに深く学ぶと面白いかもしれません。 ありがとうございます。ここからは、Swiftの言語仕様に関する勉強会の内容に焦点を当てていきます。
まず、UInt8
に関してですが、これは8ビットの符号なし整数を表します。ここで「64倍」という話が出ていますが、これはUInt8
のセルフでのサイズのことかもしれません。ゼロやヌル(null)の取り扱いも重要なポイントです。これがニル(nil)だとプログラムはどのように挙動するかを確認しています。Swiftでは、オプショナル型を使うことで、値が存在するかしないかを明示的に表現できます。
次に、クラスとオブジェクトに関する話題です。クラスのオブジェクトのメモリレイアウト、オブジェクトのサイズ、オプショナルのサイズについて詳しく説明がありました。Swiftでは、オブジェクトの参照をオプショナル型にしても8バイトのままなので、ヌルポインター(null pointer)と同じ容量です。ここで、オブジェクトの参照がオプショナル型になっている場合、メモリ内でどのように管理されているのかを説明しています。
たとえば、let someObject: SomeClass? = nil
のようにすることで、一部のメモリがヌルポインターに設定されるのですが、参照写真が出ているだけで参照先が選択した部分まで行くだけのような挙動になります。ヌルポインターの場合はゼロポインターとも呼ばれます。メモリダンプを使ってポインターの中身がどう変わっているかを確認することもできます。
このように、Swiftではオプショナル型のメモリ管理も非常に効率的に行われていることがわかります。
それでは、今日の勉強会はこれで終了です。次回は、オプショナルバインディングやニル(nil)の具体的な使い方について詳しく見ていこうと思います。お疲れ様でした。