https://youtu.be/JTDNyF3qyaI
このところ オプショナルバインディング
の話題から少し脱線していろいろと細やかなところを眺めていましたけれど、前回の後半からようやく The Swift Programming Language の本流に戻って、改めて オプショナルバインディング
の基礎的なところを眺めていきます。既に話し終えたことも登場してくる見込みですけれど、ここ最近で話していたことの総浚い的な機会として活用してみてくださいね。
それと今回は、ゆめみ社外の人への一般公募はなかったようなので、基本的に社内メンバーのみでの開催になりそうです。どうぞよろしくお願いしますね。
————————————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #156 00:00 開始 01:15 オプショナルバインディングの使用例 02:18 失敗可能イニシャライザーとオプショナルバインディング 03:12 オプショナルバインディングを使わない場合の例 04:52 オプショナルバインディングを使ったコードの安全性 05:40 失敗可能イニシャライザーの恩恵 06:36 変換失敗を考慮した条件処理 07:35 オプショナル型から通常の型に振り替える 09:28 スコープによる安全性 10:50 コード例の読み方 12:31 勉強会の発表と人柄のお話(余談) 15:06 勉強会を開くの、おすすめ(雑談) 19:39 コード例の説明 20:44 変換イニシャライザー 21:37 値を保全する型変換 23:28 狭義の型変換 24:03 値を保全する型変換の具体例 27:36 StringProtocol な型を受け取る 29:05 LosslessStringConvertible の要求との兼ね合い 30:27 Substring 型による最適化 33:58 String と Substring を区別なく扱う方法 35:36 クロージングと次回の展望 —————————————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #156
では、始めていきましょう。今日は前回の後半から引き続き、オプショナルバインディングの基本的なところを学んでいきます。Swift言語の公式ガイド「The Swift Programming Language」に戻って、再び基本を確認していく形になりますが、もう少しこの内容は続くようです。われわれとしては、ほぼおさらいになると思います。直近のおさらいとして聞いてもらえれば、より記憶に定着しやすくなるでしょう。
では、さっそく見ていきましょう。これはオプショナルバインディングの使用例です。前回の最後に見た資料で、残り1分くらいで触れた部分からですね。以前に出てきたコードを書き換えたものですが、具体的なコードを思い出しつつ見ていきましょう。
if let actualNumber = Int(possibleNumber)
この例は、possibleNumber
という文字列を整数に変換する試みをしています。この変換が成功すれば、その値をactualNumber
に取り出し、失敗した場合はnilになるというものです。これをオプショナルバインディングを使用して書き換えることで、成功した際にはその値を表示し、失敗した場合には失敗を知らせるといったコードに変換されています。
実際に、これは以前に出てきたコード、強制アンラップを使ったコードです。以下のように書かれていましたね:
let possibleNumber = "123"
let actualNumber = Int(possibleNumber)!
このコードは、possibleNumber
が変換できる文字列の場合にのみ動作しますが、失敗した場合にはクラッシュしてしまいます。
一方、オプショナルバインディングを使えば以下のように書き換え可能です:
if let actualNumber = Int(possibleNumber) {
print("The number is \\(actualNumber)")
} else {
print("Failed to convert the string to an integer.")
}
このように、オプショナルバインディングを使うことで、安全に値を取り出し、エラーハンドリングを行うことができます。
もしオプショナルバインディングという概念がなかったら、次のようにnil判定を行って処理しなければならなくなります:
let possibleNumber: String? = "123"
if possibleNumber != nil {
let actualNumber = Int(possibleNumber!)
print("The number is \\(actualNumber!)")
} else {
print("Failed to convert the string to an integer.")
}
このようなコードは、古い高級言語では当たり前の書き方でしたが、現代的なSwiftではオプショナルバインディングを使ってもっと簡潔に、親しみやすく書けるようになっています。以上が、オプショナルバインディングの基本的な使い方の説明になります。次回はさらに進んで、より高度な使用例や考え方について見ていきましょう。 アンラップという考え方ですが、暗黙的なアンラップではないものでしたね。こちらはオプショナルバインディングを使って安全にコードを書いている例です。このコードは非常に見やすく、さまざまな面で安全なコードを書けています。
では、どのような点で安全が図られているか具体的に見ていきましょう。もちろん、自分が思い浮かぶところしか話せませんので、他にも安全が確保されているポイントがあれば教えてくださいね。
まず、失敗可能イニシャライザー(failable initializer)についてです。このイニシャライザーのおかげで、与えられたパラメーターが意図した値であるかを確認できます。失敗可能イニシャライザーがないと、互換性のないパラメーターが渡された際に強制終了せざるを得ないか、不正な値のまま進行してしまうかという分岐点になってしまいます。しかし、失敗可能イニシャライザーがあることで、確実な値が取れることが約束されます。
次に、失敗可能イニシャライザーと型変換ですが、パラメーターには String
型の引数を取ることが明示的に示されています。他の型は受け付けないため、安全です。また、失敗可能イニシャライザーの結果に nil
が混入する可能性があるため、if
文を使ってそのチェックを行うことができます。if
文によって、nil
だったときには制御が else
ブロックに移され、値があったときにはその値をローカル変数に格納します。この際の変数はオプショナル型ではなくなるため、安全です。
安全性の点では、if
文の中でオプショナル型ではない値として扱えることが挙げられます。これにより、コードの明瞭性が高まり、nil
の可能性を再度チェックする必要がなくなります。重複チェックを省けることでパフォーマンスの向上も図れますし、コードの明瞭化にもつながります。
また、if
文が成功したときにのみ使えるローカルなプロパティーとして定数が存在するため、失敗した場合にはその変数は else
ブロックで使えません。これも安全性の一つです。値がない状態でその変数が使われることがないという点で、安全が確保されています。
他に、このコードで安全性が図られている点を見つけた方がいれば教えてくださいね。大体これくらいかなと思いますが、もし他にもポイントがあればぜひ教えていただきたいです。以上がオプショナルバインディングを使った例の解説でした。他にも話したいことがあれば、割り込んで構いませんので意見をいただければと思います。 下の説明にあったね、このコードは Int
を返す関数で、possibleNumber
がオプショナルな Int
です。もし値を持つ場合、その値を actualNumber
という変数に代入します。こういうふうにコードの読み方を解説するのは、個人的にはとても好みです。
通常、コードを自然に読める人は、慣れてくるとスムーズに理解できるものです。例えば、if let actualNumber = possibleNumber
のような構文です。それでも、コードにまだ慣れていない人にとっては、理解するのが難しいこともあります。こういった読み方の指針が示されていると、何かのきっかけでコードに親しみやすくなる可能性が高くなります。こういう気遣いができるのは素晴らしいと感じます。
教えるのはなかなか難しいですが、人によって気づくことや、得意かどうかが異なります。経験や感性など、様々な要素が影響するのだと思います。そのため、こういった指導を見て「いいな」と感じることがあります。各個人の持ち味が表れるからこそ、同じテーマで発表をしても、その視点や解釈が異なるため、全く無意味ということはありません。
例えば、勉強会で同じ題材で発表しても、それぞれの視点が違うことで新しい発見が生まれます。同じテーマでも、理解の助けになることが多いので、自信を持って発表してほしいと思います。一度聞いてわかる人もいますが、複数の視点から同じテーマを学ぶことでより理解が深まることがあります。
このように、勉強会で話す機会があったら、ぜひ自分の視点で話してみてください。みんなで学び合うことで、新しい気づきや理解が生まれます。
勉強会の運営は、その人柄によって雰囲気が変わることもあります。進行役の特徴によって参加者の反応や雰囲気も変わりますが、それもまた勉強会の醍醐味です。いろいろ試してみて、新しいアプローチで学べる場を作っていきましょう。
例えば、「誰々さんの優しいSwift勉強会」といったように、テーマに沿った勉強会を開催するのも面白いと思います。Swiftのプログラミング言語に関する本を読んで自由に話すだけでも、良い勉強会になります。必要なのは熱意と柔軟な思考です。好きな時に、好きな形で開催してみてください。きっと面白い発見がありますよ。 なので、興味があったらぜひやってみてください。特に初心者の方にはとてもおすすめしたい内容です。話しているように聞こえるかもしれないですが、毎回勉強するところがあり、しかも記憶に強く残るんですよ。今までは普通にコードを書いて勉強してきましたが、それとは全然違うんです。昔も自由に書けていましたが、今も変わりません。ただ、その時の発想が違います。本に載っていたり、誰かが言っていたことや、その根拠が色々な面で思い浮かびます。コードを書いていると、それが楽しく感じられるんです。コードを書くこと自体が楽しくなる効果もあって、非常におすすめです。
聞く勉強会も良いですが、自分で話す勉強会が一番学びになります。わからないことをその場で質問できるので、発表者としても美味しい経験ができます。実際の業務とは異なるレベルでの質問が出てくるので、非常に有益です。勉強会を開催するのは大変かもしれませんが、社内であればハードルが下がりますので、いい機会だと思います。積極的に勉強会を開催することで、周りも幸せになり、良いことがあると思います。
さて、少し脱線しすぎましたが、次に進みましょう。
さっきの解説は、オプショナルバインディングに成功すると、if
文の最初の分岐の中で実際の値が定数として使えるようになる、という話でしたね。ここでは、オプショナルから値が取り出せているため、強制アンラップ(「!」)を使う必要がないということです。この例では、変換結果を表示する際に値がそのまま使われていて、強制アンラップがされていないわけです。特に難しいところはありません。
一応補足しておくと、ここでの「変換」はinit?
を使って、特定の型から目的の型に変換するイニシャライザーのことです。ここでは「失敗可能イニシャライザー」が用いられており、Int(init?(str))
のように、文字列から整数への変換を試みています。このように、与えられた値と同じ重みの値を変換先に用意するということがAPIデザインガイドラインに規定されています。
基本的に、パラメータのラベルを省略するスタイルのイニシャライザーを使うと、変換イニシャライザーとしての意味合いを持つことになります。これによって、例えば文字列として渡された数字が、そのまま整数として保持されるなど、重みの同じ値が得られるようになっています。この点については、コンパイラの介入ができないため、プログラムを設計する人の力量が問われます。このコードが意図したとおりに動作するかどうかの安全性を考慮する際に重要なポイントとなります。 ラベル付きのイニシャライザについて説明します。このイニシャライザは、インスタンスを生成するための通常のイニシャライザである場合もありますし、渡された値を変換して返すイニシャライザ(ナロータイプコンバージョン)である場合もあります。この違いはAPIの設計に反映されています。
例えば、バリオプリザービングタイプコンバージョンでは、文字列として 「123」 があり、その値を Int
に変換するとします。このような変換では、変換元と変換先が異なるため、表現できる範囲が異なります。Int
型はその表現範囲が限られており、すべての文字列が変換可能ではありません。そのため、これには失敗可能なイニシャライザが使われます。
もう少し複雑な変換が必要な場合や、変換に失敗したときの回復手段を提供したい場合は、エラーを返すイニシャライザを使うこともできます。失敗可能なイニシャライザを使用することで、変換が失敗したときに適切にエラーハンドリングが行えるようになります。
次に、特定の基数に沿った変換を行うイニシャライザについて見ていきます。例えば、16進数に変換するイニシャライザを持つ場合、「a」を含む文字列(例えば "1a")は、16進数として解釈されます。これも一種のオーバーロードであり、異なる引数を取る複数のイニシャライザが存在することになります。
具体的な例として、次のようなコードを考えます。
let hexadecimalValue = Int("1a", radix: 16)
ここで第2引数は、文字列が16進数として解釈されるように基数を指定するものです。この場合、第1引数だけに注目し、それを省略することでAPIデザインが一貫して簡潔になります。こうしたことを考慮することで、APIを設計する際や定義する際に効率よく、かつ直感的にコードを書けるようになります。
また、この「Intのイニシャライザ」のうち、StringProtocol
に準拠した型(String
やSubstring
)を受け取るイニシャライザもあります。このStringProtocol
はSwiftの標準ライブラリによって提供されており、通常は独自に使用することは推奨されていません。このプロトコルに準拠した型を受け取ることで、文字列や部分文字列の両方を統一的に処理できます。
例えば次のようなコードが考えられます。
let value = Int("123") // これはロスレスストリングコンバーティブルに基づく
このように、イニシャライザをどのように設計し、どのプロトコルを適用するかを考えることで、より柔軟で再利用可能なコードを作成することができます。 パラメーターとしてラベルなしのディスクリプションを取っているということは、あるインスタンスのディスクリプションから再変換で元に戻せるという、そういったイニシャライザであるということが想像できます。それが規定されているのがLosslessStringConvertible
というプロトコルなので、それを狙ったものでしょう。これがなくても、そのプロトコルに縛られていなければ、普通に「3行目の2行目のイニシャライザ」で書けるわけですけど、それでもいいわけです。しかし、あえてこのプロトコルを利用することで、便利な機能が予め用意されています。
たとえば、「let intValue = Int.init("123")
」のようにして文字列のサブ文字列を使うと、ストリングの開始インデックスから終了インデックスまでを指定して取り出すことができます。これは自動的にオーバーロードされたメソッドが呼ばれるため、便利ですね。
ここで重要なのは、SwiftではString
型とSubstring
型が異なることです。Substring
型は元のString
を複製して作成するわけではなく、元のString
のメモリを参照しています。そのため、パフォーマンスやメモリ効率が向上します。Substring
は特にテキスト解析などで有効です。このように、サブストリングを活用し、最終的にリターンする際にString
に変換して返すなど、パフォーマンスの向上を図ることが重要です。
例えば、文字列から何かを得る関数を作る場合を考えましょう。以下のコードのように関数を定義するとします。
func countLetters(inString string: String) -> Int {
return string.count
}
この関数はString
型の引数を取ってカウントを行いますが、Substring
では使えません。適切なAPIがないため、String
に変換して使おうとすると、メモリの複製が発生し、パフォーマンスが悪化します。このような場合、StringProtocol
を利用することで、String
もSubstring
も受け付けるAPIを作ることができます。
今日の勉強会はここまでにしておきましょう。次回はオプショナルバインディングについて引き続き学んでいきます。それでは、お疲れ様でした。ありがとうございました。