https://youtu.be/ZMv4xf8CNOQ
今回から The Basics
の 論理値
について眺めていきます。とてもシンプルな機能になるので、見どころがどれくらいあるかはわからないですけれど、せっかくの機能ですのでじっくりと観察していこうと思います。どうぞよろしくお願いしますね。
——————————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #128
00:00 開始 00:15 Boolean の日本語訳について 00:45 真と偽 01:03 Boolean とは 02:32 Bool 型 03:06 サンプルコードの紹介の仕方で気になったところ 04:58 真偽リテラル 05:31 ときどき出会う周辺知識 06:38 算術演算と論理演算 07:52 真偽の表現方法 10:48 Bool のデータサイズ 11:15 Bool 型が採る実際の値 11:51 Bool を数値とは比較できない 13:31 MemoryLayout は 8 ビット未満を検出できない? 16:51 真偽値と条件式 18:10 条件式で代入演算子は使えない 19:10 代入演算子とオプショナル・バインディング 20:48 if とパターンマッチング 22:48 複数条件の書き方と、それを検証しようとしての勘違い 27:21 ここまでの勘違いに気づいて解説 28:01 いろいろな条件式の書き方 28:50 条件式では Bool しか評価式として扱えない 29:54 かつての BooleanType と boolValue 32:28 パフォーマンスの都合で BooleanType は廃止されたらしい ———————————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #128
では、始めていきましょうか。今日は論理値のお話に入ります。論理値は「ブーリアン」とも訳せますが、審議値という言葉も使えますね。また、値ではなく論理型といった言葉もありますが、とりあえずここでは全体的に見て「論理値」という訳を使うことにしました。
論理値、審議値はとても当たり前のもので、多くのプログラミング言語に存在します。True
やFalse
といった値を持ちますね。では、まずスライドを見ていきましょう。
論理値とは何かというお話ですが、これだけ見れば大体理解できるかと思います。例えばSwiftではブールという審議値型があります。これは真または偽、そのどちらかだけの値を取ります。そのため論理的ロジカルとも呼ばれます。なので、論理値といったお話はこういったところから来ている感じです。
ブーリアンという言葉が審議値型を意味するかについてですが、英語では一般的な単語として使われています。ロジカルという言葉は日本語でも外来語として使われますし、ロジカルという変数名を採用している言語もあるかもしれませんね。ちょっと具体的な言語は思い出せませんが。
ほとんどの言語にはBool
型が用意されており、True
とFalse
といった値を持ちます。特に問題ないかと思います。
次に、コードの例を見ていきましょう。このコードですが、ブール型の話をしているのに型推論させてしまっていいのかと感じますね。ブール型と明示的に書いた方が親切な気がします。変数名の中に"A"が入っているから既に注目されているのかもしれませんが、個人的にはBool
と書いてほしいと思います。
アップルのコード例では時々、かっこをつけたり、不要なものを入れたりすることがありますね。Bool
型があるというだけでなく、それが組み込みのキーワードになっています。例えばlet true = ...
といった形で定義されているわけではありません。あくまで組み込みの定数で、どこかに定義されているわけではないという感じです。
ちなみに、ターニップというのはヨーロッパ原産のカブのことだそうです。プリンターの例では、昔のプリンターには火災がつきものだったという面白いエピソードもあります。火災発生中などのステータスを示すものがあったんですね。こういった知識も時として役に立つことがあります。
以上です。それでは、次に進んでいきましょう。 ブール型の話に戻りましょう。ブール型は様々な場面で使われます。例えば、論理演算やフラグとして使われますね。真偽値は演算と密接に関係しており、特に論理演算とビット演算に分類されます。これにより、見方が少し変わるかもしれませんね。まずは、真偽値とは何かについて見ていきましょうか。
TRUEとFALSEという基本的なものを見てみます。例えば、C言語の場合、TRUEは何か数値で表現します。具体的には、0がFALSE、それ以外の数値がTRUEとして扱われることが多いです。ちょっと前の言語では、TRUEを-1として表現するものもありました。そういった言語では、int型で真偽値を捉えるのが一般的でした。
しかし、Swiftでは、bool型という独立した型が用意されています。これはC++と同じように、独立した型として定義されています。そのため、例えば、bool型の変数にint型の値を渡すことはできません。具体例として、以下のような形になります。
let value: Bool = true
もし、bool型がint型と同じように扱われていたなら、意図しない動作が発生する可能性があります。しかし、Swiftではそれが防がれています。
あと、ブール型のサイズについても見ておきましょうか。Swiftでは、ブール型は1バイトで渡されます。そのため、64ビットの値を丸々取るわけではないので、比較的軽量です。ただし、1ビットしか使っていないため、7ビットが無駄になっているとも言えます。
また、TRUEやFALSEが実際にどのような値として格納されているかも確認しておきましょうか。例えば、以下のようにキャストしてみます。
import Foundation
let trueValue: Bool = true
let intRepresentation = trueValue as Int8
print(intRepresentation) // 出力: 1
このように、TRUEは1、FALSEは0として格納されています。しかし、これをそのままif文で条件式として使うことはできません。あくまで評価式の結果としてブール型が必要です。これにより、意図しない評価式を防ぐことができます。例えば、以下のような形です。
let condition: Bool = (someValue > 0)
if condition {
// 条件がtrueの場合に実行される
}
最近のプログラミング言語では、こうした扱いが一般的です。意図しない評価式が入り込むのを防ぐために、明示的にブール型を求めるという設計は、使い勝手の面で厳しいかもしれませんが、安全性を高めることができます。
以上を踏まえた上で、コメントも拾っていきましょう。メモリレイアウトや、戻り値について考えると興味深い点が多いので、これも見て行く価値はありますね。
なるほど、面白いですね。 メモリレイアウトサイズはバイト単位で返すという意味ですね。ですので、例えば「0.何バイト」というような判定はできず、どうしても1バイト単位で判定する必要があるのは確かにその通りです。それを確かめるにはどうしたらいいでしょうか。構造体でBoolを複数まとめたものにする…いや、構造体じゃなくていいので、メモリレイアウトを活用してみるとよいかもしれません。例えば、Boolが二つある場合のサイズを見たときに、それが1バイトになるか2バイトになるかを確認することができます。
実際に試してみた結果、2バイトとなったので、これはメモリの配置の関係でパディングが取られてしまっている可能性があります。もう少し詳しく確認するために、次に8個のBoolを並べてみましょう。8個並べても特に意味がないかもしれませんが、とりあえず試してみます。
この結果を見て、やはりバイト単位でしか返されないというところを確認できます。メモリレイアウトはバイトサイズで返し、そのため常にバイトサイズでの結果となります。最適化の関連については内部処理の詳細を知っているともう少し分かりやすくなるかもしれませんが、このあたりの詳細はコンパイラの最適化に依存する部分もありそうです。
さて、次に「if」文の話を進めていきます。「if」文では必ず真偽値を取る必要があります。オブジェクトを取得する関数があり、その関数はオブジェクトを返す場合とnilを返す場合があります。このようなケースでは、let
でオブジェクトを取得し、「if」文内でそのオブジェクトがnilでない場合に処理を行うといったコードがよく使われます。
if let object = getObject() {
// オブジェクトが取得できた場合の処理
}
C言語の場合、代入演算子が自分自身を返す特性があり、以下のようなコードがよく見られます。
if (object = getObject()) {
// オブジェクトが取得できた場合の処理
}
Swiftではこのような代入演算子を使うべきでなく、必ず明確な評価式を使うべきです。代入と評価を区別するためにSwiftではオプショナルバインディングを使用します。これにより、評価式が常に真偽値として評価されることが保証されます。例えば、オプショナルの値を取得した場合、その値を変数に取り出して使用することができます。
以上のことを踏まえ、Swiftでの「if」文の使い方やメモリレイアウトの理解を深めることができます。また、詳細な部分や疑問点については随時議論しながら進めていきましょう。 ちょっと話が逸れましたが、全体像を捉えほんの少しの変更でも、理解を深めるためには重要です。Swiftのif-let
文は、オプショナルな値をアンラップするための特別な構文です。もう一つ、if-case
やif-case-let
もあります。これらは、特定のパターンに一致する値を扱うための構文です。
例えば、26行目を少し変更して、オブジェクトから値を取得できた場合の処理に使います。通常のif-let
とは異なる書き方が必要になるため、分かりにくい部分があります。この場合、ifの条件に対してパターンマッチングを適用します。パターンマッチングは、より複雑な条件判定を可能にします。
具体例を示しましょう。例えば、オブジェクトがrandom
というBool型のプロパティを持っていて、そのプロパティがtrue
の場合には特定の処理を行いたいとします。このとき、where
句を使ってその条件を定義できます。
if let object = someObject, object.random {
// object.randomがtrueのときの処理
}
また、別の例では、オブジェクトのrandom
プロパティが0のときの処理を次のように書くことができます。
if let object = someObject, object.random == 0 {
// object.randomが0のときの処理
}
コードが少し複雑になる場合や、条件が多くなる場合にどのように書くかが重要です。コードの可読性とデバッグのしやすさを考慮する必要があります。例えば、次のコードは実行結果に関与する条件分岐を明確にします。
if let object = someObject {
if object.random {
print("True case")
} else {
print("False case")
}
} else {
print("Optional value is nil")
}
コードが適切に動作しているか確認するためには、デバッグと検証が重要です。この具体例では、条件分岐が期待通りに動作するかどうかを確かめることがポイントです。時には、オブジェクトのスコープや型を見直すことが必要になります。
最終的には、スコープと変数の定義が適切であることを確認し、コードが意図した通りに機能するかを確かめることが重要です。例えば、次のようなコードではスコープの確認を行います。
if let object = someObject {
// objectがnilでなければこのブロックが実行
print(object)
} else {
print("Optional value is nil")
}
これが正しく動作しない場合は、変数のスコープが適切か、初期化されたタイミングが正しいかを確認する必要があります。もし同じ名前の変数が異なるスコープで定義されていて混乱が生じる場合、変数名を変更するかスコープを見直すとよいです。 なので、これを定義をたどると、オブジェクトのrandom
を例えばtrue
に強制的に設定して、それから他の場所でこのオブジェクトのrandom
を見る方法を考えてみましょう。ですが、こっち側は見れないですね。ああ、でもこっち行くことはないんですよね。ああ、行ったよね。うん、だからいいんだ。こっちのオブジェクトはtrue
にして...そうか、全く関係ない部分を重ねて考えていたんですね。
そうです。このオブジェクトは条件式に何も関与していなかったのに、そのオブジェクトでどうにかしようと見てしまったせいですね。別の独立したオブジェクトに対して条件判定を行ったので、確認した内容が無意味だったのです。
このように、条件式が29行目か32行目かどちらが動くかが変わっていた場合、パターンマッチングの条件を満たして得られた変数がどういう状態であるかを判定する必要があります。複雑なパターンマッチングというものもあるし、値を取れたかどうかが重要です。取れた場合にはその値を使うこともあるし、if
オブジェクトと得られたオブジェクトが一致しているかといった単純な評価パターンもあります。
具体的には、純粋なif
文(例えば28行目のような)の評価式には、ブール型の値しか使えないという仕様があります。この評価式が論理的なものであるか、値を取るものであるか、パターンマッチングであるかは関係なく、その仕様に従う必要があります。
昔は、BooleanType
というプロトコルがありました。例えば、オブジェクトがそのもので真偽を表すものであることをプロトコルで規定すると、オブジェクトを直接if
文に書くことができました。BooleanType
に準拠して、boolValue
としてオブジェクトの状態が真か偽かを示す方法があったのですが、今はそれができなくなっています。そのため、評価式として確実にブール型になければならないのです。
例えば、C言語のブールのように0
を入れるといったこともできますが、その際にはif value
の判定がエラーになることがあります。現在はBooleanType
プロトコルがないので、評価式においてより確実にブール型を使う必要があります。
時間になりましたので、今日はこれで終了にします。次回もさらに詳細な内容について掘り下げていきたいと思います。お疲れ様でした。ありがとうございました。