配列に紐付いたデータは、たった一つ、配列の要素だけです。メモリ上では次のようになっています。byteつまり、変数は256バイトのデータを保持していて、ただそれだけです。私たちは各要素によく知られたインデックスの構文でアクセスできます。この範囲の外側にある値のインデックスにアクセスしようとすると、プログラムがクラッシュします。いう名前の組み込み関数があり、これは配列、スライス、また他のいくつかのデータ型の要素数を返します。何を返すかは明らかです。配列には使われるべき場所があります。インスタンスの変換行列として良い表現になっています。スライス内部で保存領域を確保する目的で使われるときです。理解しなければいけません。スライスはスライスの変数自身とは別に保存された配列の境界を表現したデータ構造です。スライスは配列の一部を表現しています。配列から切り取る(スライスする)ことで作ることもできるでしょう。buffer[100:150]上のスニペットでは、明示的になるように変数宣言を冗長な形で書きました。型で、「バイトのスライス」と呼びます。いう名前の配列から、100(含む)から150(除く)の要素を切り取ることで初期化されました。イディオムに近い構文では、初期化表現で追加される型情報を省略します。buffer[100:150]関数内では、短い記法を使うこともできます。変数とは一体なんでしょう。小さなデータ構造とみなしてみましょう。するとスライスが作られるときには、その背後で次のようなことが起きていると考えられます。&buffer[100],}もちろん、これは単なる説明にすぎません。要素のポインターの型が要素の型に依存するものとは言え、このスニペットは一般的な仕組みを説明しています。これまで、配列を切り取る操作をしてきましたが、スライスを切り取ることもできます。slice[5:10]配列から切り取ったときと同様に、この操作では新しいスライスを作成します。これはつまり元の配列の要素105から109を意味します。構造体は次のようになります。変数の中にあるものと同じ配列を参照していることに注意して下さい。再切り取り(再スライス)、つまりスライスを切り取って、その結果を同じスライスに保存することもできます。変数のものと同様になります。スライスと切り詰めるときに再切り取りをよく目にします。最初と最後の要素を捨てます。構造体がどのようになるかを書いてみてください)経験あるGoプログラマがしばしば「スライスヘッダー」について話すのを聞くでしょう。保存されているからです。関数に渡される実態です。引数は、実際には「スライスヘッダー」なのです。このスライスヘッダーが存在することが何を意味するのかを理解しましょう。スライスを関数に渡すスライスがポインターを持っていたとしても、スライスそれ自身は値であることを理解することが重要です。一皮めくれば、スライスはポインターと長さを持つ構造体の値です。構造体のポインターではありません。スライスヘッダーのコピーが渡されました。この振る舞いは重要な結果をもたらします。次の簡単な関数を考えてみましょう。ループを使って)繰り返し進めていき、各要素を1増加させています。slice)}実行結果は次の通り。スライスヘッダーのコピーは同じ配列を表しています。関数に渡された引数は本当にコピーです。次の例で確認できます。49スライスの引数の中身は関数によって修正可能だけど、そのヘッダーは修正不可能であることを確認しました。変数に保持されている長さは関数の呼び出しでは変更されません。元のスライスヘッダーが渡されているわけではないからです。いま例示したように、それを結果として返さなければいけません。スライスへのポインター:メソッドレシーバースライスヘッダーを変更する関数を書く場合、関数にスライスヘッダーのポインターを渡すというのも手です。先ほどの例の違うバージョンを書いてみます。49この例では、特に間接的な代入をしているところ(一時変数を使っています)がぎこちなく見えますが、ポインターへのスライスではよくある例です。スライスを変更するメソッドにポインターレシーバーを使うイディオムがあります。スライスに最後のスラッシュ以降を捨てるメソッドを持たせたいとしましょう。そのメソッドは次のように書けます。pathName)}このサンプルを実行してみると、呼び出し元のスライスを期待通り更新していることがわかると重います。実行結果:/usr/bin(演習:レシーバーの型をポインターではなく値に描き変えて再度実行してみましょう。実行結果がなぜそうなるか説明して下さい。メソッドは値レシーバーでも構いません。値レシーバーでも同じ配列を参照しているからです。構文の中で2つの変数を使っています。何度も書くことを回避しています。次の、intのスライスの引数を1要素だけ拡大する関数を見てみましょう。slice}(なぜ修正したスライスを返す必要があるのでしょうか)実行してみます。+0x220スライスの容量が20まで増えているのがおわかりに・・・増えていません。スライスヘッダーの3つ目の要素についてお話するときがやってきました。スライスヘッダーには、配列へのポインターと長さに加えて、スライスの容量も保持しています。フィールドは内部で持っている配列に実際どれくらいの空き容量があるかを記録しています。パニックを引き起こす原因となります。iBuffer[0:0]ヘッダーは次のようになっています。フィールドは、内部で持っている配列の長さからスライスの最初の要素の配列におけるインデックス(この場合は0)を引いたものとなります。full!")}makeスライスをその容量以上に大きくしたい時にはどうしたらいいのでしょうか。スライスの定義では、容量が大きくできる最大値です。スライスが新しい配列を指すように変更することで可能となります。新しい配列を確保するところからはじめましょう。使うほうが簡潔にできます。関数は3つの引数を取ります。スライスの型、初期値の長さと容量です。確保する配列の長さを意味します。次の関数呼び出しで、長さ10で余裕が5(15-10)あるスライスを作成しています。15このスニペットではintのスライスの容量を倍にし、長さは同じままに保っています。cap(slice))このコードを実行すると、スライスは、配列を再確保する必要ができるまでに、ずっと多くの余裕ができます。30スライスを作成するとき、長さと容量が同じであることがしばしばあります。短い書き方があります。長さの引数を容量のデフォルト値にすることができます。容量の引数を書かなければ、長さと容量を同じ値にできます。スライスは長さと容量が10に設定されます。容量を倍にしたときに、古いデータを新しいスライスにコピーするためにループを書きました。いう組み込み関数があります。データをコピーします。使って前の例を書き直すと次のようになります。両方の引数の長さに注意を払いながら、コピー可能な分だけコピーをします。関数がコピーする要素の数は、2つの引数の長さの短い方です。要素の大小を自分で管理しなくて済みます。コピーした要素の数を整数値で返します。関数はコピー元とコピー先に重なるところがある場合にもきちんと動作します。1つのスライス内で要素を動かしたい場合です。使ってどのようにスライスの真ん中に値を挿入するかをお見せします。Insertは値をスライス中の指定したインデックスに挿入します。指定するインデックスはスライスの範囲内でなければいけません。スライスには新しい要素が入る余地がなければいけません。slice}この関数で注意することがいくつかあります。はじめに、スライスの長さが変わったので、更新されたスライスを返さなければいけません。つぎに、この例では便利な略記法を使っています。この式はslice[i:]つぎの式とまったく同じ意味です。slice[i:len(slice)]また、まだこの使い方は見せていませんが、スライス記法で最初の要素を省略することも可能です。デフォルトでは0になります。したがってslice[:]はそのスライス自身を表すことにほかなりません。この書き方は配列をスライスにするときに便利です。この書き方は「ある配列の要素をすべて持つスライス」を宣言するのに最も短く書ける方法です。array[:]話がそれてしまいました。関数を実行してみましょう。要素を追加するための余地です。あの実装はバグがありました。スライスの容量が小さすぎる場合、関数がクラッシュしてしまうからです。例も同様の問題があります。スライスがいっぱいなとき。拡大しなければいけない。サイズが0でも拡大できるように2倍+1のサイズにする。slice}この場合、スライスを返すという部分が特に重要です。理由は、結果のスライスを再確保したとき、それはまったく異なる配列を参照しているからです。スライスがいっぱいになったらどうなるか、短いスニペットで確かめてみましょう。0x10436120最初の大きさ5の配列がいっぱいになったときに再確保していることに注目して下さい。新しい配列が確保されたときに、容量の0番目の要素のアドレスが変わっています。関数を参考にして、複数の要素でスライスを拡張できるずっと便利な関数を書くことが出来ます。この実装では、Goで関数が呼びだされてた時に、関数の引数をスライスに変換できる機能を使います。Goでの関数の可変数引数の機能を使います。繰り返し呼び出します。シグネチャーは次のようになります。取り、それ以降に0以上のintの変数が続くことを意味しています。intを扱う限り、intのスライスです。Appendはスライスにitemsを追加します。最初のバージョン:ただExtendを繰り返し呼び出す。