本日も前回の寄り道の続きで、自分の中でとても興味の湧いた技術ブログ「その Swift コード、こう書き換えてみないか」を眺めていこうと思います。前回にて特に興味の持った箇所を見終えましたけれど、せっかくの精細に見渡されている印象を持つブログですので、今回から何日かをかけてその冒頭から順を追って眺めていってみることにしますね。よろしくお願いします。
—————————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #255
00:00 開始 00:56 今回の展望 01:49 書き換えの提案ブログ 02:36 その判断に至る経過が大切 04:10 真偽値の反転 05:30 値の取り違いを予防できる 06:23 副作用を伴うものと伴わないもののセット 08:21 メソッドチェーン的な呼び出し方も便利には感じる 11:26 toggle の印象と、変数に対する価値観 12:02 変数と定数 13:27 toggle に対する所感 14:10 剰余演算子ではなく isMultiple(of:) 演算子を使う 14:42 それが何をしているか、読解が必要 16:36 isMultiple(of:) を用いた奇数判定 17:39 なぜ isMultiple(of:) を使うのが良いか 18:01 最下位ビットの値を調べての偶数判定 19:35 isMultiple(of:) は英文のように読める 21:49 0はすべての倍数 22:25 24:04 機能として備えて、意味を添えることも可能 26:01 ChatGPT の回答間違い 26:53 浮動小数点数の丸め込み 29:17 整数の非対称性 29:54 Int.min の絶対値が Int.max を超える 31:03 クロージングと次回の展望 ——————————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #255
では、始めていきますね。今日は、最近話題になっている「Swiftのコードを書き換えてみないか」というブログについて話します。あなかりょうかさんが書いたブログで、前回も見ていました。
前回は、計算量でカウントとエンドインデックスについて話しました。でも、それは前回話したので、またその話が出てきたら追加で話します。今日は、せっかくなのでこの詳細に考察されたブログを見ていきます。とても繊細な視点で、いろいろなところに注意を払って書かれているブログです。ですので、上から順に見ていこうと思います。
ただ、この勉強会で取り上げるので、全体をサクサクと見るのではなく、いつものようにゆっくりと、脱線もしながら見ていきます。何日かかかるかもしれませんが、それくらいのイメージでいます。
まず、タイトルから見ていきましょう。このブログの提案は、Swiftのあるコードをこう書き換えると良いというスタンスが基本です。ただし、そうでないときもあります。最近公開されたブログで、勉強会で話された原稿だそうです。
この原稿は精密に書かれている感じですが、とりあえず見ていけばわかるでしょう。ある実装を取る際に自分ならどう意識して書くのかをまとめたものが多いです。どういう意識で書いているのかは外から伺う機会が少ないので、このブログはその点でとても重要です。
ここに書かれていることが絶対に正しいわけではありません。その方がどういう意識で書いているのかを理解して、自分ならどう思うかを重視してほしいと思います。賛同するなら賛同で、違うと思ったらそれも正しい意見です。意見を共有すると、みんなにとって良い勉強になります。
次に、最近話題になった「ブールを判定する方法」について見てみましょう。ここでは、よくある古いスタイルの書き方について話されています。
// オールドスタイルのコード
if flag == true {
flag = false
} else {
flag = true
}
これを toggle
で書くと便利だよという話ですね。
// 新しいスタイルのコード
flag.toggle()
まず、フラグが真のとき、その反転のものを代入して逆転させるというもので、古いスタイルのコードは読みやすくないし、間違う可能性もあります。でも、toggle
を使えば大丈夫だということです。
このブログの実装も見てみましょう。自分自身の空間の中では、セルフを反転させるだけですごくスマートなコードになります。toggle
というメソッドを使うと、簡単になります。
// `toggle` を使ったコード
flag.toggle()
このように、結果だけを返すメソッドが提供されることが多いです。Swiftガイドラインにも書かれていますが、同じ機能は基本的には一つだけにするのが原則です。ですので、toggle
と !flag
のどちらか一つが使われることになり、toggle
はセルフの書き換えに使われるのです。
このように、Swiftのコードをより効率的に、シンプルにするための方法がいろいろと紹介されています。今後もこうしたブログの内容を見ながら、Swiftの理解を深めていきましょう。それでは、続きを見ていきます。 なので、これを残すとして、まず一つ、どういった理由でトグルがないかというのもありますし、今回このコードで見せてもらったややこしさを回避するシンプルな手法としてトグルが提供されていると考えれば、トグルは意味のある存在だと思います。ただの名前がついただけではないですね。
個人的には後ろの方が好きなんですが、これを反転させたいなと思ったときにどうでしょうか。思いつきで例を挙げると、例えば、トラブル
と何にしようかな、例えば、アイテム
のビジブル
状態を表すときに var item
と変数があったとして、ビジブル状態
を判定するときに item.isVisible
が false
のとき、こういった順番で項目の可視状態を確認するんですね。ビジュアル状態が反転されたい場合に、item.toggleVisibility()
のようにするのがスッキリしていて分かりやすいです。
ところが、Swiftでは変数を var
で宣言し、定数を let
で宣言するというのが基本です。この変数 var
の立ち位置も、SwiftUIが登場してから変わってきた感がありますね。定数 let
と変数 var
のインスタンスは状態を表していて、状態変化が起こる場合には変数 var
が使われます。例えば、item.visibility
が変化する場合などです。
このミュータブルな関数(ミュータブルミューティングファンクション)が存在すると、var
の活躍が以前よりも鮮明に現れます。これが新しいメソッドを使った新たな表現として見ることができますね。
Swiftのコードでは自己を指す self
というキーワードも面白い表現だと思います。この自己参照の理解は非常に重要で、多くの場面で役立ちます。
次に進みましょう。たとえば、x % 2 == 0
ではなく、x.isMultiple(of: 2)
を使うといった新しめのメソッドを使うのも良いですね。これにより、コードがより読みやすくなります。昔ながらの x % 2 == 0
という書き方もまだまだ使われていますが、新しいメソッドの方が直感的です。
x % 2 == 0
は x
が2で割り切れるかどうかを判定するための記述ですが、x.isMultiple(of: 2)
の方が意味が直感的に理解できると思います。この書き方により、他の倍数の判定も簡単にできます。
たとえば、x.isMultiple(of: 3)
や x.isMultiple(of: 4)
のように任意の倍数に対しても同じメソッドを使うことができます。これは非常に有用な機能です。
もちろん、ここで奇数の判定についても触れることができます。奇数の判定を同様にシンプルに表現できる方法があれば、それを採用したいものです。
ここまでいろいろな表現やメソッドについて話してきましたが、これでSwiftの言語仕様に対する理解が深まると思います。次回の勉強会でもさらに具体的なコード例を交えつつ、Swiftの魅力に迫りたいと思います。 Swiftのプログラムを書く際に、数の倍数判定や奇数・偶数の判定をすることがしばしばあります。この場面で、具体例を上げながら説明してみましょう。
まず、x.isMultiple(of: 2)
というメソッドが追加されました。これは数x
が2の倍数であるかどうかを判定するメソッドです。この方法を使うと、x % 2 == 0
よりも直感的でわかりやすいコードを書くことができます。
なぜこの方法が優れているかというと、まずは読みやすさが挙げられます。x.isMultiple(of: 2)
は英文のように自然に読めます。そのため、プログラムを読む人にとって理解しやすいというメリットがあります。コードを直感的に理解できると、バグが減り、保守性が高まります。
また、ビット演算を使って奇数か偶数かを判定する方法もあります。例えば、n & 1
を使う方法です。2進数の仕組みを利用すると、偶数の場合は最下位ビットが0になり、奇数の場合は最下位ビットが1になります。この方法も、パフォーマンスが重要な場合には有用です。&
演算子は✂️た余り(%
)を使うよりも高速であることがあります。
さらに具体的に言うと、x.isMultiple(of: n)
の内部実装では、x.magnitude % n.magnitude == 0
という計算を行っているかもしれません。ここで magnitude
は絶対値を返すメソッドで、負の数による不正な判定を防ぐ役割を果たします。
ある数が0の倍数かどうかの判定についても考えてみます。任意の数を0で割ることはできませんが、0は自分自身の倍数であるとみなすことができます。ですから、x.isMultiple(of: 0)
とした場合、x
が0ならばそれは正しいです。
また、このことを考慮するために、isMultiple
メソッドは特別な処理を行っているかもしれません。例えば、if x == 0 { return true }
といった特例処理が行われている可能性があります。
まとめると、Swiftではx.isMultiple(of:)
を活用することで、より読みやすく、保守性の高いコードが書けます。一方で、パフォーマンスが重視される場面ではビット演算も有効です。どちらの方法も適材適所で使い分けることが重要です。 例えば、1.isMultiple(of: 2)
とか、2.isMultiple(of: 2)
とか、3.isMultiple(of: 2)
のように動かすことができます。ですが、これを分かりやすく、かつ安定するようにしたい場合、エイリアスをさらに追加することができます。例えば、エクステンションを使って以下のように実装できます。
extension BinaryInteger {
var isEven: Bool {
return isMultiple(of: 2)
}
var isOdd: Bool {
return !isEven
}
}
このように書くと、isEven
やisOdd
といったプロパティを使って、数が偶数や奇数かどうかを確認することができます。ただし、わざわざこれをやる価値があるかはちょっとわからないですが、それでも可能だということです。このような感じで、公式に提供されているisMultiple(of:)
メソッドを使えば、安心して使用できます。
そして、ここで少し話題が変わって、小数点の扱いに関してです。例えば、マイナスの浮動小数点数を丸めるときにいろいろな方法があります。例えば、-3.4
を丸めるときのラウンディングルールとして、「0から遠ざける」場合の結果は-4
になります。このように、-3.4
という値をlet value = -3.4
とし、これを異なるラウンディングルールで処理することができます。
ラウンディングルールには、以下のようなものがあります:
towardZero
awayFromZero
toNearestOrEven
toNearestOrAwayFromZero
up
down
例えば、value.rounded(.towardZero)
と書くと、値が0に向かって丸められます。この場合、-3.4
は-3
になります。このようにさまざまなルールがあり、それぞれが異なる結果をもたらします。
さらに、もう少し複雑な範囲の話をすると、整数型や浮動小数点数の正確性も影響します。Swiftの整数型は、例えばInt8
の場合、-128
から127
までの範囲を持っています。こうした制約も適切に管理しなければならないことが多いです。
とりあえず、今日の勉強会で出てきた話題はこのくらいにしておきます。いくつか補足する部分や前回の内容もありますので、次回に続けてやっていきましょう。今日はここまでです。お疲れ様でした。ありがとうございました。