https://youtu.be/jPrdel9BQN8
前回は オリエンテーション
的なこの勉強会の振り返りで幕を閉じましたので、今日は改めて The Basics
の話題に戻って、変数宣言におけるルール
の残りのところと 変数や定数の書換可能性
について見ていきますね。よろしくお願いします。
————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #98
00:00 開始 00:47 予約語と同じ名前を変数で使う 05:24 特殊記号は変数名に使える? 09:19 雑談:顔文字に見える 09:33 捨てるはずの引数が使えている? 10:09 引数ラベルの省略表記を明記する 12:00 引数ラベルの省略を明記する場面 15:16 引数の名前を _ にしたときの挙動 19:04 列挙子に予約語を使う 22:06 記号は変数名に使わないのが良さそう 24:26 名前にキーワードを使うことは避ける 25:24 標準ライブラリーで .default は使われている? 26:26 default とシングルトン 27:51 UIDevice と current 28:31 予約語を避けることよりも、名前の意味を考える 30:27 既定値と標準 31:39 UserDefaults という名前のわかりにくさ 33:18 Date.FormatStyle.TimeStyle 34:57 単位元、恒等写像 36:23 zero と AdditiveArithmetic 39:19 次回の展望 —————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #98
はい、じゃあ始めていきますね。前回はオリエンテーションの回で、スライドには入れなかったので、お久しぶりな感じがしますけれども、今日は改めて、引き続き変数と変数の名前付けについてお話ししていこうと思います。名前付けって少し大げさだったので、予約語と同じ名前をSwiftで付ける方法について説明します。この勉強会でも以前盛り上がりましたが、その話を振り返りつつ思い出していこうかなと思います。
まず、具体的にどういったところかというと、backtick
という記号を使って行うものです。プレイグラウンドで簡単に紹介すると、例えば普通の変数はこう書きます:
var myVariable = 10
しかし、例えばreturn
という名前の変数を作りたいとき、こう書くとコンパイルエラーになります:
var return = 10
このエラーメッセージは「キーワードreturn
は識別子として使えない」と出ます。これは素晴らしいですね。このようにエラーがしっかり出るのは感心します。他の言語だとreturn
を先に認識して、微妙なエラーが出そうな気がするんですが、Swiftはしっかり識別子として認識していますね。
このような場合、backtick
を使うことで回避できます。例えば、次のように書くと:
var `return` = 10
これでコンパイルが通ります。そして、この変数を使うときも同じようにbacktick
を使って書きます:
print(`return`)
そうすれば、Swiftにとって予約語であるreturn
を変数名として使用することができます。
以前の勉強会で、この話題で盛り上がりました。自分も使い方を忘れていて、参加者に教えてもらいました。そのため、印象に強く残っています。
同様に、他のキーワードも同じ方法で変数名として使用できます例えば、func
やclass
もbacktick
を使えば、変数名として扱えます。この記述方法を使うとコードの可読性が向上する場合もありますが、逆に混乱を招くこともあるので、使用には注意が必要です。
意外と知られていない便利な機能なので、コードレビューやペアプログラミングの際に紹介すると、チームメンバーに驚かれることもしばしばです。ぜひ活用してみてください。 平岡:ああ、ドルゼロは何か妙なエラーが出ていますね。
ちょまど:そうですね。「エクスペクティブウィルセットパラメータネーム」って表示されてますけど、これはどういうエラーなんでしょう?
平岡:多分、willSet
の中に名前を入れないといけないんですけど、ドルゼロが名前として認められていないからかもしれません。
ちょまど:ああ、そういうことかもしれないですね。確かに特別扱い感がありますね。
平岡:なるほどね。
ちょまど:ドル記号は流石に変質に見えるかもしれませんね。あと、?
もまた強い記号ですし。
平岡:なるほど、ドルという感じですね。
平岡:あ、コメントが文字化けしているという話ですが、顔文字みたいに見えますね。
ちょまど:確かに。
平岡:確かに親っていう顔文字ですね。そうしか見えなくなってくるわ。
平岡:ところで、そのコメントに書いた通りなんですけど、これをプリントしてみます。
(動きが変)
平岡:ああ、グローバルの方をキャプチャしたのか。それならいいんだ。勘違いしてた。引数で取っちゃったのかと思いましたが、大丈夫そうですね。
平岡:なるほど、これをバックティックで囲んでみると...。あ、これはだめですか?ああ、こっちがラベルか。色々勘違いしてるみたいですね。もう一回やってみましょう。こうするとラベルがないという状況です。
平岡:じゃあ、10行目のコードを見てみましょう。何か混乱してますけど、10行目ってこうじゃないですか?あ、そうですね。これが動いているのか。なるほど。
平岡:ラベルを省略できるメソッドとしては、例えば String
のイニシャライザーや変換イニシャライザーなどがありますね。例えば、init?
みたいなこういう呼び出しができますね。ああ、そうなんだ。知らなかったですね。わざわざ書く人はいないと思いますけれど、こういう呼び出し方もできるんですね。
平岡:いいですね、という感じにはなりませんが、場合によっては読みやすくなるかもしれないですね。例えば、メソッドコールでなくてもオーバーロードされた関数で引数名ラベルを取らないものや同じベース名の関数があるときに、公開関数で使う場面でエラーが起きてしまう場合。
平岡:こうすると、どちらのアクションか分からないためエラーになりますね。この時には、ラベルありのものを明確に書きたい場合には、func action(value:)
というふうに書くのが良いですね。同様にもう1個の変数にはラベルなしのものを渡したい場合には、こういうふうに書くということです。呼び出す時には、パラメータにラベルを渡すかどうかを決めることで、指定せずに呼び出すという感じですね。 さて、ここでは Swift での関数呼び出しの方法について話していますね。
まず、関数呼び出しの際に必要なラベル名についてです。例えば func example(parameter: Int)
という関数があった場合、呼び出すときには example(parameter: 10)
と書く必要がありますね。ラベルを省略する書き方についても触れています。平岡さんが言っているように、ラベル名が分からなくなったり、ラベル名を省略したりする場合の構文について述べています。
func greet(person: String, from hometown: String) -> String {
return "Hello \\(person)! Glad you could visit from \\(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
この例のように、ラベル名 from
を使って引数を明示的に示すことができ、これがあることで関数の呼び出しが明確になります。時々、ラベル名を省略したいと思うこともありますが、これは Swift の構文解析がそれを許してくれない場合があります。平岡さんが指摘しているように、その場合はちょっと混乱するかもしれません。
次に、別の関数についての考察です。例えばラベルを付ける必要がある場合、その方法を間違えないように注意が必要です。次に、アンダースコアを使ってラベル名を省略する方法について言及しています。
func example(_ parameter: Int) {
print(parameter)
}
example(10) // これは動作します
また、平岡さんが指摘したように、特定の予約語をラベルとして使用する際のルールも存在しています。例えば default
や case
などの予約語はラベル名として使えません。この点についても議論がありました。
次に、ラベル名と予約語に関する混乱について言及しています。
enum Example {
case `default`
// これはコンパイルエラーになります
}
最後に、実際にコードを書いているときには、その場では混乱しなくても他人がコードを読むときに混乱する可能性があるため、明確なラベル名を使用することの重要性について強調しています。そして、現在の Swift の仕様では、ラベル名やアンダースコアを使った省略など、異なる記法が存在することを理解し、それぞれ適切に使い分けることが求められています。
このように、一見すると単純な問題のように見える関数ラベルの扱いにも多くの注意点やルールが存在しています。ソフトウェア開発では、こうした細かい仕様を理解し、適切に対応することで、より読みやすく、理解しやすいコードを書くことができます。 とりあえず、こういった場合はバックティックを使います。予約語をユーザー定義のものとして使うときに使用します。また、この親というコンセプトが解除されたので、不足として記号関係をバックティックで括って変数として使えることがわかりましたが、記号系は使わないほうがよさそうです。
さっきのドルサインの話もありましたが、必要な変更で使えなくなってしまうこともあります。そして、このアンダースコアは分かりにくく、誤解を招きやすいので避けたほうがよさそうです。しかし、アンダースコアのようなものが使えたということは、バックティックも大丈夫でした。
アセンダントはどうでしょうか?これはさすがに使えないようです。次にユニコード文字で使えないものを試してみたいと思います。シグマなどが使えなかった記憶があります。
例えば、シグマやオプションW (W
)なども試してみましたが、これもダメでした。エラーの出方がざっくりとしていて、何が問題か分かりにくいことがあります。バックティックを使っても特別扱いされるとは限らないようです。アンダースコアのような特殊な事例がたまたま見つかっただけみたいです。
では、本題に戻りましょう。バックティックについてですが、予約語と同じ名前を変数として使うときに使います。ただし、絶対に避けられない場合を除き、キーワードを変数名として使うことは避けるべきです。これは、予約語と同じ変数名を使うのは混乱を招くからです。
列挙型でdot.default
のような名前を使う場合もありますが、標準ではあまり見かけません。標準機能としては、例えばFileManager.default
やUserDefaults.standard
のように使われています。また、他にもシングルトンパターンとしてshared
という名前がよく使われます。アプリケーションの共有インスタンスに対してもshared
を使用します。
ですが、シングルトンパターンと異なり、複数のインスタンスを生成できるけれどデフォルトのインスタンスがある、という使い方もあります。こういった場合、デザインパターンとしてはシングルトンパターンではないことになりますね。デザインパターン警察から怒られるかもしれませんが。
他にもNotificationCenter.default
やUserDefaults.standard
といった完全なシングルトンとして用意されているものがありますね。 ユーザーデフォルトはスタンダードユーザーデフォルトですね。グループでやったりとか、キーチェーンのグループもありますよね。UIデバイスについて話すと、UIデバイスはUIキットの一部です。そうなりますね。UIDevice.current
ですね。あまり馴染みがないかもしれませんが、デバイス情報を取得するために使います。
UIデバイス(UIDevice)に関して、初めて聞いた人もいるかもしれません。デバイス情報を取得するのに使いますね。思い出しました。UIDevice.current
でデバイス情報を取得します。確かにデフォルト(default)と言われるよりも、カレント(current)の方が意味が分かりやすいですね。デフォルトデバイスと言われると、人によって解釈が違うかもしれないですからね。カレントデバイスと言えば、今持っているデバイスという意味が通じるので、確かに分かりやすいです。
ファイルマネージャーのデフォルトと言われると、スタンダードとデフォルトの違いがわからなくなりがちです。シェアード(shared)は共有感がありますので、シェアードとカレントの方が意味が明確です。スタンダードとデフォルトの違いについては、標準的なものと規定のものというニュアンスの違いがあります。標準はふわっとしている印象がありますが、規定はより具体的な設定値を指します。
例えば、イント型の初期値は0ですが、これがデフォルト(規定値)ですね。でもこれがスタンダードと言われると、少し違うかもしれません。イント型の0がスタンダードというのは、ちょっと意味が違ってきますから。
ユーザーデフォルトに話を戻すと、確かに「スタンダードユーザーデフォルト」という名前は少し混乱するかもしれませんが、アプリケーションの標準のプロパティリストのことを指します。それに対して、デフォルトと言われると、他のユーザーデフォルトが含まれるかもしれないなど、少し広がりを持った意味になります。スタンダードユーザーデフォルトという名前は、デフォルトを使いすぎて名前が分かりづらくなったのかもしれません。
他にもデートのフォーマットスタイルやタイムスタイルについても、スタンダードという言葉が使われています。例えば、DateFormatter.Style
のstandard
やshort
、long
などがありますが、ここでデフォルトと言うと少し混乱してしまいます。こうやって見ていくと、かなり上手に命名されていますね。 これでね、コメントで「Intにドットゼロがあるけど、Intにアイデンティティがない」って話が出ていましたが、確かに整数型(Int)にアイデンティティがないですね。
アイデンティティというと、何だと思いますか?たぶんゼロにしたいという意味でしょうか。数学の用語では単位元と呼ばれるものですね。足し算の単位元はゼロですが、掛け算の単位元は1です。なので、アイデンティティは1ですね。
この話、CGのアフィン変換にも関連しています。アフィン変換はすべて掛け算だから、掛け算にはアイデンティティが必要です。
そうですね、あまり厳密に整数のゼロや1を不動小数点数と比べて考えることはあまりないです。アトム単位(Atom)にはアイデンティティがないですし、インフィニティ(Infinity)やその他の値はありますけどね。アイデンティティについて考えないことが多いです。
掛け算も求めるのであれば、評価をするときに和を求めるだけではないという視点が面白いですね。この「ゼロ」って、「AdditiveArithmetic」に定義されています。このプロトコルは足し算(加減算)ができる型にゼロを含んでいます。そして、その他に足し算演算と引き算演算も規定されています。
これと同じノリで掛け算にも適用できる「MultiplicativeArithmetic」みたいなプロトコルを仮に作り、その中で static var identity
とかを定義すれば、統一感が出てきてかっこいいかもしれないですね。確かに、そのアイデアはいいかもしれません。それがあれば、どんな型でも汎用的にジェネリクスで対応できるので便利です。
もしかすると、このアイデアをプルリクエストすると通るかもしれないですよ。
最近ありましたが、掛け算を含ませたいのにプロトコルにはあまり定義されていないんですよね。例えば、BinaryInteger
とか、もっと深い方に行かないと掛け算が規定されていないんです。確かに Numeric
にはあったかな、無かったかなと忘れましたが、もしかしたら Numeric
にもないかもしれません。もう一段階深いところにあるかもしれないですね。
確認してみると、Numeric
はありましたね。ただ、AdditiveArithmetic
プラス Numeric
で掛け算ということになっていて、なぜこういった汎用的な方に行っちゃうんでしょうね。もう少し基本的な概念としての掛け算であるべきな気がします。
このあたりは専門的なので詳しくは分かりませんが、「MultiplicativeArithmetic」のようなプロトコルがあってもいいんじゃないかなと思います。もし、明るい人がいれば、そのようなリクエストを出して論理的に進めていけば、良いものができるかもしれませんね。
それでは、時間になりましたので、今日はこれぐらいにしておきましょう。お疲れ様でした。ありがとうございました。