前回までは Swift API Design Guidelines の中から「命名規則」に着目して API の描き方をみていきましたけれど、今日から「表現方法」について眺めていきますね。

———————————————————————— 熊谷さんのやさしい Swift 勉強会 #18

00:00 開始 00:43 全般的な表現方法 01:36 プロパティーの計算量 16:39 didSet 18:51 焼却計算量 21:12 オーダー表記 22:13 min 関数の計算量 22:29 プロパティーにするかメソッドにするか 25:25 フリーな関数を使う場面 26:47 明確な主体が存在するか 27:14 何にも制約されないとき 28:48 確立された表記方法のとき 29:45 フリーな関数を使う例 31:47 関数を型に所属させる例 34:42 制約のない主体を扱う場面 38:51 確立された分野の表記方法を扱う例 43:24 フリーな関数を使おうと思ったときは 44:40 大文字小文字の区別 45:55 キャメルケース 49:01 頭字語の扱い 53:31 頭字語における例外的扱い 55:34 次回の展望 ————————————————————————

Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #18

今日は、引き続きAPIデザインガイドラインのお話です。今回は新しいセクションに入ります。以前はガイドラインの基本項目やドキュメントコメントについて、また命名規則についても学びました。命名規則については特に長くじっくり見てきましたが、今日は表現方法についてです。

一見、命名規則と同じように感じるかもしれませんが、APIデザインガイドラインでは表現方法は別のセクションとして記載されています。メソッドの名前に直接関与する部分もありますが、そうでない部分もあり、もう少し広い視点でAPIを読みやすくデザインしていこうという内容になります。

まず最初に挙げられているのは、ドキュメントコメントについての追加ルールです。計算量についてのガイドラインです。プロパティは一般的にオーダー1(O(1))だと思われがちです。要はプロパティにアクセスすれば、瞬時に答えが返ってくるという感覚です。何か値を入力したらすぐに応答が返ってくるという時間感覚があります。そのため、プロパティがO(1)でないときにはドキュメントコメントで明記しましょうというのがガイドラインに含まれています。

具体的にどういったことかというと、例えば変数に値が入っていて、それを使おうと思ったときに、グローバルであれば普通に値が取得できるといった感覚があります。これがオブジェクトになるともう少し分かりやすいかもしれません。例えば、ストラクトがあり、そのストラクトがvalueというプロパティを持っているとします。このインスタンスを作成してvalueにアクセスするとき、データ構造体が持っているvalueがすぐに取得できると思うじゃないですか。しかし、これが実際には30分かかるとは想像しないですよね。

そのため、プロパティがO(1)でない場合には、それをドキュメントコメントに書いておくべきです。保存型プロパティについては基本的にそんなに遅くなることはないと思いますが、計算型プロパティの場合は処理が重くなることがあります。計算型プロパティではゲッターを使って値を計算するため、重たい処理を含むことがあり、その場合にはドキュメントコメントに計算量を明記しておくことが重要です。

例として、O(1)でなく、計算量が非常に大きい関数があるとします。ドキュメントコメントに「オーダー O(n^2)」と書いてあれば、「これは重い処理だな」と事前に分かります。また、非同期処理にするなどの対応が必要だと判断できます。このように、期待される計算量から逸脱している場合には、ドキュメントコメントに詳細を明記しておきましょうというガイドラインになっています。

例えば、文字列のカウントプロパティなどは一般的には瞬時に返ってくると考えられます。しかし、重たい処理を含む場合にはその旨をドキュメントコメントで示す必要があります。そうすることで、利用者がプロパティの使いどころを適切に判断できるようになります。

要は、計算量が直感的でないプロパティやメソッドについてはドキュメントコメントで詳細を記載しておき、利用者にその旨を伝えることが大事です。これがAPIデザインガイドラインの一部として重要視されています。 こうすると配列のカウントはもしかすると数えているかもしれません。ドキュメントコメントには特にオーダーは書いてないですけどね。右側には今ドキュメントコメントが出るようにしています。でも、多分配列のカウントは数えています。Array型は何かをやってるかもしれませんけど、自分で作ったコレクションなんかは絶対数えますよね。Sequenceとかでね。インデックスがあるから早いのか、だからSequenceはカウントを持たないんですね。だからコレクションは大丈夫なんです。インデックスの範囲を取ればいいからですね。ディクショナリーも一緒です。

とりあえず、isEmptyの方が最適化されて早くなるみたいです。みんないい感じの補足を入れてくれています。とりあえず、カウント数を数えるのと空であるか否かを判定するのはちょっと違うみたいですね。文字列の場合も、確かisEmptyが最適化されているはずです。確か、Objective-Cの頃にそういうのを調べた気がします。

今、計算型プロパティと保存型ストアドプロパティの話をしています。それと、アレイのremoveFirstがもしかするとと指摘してくれた方がいます。removeFirstはちょっとメソッドになっちゃうので、プロパティの話とは少し違いますけど、それでもメソッドの場合も計算量が多いようなら書いてあげると親切でしょう。これがvarになってないからですね。親切になることは間違いないです。

計算量を意識して書いたコメントをつけてあげましょうということで、ドキュメントコメントに@complexityを使って書くことが基本になってくるみたいですね。例えば、O(n)と書いてあげる感じです。ありがとうございます。

続いて、計算型プロパティと保存型プロパティについてですが、ストアドプロパティは直感的に見て、普通保存されているものを取り出すだけなのでサクサクっと行きそうな気がします。でも、didSetwillSetなんてものがあるから、処理が重くなることもあります。それで計算型プロパティについてガイドラインに従うことが大事です。計算量が多かった場合には、ちゃんとコメントをつけてあげるのが親切ですね。読みやすくて理解しやすいコードになります。

同様に、メソッドにも計算量がかさむ場合はしっかり伝えていくと親切です。例えば、removeFirstとかも計算量がかさむときには計算量についてコメントをつけてあげるのがいいですね。

それから、didSetがあると内部的には実質的に計算型プロパティとして扱われるという感じです。何かフックが入るので、確かにゲッターが追加されるイメージになります。ここまででOKですかね。こんな風にプロパティの計算量をちゃんとコメントに書いていくことが重要です。

計算量の話については前回もちょこっと出てきましたね。何の話で出たのか忘れちゃいましたけど。それについてのO(1)と、コメントに特にオーダーが書いてないのとでは大きな違いがあります。これが計算量をどれくらいかってわからないと、ガイドラインに沿っているのかどうかがわかりません。

他にも、valuesappendとかも計算量がO(1)って書いてあります。この話が前のサーバーサイドLT会議でちょうど話題に上がっていて、配列のappendってO(n)じゃないのかという話がありました。でも、これはO(1)って評価するんだというのが面白かったです。Swiftもその評価に倣ってO(1)になっていて、平均を取るんですね。 超たくさんの呼び出しが起こった時に、一般的にどういう計算量になるか、みたいな話です。焼却計算量という考え方があります。これはリストの末尾に要素を追加する処理などで使われます。この「焼却計算量」がO(1)ではない理由が面白いので、興味ある人はぜひ調べてみてください。また、サーバーサイドのLT会で阿部さんに聞いてみるのも面白いかもしれません。