https://youtu.be/qvmoT3iFKUQ
今回からは The Basics
の 整数
について見ていきます。プログラミングにおける整数の在り方、Swift でどのように表現されているか、そういったあたりを確認する回になりそうです。シンプルな話題ですけれど、全ては 0 と 1 で表されると言われがちなコンピューターの世界を支える技術とも言えるところですので、そんなあたりを意識しながら眺めてみると改めて見直せるところがあるかもしれないです。どうぞよろしくお願いしますね。
———————————————————————————— 熊谷さんのやさしい Swift 勉強会 #110
00:00 開始 01:43 整数とは 02:40 数学における整数 04:43 整数と自然数 05:10 自然数はゼロを含む? 09:47 コンピューターにおける整数 12:37 Swift における型の命名規則 13:08 整数部分と小数部分 14:23 BASIC 言語における型の扱い 15:19 型とデータ表現 16:10 型を意識させない言語 17:28 固定小数点数 17:53 二進化十進数 19:58 ビット操作まわりを学んでみるのも良いかも 22:17 Z80 マシン語秘伝の書 24:53 Swift の整数表現 25:33 固定長整数を表すプロトコル 26:31 二進数表現の整数を表すプロトコル 27:14 数を表すプロトコル 27:42 Swift の整数は二進数で表現される 29:51 数値をプロトコルで扱う場面 32:31 標準ライブラリーで数値をプロトコルで扱う場面 34:11 数値表現についてのまとめ 34:32 数値とメモリーサイズのお話 37:22 クロージング ————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #110
はい、じゃあ今日は新しいセクションに入る…と言っても、セクションというよりは「ザベーシック」の中の一つのトピックですね。そのトピックは「整数(インテジャー)」についてです。
整数というと、単純な分野に思えるかもしれませんが、パソコンの仕組みを理解する上で非常に重要なテーマです。昔よく「パソコンは0と1でできている」と言われていましたが、これは今でも変わりません。パソコンの世界はビットで構成されており、そのビットが集合して整数を表します。これがパソコンの基本的な動作の一つとなるわけです。
では、具体的に整数について見ていきましょう。整数とは、端的に言えば小数部分を含まない数です。例えば、42や-23、さらには0も整数の一部です。数学的な定義で説明するなら、高校の数学で習う範囲かと思います。
ざっくりした説明としては、 0, 1, 2, 3, ...
のような数を指しますが、正確な定義は少し確認してみましょう。Wikipediaによると、「数学における整数とは、1とそれに1ずつ加えて得られる自然数、これらに-1を乗じて得られる数、および0の総称である」となっています。これは要するに、自然数(1, 2, 3, ...)とその負の数、および0を全て含むものです。
次に、整数の集合は一般に Z
として表されます。これは大学数学でよく使われる表記ですが、高校の数学でも見かけるかもしれません。頭の中で N
を想像しがちですが、 N
は自然数を意味します。自然数は 1, 2, 3, ...
のような正の整数のみを含むものです。
自然数に関しては、0を含むかどうかという議論があります。大学数学では自然数に0を含めることが多いようですが、中学高校では含めないことが多いです。この辺は数学の専門家でも意見が分かれるようです。
0という数字は非常に特別で、数学の発展において重要な役割を果たしました。0が発見されたことによって数学が大いに進化した歴史があります。たとえば、理科や物理学の中でも目に見えないものを表すための概念として、空気のように0やその他の抽象的な概念が重要になってきます。
まとめると、整数は0と正の整数、負の整数を全て含む数です。そして、この整数はパソコンの基本的な仕組みを理解する上で非常に重要な役割を果たしています。
今回の話を通じて、整数の定義やその重要性について深めていけたらと思います。皆さんも高校数学や大学数学で習った知識を振り返りながら、一緒に勉強していきましょう。 とりあえず0は入らなくてもいいかな、という認識でいると良いかもしれません。整数の定義についてはこれくらいで良いでしょう。細かいことは書いてあるけれど、要は自然数に0が含まれるか否かが重要になる場面では、その点を明記すれば良いのです。
スライドに戻りますが、整数とは、0や1、2、3、4などの数だけでなく、マイナスの数も含まれます。符号付き整数や符号なし整数といった区別がありますが、この部分は特に強調するほどのことではありません。符号付き整数には正の数、0、負の数が含まれ、符号なし整数には正の数と0が含まれます。0はどちらにも含まれるという感じですね。
これまで話していた整数は一般的な概念としての整数で、これから話すのはコンピューター特有の話です。符号付き整数と符号なし整数には、それぞれ8ビット、16ビット、32ビット、64ビットの型が提供されています。ここが重要なポイントです。現在のコンピューター環境では、整数をビットで表現することが非常に重要です。ビットは0か1かの二進数で、8桁なら8ビット整数、16桁なら16ビット整数です。
次に、C言語と同じような命名規則についてです。型の名前として、8ビット符号なし整数はUInt8
、32ビット符号付き整数はInt32
というように表記します。符号付きか符号なしは略されています。整数を表すインテジャーはint
でサイズが付けられています。現在のプログラミングでは当たり前の命名規則です。
UInt
を見るたびに自分の経験を思い出します。以前、型を付ける言語としてBASICを使っていたころ、BASICの整数表現はUInt8
のような記法ではなく、変数名にドルマークを付ける形式でした。C言語を初めて使ったときにUInt
を理解できず、しばらく「ユニット」と読んでいました。この話が通じていたのかは分かりませんが、間違っているなと思われていたかもしれません。まあ、どうでも良い話ですが、UInt
を見るたびにそれを思い出します。
これらの整数型は大文字で始まる名前を持っており、SwiftのAPIデザインガイドラインに沿っています。型やプロトコルの名前は大文字で始め、その他は小文字で始めると書かれています。この辺りについては、特に他に重要なことはないでしょう。 小数部分というのは、例えば 3.1415
の 0.1415
といった部分です。このスライドを作る際に、発表者ノートにメモを残していたのですが、そのメモが何を言いたいのか自分でもよくわかりません。ただ、とりあえず小数部分については「フラクショナルコンポーネント」と呼ぶようです。どうしてこの話題を調べたのか記憶が曖昧ですが、まあ紹介しておきましょう。メモに書かれていたので。
整数部分については「フォールナンバーパート」もしくは「インテジャーパート」と呼ばれます。このような表現があるらしいので、紹介しておきます。
続いてコメントにベーシック言語に関する話題が出てきました。そういえば、自分はMSXベーシックをやっていた頃があって、記号の使い方を思い出しました。パーセント (%
) が整数を、ビックリマーク (!
) がフロートを、シャープ (#
) がダブルを表していました。確かにそうでしたね。具体的にどのように使われていたかはあまり覚えていないのですが、内部で暗黙的に型が扱われていたようです。
JavaScriptは整数をダブル型で扱うという話もあります。これは面白いですね。ベーシック言語のように型が明示されない言語は、実際には古くから型が暗黙的に処理されていました。これはマシン語においても同じことで、プログラマーが意識して型を扱う必要があったためです。特にZ80というCPUのマシン語では、全てが整数であり、整数型のビット配列を自分で用いて小数を表現していました。これを簡単にするためにベーシックが作られたので、型がない言語といっても実は裏では型が存在していたのです。
最近では型のある言語と型のない言語が区別されますが、型がないと何も感じさせないということはないですね。逆に、型があることが当たり前となっています。
固定小数点数についても懐かしい話題です。整数型で事実的に固定小数点数を作る方法がありましたね。何桁目からが整数部分で、それより前が小数部分というふうに表現します。これも手軽でした。
さらに、マシン語にお世話になったのがBCDコード(Binary-Coded Decimal)です。これは、4桁の二進数で0から9を表現する方法です。一桁がゼロから9までを4桁の二進数で表現するというものです。
具体的には、実進数の桁が上がるごとに二進数の桁が4桁ずつ増えます。例えば実進数の9は 1001
、28は 0010 1000
と表現します。これが便利だと思って使っていましたが、今こういう話を見てもあまり思い出せなくなっています。それが対価なのか進化なのかはわかりませんが、とにかく便利だった記憶があります。 とりあえずそれはそれでいいのかなと思います。昔はビットをいろいろ工夫して頑張っていた世界がありました。マシン語などをやっていた人にとっては、BCDなども馴染みのあるものでしたよね。
最近はビットを意識して何かをするということが少なくなった気がしますが、コンピューターは依然としてこの考え方で支えられているので、ビット周りの知識も重要ですね。あまり馴染みのない人も勉強してみると見える世界が変わってくるかもしれません。もちろん、興味がなければ無理にやる必要はないと思いますが。
情報処理技術者試験などでも、この分野は範囲に入っています。基本的なところから勉強することになるので、そういう勉強をする際にはビット表現の世界を見ておくといいかもしれません。
例えば、数字を5倍することを考えると、2倍して2倍して4倍にしてから元の値を足すという方法もあります。これは二進数的には左シフトを2回行うことで4倍になり、その後元の値を足すことで5倍になります。現在では *5
と書いたほうが簡単でわかりやすいですが、こういったビット操作も一種の面白いパズルのようなものです。
組み込みでは今でもビット操作が現役です。自分は組み込みやマシン語には縁がなかったですが、興味のある方には面白い分野だと思います。たしかに、z80マシン語入門
なんて本もありましたね。z80マシン語秘伝の書
という本が面白いです。例えば、「同じ値同士でORを取るとキャリーフラグだけリセットできる」、「同じ値でXORを取ると0に初期化できる」など、いろいろなビット操作のテクニックが載っています。
昔は各コンピューターごとにアセンブラーの本などもありました。私は msxマシン語
の本を読んでいた記憶があります。
さて、Swiftの話に戻りますが、Swiftでも整数はビット表現で表示されています。8ビット
、16ビット
、32ビット
、64ビット
といった固定幅整数として定義されています。Swiftの整数は全て二進数で表現された整数として作られており、これは固定幅整数プロトコル FixedWidthInteger
に準拠しています。このプロトコルにより、Int8
、Int16
、Int32
、Int64
だけでなく、符号なし整数の UInt8
、UInt16
、UInt32
、UInt64
も全て準拠しています。
Swiftの標準で提供される整数型は全てこの FixedWidthInteger
に準拠しているという特性を押さえておくと良いことがあるかもしれません。 さらに、これが固定長整数で、桁が固定されている整数です。そして、「プロトコル」に準拠しているのですが、ここでいうプロトコルとは、「桁が固定されている」という概念がない BinaryInteger
というプロトコルです。つまり、桁を考慮しない二進数表現の概念がここにまとめられている感じです。
次に BinaryInteger
に続くのは何でしょうか。たしか、Numeric
になってしまうのではないかと思います。そうですね、Numeric
です。この BinaryInteger
よりも前の方で、整数ではなく数字全般に関する広い概念を扱っています。このように、広い概念に対するプロトコルがまとめられているという感じです。
また、Swiftの標準ライブラリで提供されるすべての整数は FixedWidthInteger
に準拠しており、さらにそれが継承している BinaryInteger
にも準拠しています。したがって、二進数を用いた発想で標準ライブラリの整数を扱うことができるわけです。
BinaryInteger
、つまり二進表現の整数は、コンピュータの世界では標準的な考え方が決まっています。符号付き整数の場合、先頭の1ビットが符号を示し、残りのビットで値を表現する形になっています。どの整数型でもこの構成は変わりませんので、それをもとにビット操作も許されると理解して良いでしょう。少なくとも BinaryInteger
に準拠しているものとして扱えば、その型を取ることで、近代のコンピューター環境で当たり前になっているビット操作が可能です。
BinaryInteger
に準拠している型を使用すれば、例えばビットシフト演算も普通にできます。これが固定長整数になってくると、型のビットサイズがどうであるかは関係なく、プロトコルに指定されているものとして扱えるので、それに依拠して汎用的なコードを書くことができます。これにより、型の具体的なサイズを知らなくてもコードが書けるようになります。
ただ、あまりこのプロトコルを直接取り扱ってコードを書くことはないかもしれません。頻繁に使っている方はいらっしゃるのでしょうか。自分が使うことがあるとすれば、整数を対象としたメソッドを増やすときにエクステンションで使うことがある程度です。あとは、配列で何かをするときや、合計を計算するためにプロトコル関数を増やす際などでしょうか。
このように、例えば BinaryInteger
や FixedWidthInteger
に準拠しているプロトコルで全ての整数が準拠していると書いておいて、reduce
で合計などを計算するコードを書くときには便利です。汎用的なコードを書く際に少し使う程度で、頻繁に使うことはないかと思います。 少なくとも自分は、これも表現がオーバー気味で、もうちょっと宣伝させて、バイナリーインテージャーぐらいにします。もう少し下げますけどね。これも通るけど、足し算さえできればいいのでこうするかな。これで成り立っている。0がダメか?結構 0.0
で取れんじゃなかったかな。こんな感じ、これぐらいが妥当ですかね。
とりあえず、こうやってちょっと整えていきますけど、雰囲気的には BinaryInteger
みたいなレベルのプロトコルを使うときって、こういう汎用的な関数をエクステンションで増やすようなときぐらいかな。ただ、標準ライブラリーで BinaryInteger
を想定したものを使ってることとかって。たとえば今、定義たどれたっけ、Int8
と何でもいいんですけど。型が違う比較演算、この二つ、これでいいや。AとBの一致比較なんか今できるようになってますけど、この定義。この比較演算の定義をたどると - たどれないか、そうか。
このときに受け取るパラメーターとして BinaryInteger
みたいなふうに取ってたと思うんですよ、確か。そういったときに桁数がいくつかわからないけど、一致比較をする上で桁数ってそんなに問題ないじゃないですか。どんなにあべこべな桁数であっても一致判定できる。そういったような汎用的な演算を作るようなときには BinaryInteger
プロトコルをパラメーターとして想定するっていうのは有意義だったりして。
関数出てこないな、まあいいや。という意義だったりして、こういったときに BinaryInteger
プロトコルと、あと BinaryInteger
っていうものがどういうデータ構造になっているのかっていう知識が、こういう場面で役に立つっていう感じかな。まあまあ、とりあえずね、こんな感じでスイフトの整数っていうものは BinaryInteger
、二進法表現の通知として表現されているよっていうのが、整数のお話の中で特に重要になってくるところかな。
はい、だいたいこれで区切りが良くなりましたけど、なんか整数周りで面白いお話知ってる人いますか?なんかあるかな。まあ面白いって、なんか豆知識程度でいいんですけどね。特にないかな?スイフトじゃなくて良いんですけど、何でもです。
組み込みとかだと山ほどあるんですけど、いいですね。特になんかパッと思い出すものとかありますか?そうですね、こういう高級な言語を使い出すとちょっと頭からすっ飛んでしまうんですけど、組み込みとかだとメモリーのサイズとかにやたらと制限があったりとか、ほとんど最近はないんですけど、整数とかもやっぱりメモリーなのでできる限り小さくって言うんですよ。ビット幅。またこれか。できるだけ小さくっていうのと、でも必要なサイズがいるっていうことで、やたらとオーバーフローしたりとかいうようなこと聞こえる。ビット幅ってものすごく一番気を使うところ。最初の設計、どこまで小さくできるのとか。一つか二つでやったらなんてことないんですけど、テーブルとかにすると、それも100万とかのテーブルになっちゃうとサイズが尋常じゃなく増えていくんで。
そうですよね。だから今どきの言語って Bool
型ってたぶん1バイトですよね、8ビットか。取っちゃうけど、そんな贅沢なことしてらんないからね。やってらんないですね。1ビットですね。ちゃんと1ビットずつで取りますよね。ビット演算使って抽出してね。抽出って大げさか。判定したいビットを1にしたやつで & を取って0じゃないか判定するんですよね。そうです、そういう風にしてね。
他にはC言語だと、通常だと int
、昔のやつを引き継がれてるんですけど、int
って書くと何ビットか分からない。そうですよね、そういうのありますね。処理系によって変わってくるんで、これ16ビット?32ビット?とか。組み込みやり始めた時に int
って書いたら叱られるんですよ。これ何ビットやったらできるって。そうですよね。データサイズを厳密にするときには int
って書けないんですよね。その int
のサイズの話、この後、だから次回とかに出てくるんですけど、そういう人は逆に int
って書くのを推奨してくるんですよ。ここも面白いところでね。そういったところをちょっと見目しつつ、昔からやってる人がいればね、比べてみてもらえたらいいなと思います。
コメントに、ツイートでAPIデザインガイドラインで、そうそうそう、int
を推奨していたはず。そうなんですよ。これこの後出てくるんでね、なぜそうなってるのかっていうのは次回以降楽しみにしてもらえればと思います。
はい、じゃあいい時間になりましたのでね、今日はこれで終わりにしましょう。お疲れ様でした。ありがとうございました。