Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 1194] ファイルの概要

このコミットは、Go言語の初期開発段階において、内部的に使用されていたvectorパッケージの利用を廃止し、代わりにarrayパッケージに移行した変更を記録しています。具体的には、正規表現エンジン(regexpパッケージ)とテストコードにおいて、動的なデータ構造の管理にvectorではなくarrayを使用するように修正されました。また、ビルドスクリプトであるmake.bashも、この変更に合わせて更新されています。

コミット

commit bef9b1713a83f5d1722a2b01a73f2a6600fda43b
Author: Robert Griesemer <gri@golang.org>
Date:   Wed Nov 19 15:16:20 2008 -0800

    - removed uses of vector in favor of array in a few places
    - fixed make.bash
    
    R=r
    DELTA=21  (1 added, 3 deleted, 17 changed)
    OCL=19624
    CL=19629

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/bef9b1713a83f5d1722a2b01a73f2a6600fda43b

元コミット内容

- removed uses of vector in favor of array in a few places
- fixed make.bash

変更の背景

このコミットは、Go言語の非常に初期の段階(2008年11月)に行われたもので、言語のコアライブラリの設計がまだ流動的であった時期のものです。コミットメッセージにある「vectorの使用を廃止し、arrayに移行した」という記述は、Go言語が動的な配列のようなデータ構造をどのように扱うかについて、初期の試行錯誤があったことを示唆しています。

Go言語の最終的な設計では、C++のstd::vectorのような専用の「vector」型は導入されず、代わりに「スライス(slice)」という組み込み型が動的な配列の機能を提供することになりました。スライスは、固定長配列(array)の上に構築された軽量なデータ構造であり、配列の一部を「ビュー」として参照する形で機能します。このコミットは、おそらくGo言語がスライスという概念に落ち着く前の、vectorという名称の実験的なデータ構造から、より基本的なarray(そして将来的にはスライス)へと設計思想が移行していく過程の一部であったと考えられます。

make.bashの修正は、container/arrayパッケージがビルドプロセスに正しく含まれるようにするためのものであり、vectorパッケージが不要になったことと関連しています。

前提知識の解説

Go言語における配列(Array)とスライス(Slice)

Go言語において、**配列(Array)**は固定長で、同じ型の要素を連続して格納するデータ構造です。配列のサイズは型の一部であり、例えば[5]int[10]intは異なる型として扱われます。配列は値型であり、関数に渡される際には配列全体のコピーが作成されます。

一方、**スライス(Slice)**はGo言語における動的な配列の概念を提供します。スライスは、内部的には既存の配列を参照する「ビュー」として機能します。スライス自体は、基盤となる配列へのポインタ、スライスの長さ(現在含まれる要素数)、および容量(基盤配列がスライスの開始点から保持できる最大要素数)の3つのコンポーネントから構成されます。スライスはappend関数などによって動的にサイズを変更でき、必要に応じてより大きな基盤配列が自動的に割り当てられ、要素がコピーされます。

このコミットが行われた時期は、Go言語がまだvectorという概念を試していた段階であり、現在のスライスが確立される前の過渡期であったと推測されます。

vectorパッケージ(初期Go言語における)

このコミットで言及されているvectorパッケージは、現在のGo標準ライブラリには存在しません。これは、Go言語の初期開発において、動的なコレクションを扱うための実験的なデータ構造として存在していたものと考えられます。C++のstd::vectorのように、要素の追加や削除によってサイズが動的に変化する機能を提供していた可能性があります。しかし、Go言語の設計思想が固まるにつれて、このvectorパッケージは廃止され、よりGoらしいスライスという概念に置き換えられていきました。

技術的詳細

このコミットの主要な変更点は、src/lib/regexp/regexp.gotest/vectors.goの2つのGoソースファイルにおいて、vectorパッケージのインポートと使用をarrayパッケージに置き換えていることです。

src/lib/regexp/regexp.goの変更

正規表現エンジンを実装しているregexp.goファイルでは、以下の変更が行われています。

  1. インポートの変更: import ("vector";)import ("array";) に変更されています。これにより、regexpパッケージがvectorではなくarrayの機能を利用するようになります。

  2. RE構造体のinstフィールド: 正規表現の命令(instruction)を格納するRE構造体のinstフィールドの型が *vector.Vector から *array.Array に変更されています。 初期化時も re.inst = vector.New();re.inst = array.New(0); に変更されています。 命令の追加メソッドAddでは、re.inst.Append(i);re.inst.Push(i); に変更されています。これは、vectorAppendメソッドがarrayPushメソッドに相当することを示唆しています。

  3. CharClass構造体のrangesフィールド: 文字クラス(CharClass)の範囲を格納するrangesフィールドの型が *vector.Vector から *array.IntArray に変更されています。IntArrayarrayパッケージ内の整数専用の配列型であったと考えられます。 初期化時も c.ranges = vector.New();c.ranges = array.NewIntArray(0); に変更されています。 範囲の追加メソッドAddRangeでは、cclass.ranges.Append(a);cclass.ranges.Append(b);cclass.ranges.Push(a);cclass.ranges.Push(b); に変更されています。 要素へのアクセスも cclass.ranges.At(i).(int);cclass.ranges.At(i); に変更されています。これは、array.IntArrayが型アサーションなしで直接整数を返すようになったことを示しています。

test/vectors.goの変更

テストファイルであるvectors.goでは、vectorパッケージのインポートと使用がarrayパッケージに置き換えられています。

  1. インポートの変更: import vector "vector"import "array" に変更されています。

  2. テスト関数test0()test1(): v := vector.New();v := array.New(0); に変更されています。 v.Insert(0, a[i]); の行は変更されていませんが、これはarrayパッケージにもInsertメソッドが存在したか、あるいはこのテストがInsertの動作を検証するものではなかったため、変更が不要だった可能性があります。

src/lib/make.bashの変更

ビルドスクリプトであるmake.bashでは、以下の変更が行われています。

  1. builddirs関数の変更: ディレクトリをビルドするbuilddirs関数内で、cd $i; make install; cd .. という形式でサブディレクトリに移動してmake installを実行していた部分が、(cd $i; make install) というサブシェルで実行する形式に変更されています。これにより、現在のシェル環境のディレクトリが変更されずに済み、スクリプトの堅牢性が向上します。

  2. container/arrayの追加と削除: builddirsの呼び出しリストに container/array が追加され、その後、リストの末尾から container/array が削除されています。これは、container/arrayがビルド対象のライブラリとして一時的に追加されたものの、最終的にはregexpパッケージが直接arrayパッケージを参照する形になったため、container/arrayという独立したビルド対象としては不要になったことを示唆しています。あるいは、container/arrayarrayパッケージの内部的なパスであった可能性もあります。

コアとなるコードの変更箇所

src/lib/make.bash

--- a/src/lib/make.bash
+++ b/src/lib/make.bash
@@ -18,9 +18,7 @@ function builddirs() {
 	for i
 	do
 		echo; echo; echo %%%% making lib/$i %%%%; echo
-		cd $i
-		make install
-		cd ..
+		(cd $i; make install)
 	done
 }
 
@@ -35,6 +33,7 @@ builddirs	syscall\
 		math\
 		os\
 		strconv\
+		container/array\
 		reflect\
 	
 buildfiles	io.go
@@ -54,4 +53,3 @@ builddirs	net\
 		time\
 		http\
 		regexp\
--		container/array\

src/lib/regexp/regexp.go

--- a/src/lib/regexp/regexp.go
+++ b/src/lib/regexp/regexp.go
@@ -8,7 +8,7 @@ package regexp
 
 import (
 	"os";
-	"vector";
+	"array";
 )
 
 export var debug = false;
@@ -50,7 +50,7 @@ type RE struct {
 	expr	string;	// the original expression
 	ch	*chan<- *RE;	// reply channel when we're done
 	error	*os.Error;	// compile- or run-time error; nil if OK
-	inst	*vector.Vector;
+	inst	*array.Array;
 	start	Inst;
 	nbra	int;	// number of brackets in expression, for subexpressions
 }
@@ -123,8 +123,8 @@ type CharClass struct {
 	Common;
 	char	int;
 	negate	bool;	// is character class negated? ([^a-z])
-	// Vector of int, stored pairwise: [a-z] is (a,z); x is (x,x):
-	ranges	*vector.Vector;
+	// array of int, stored pairwise: [a-z] is (a,z); x is (x,x):
+	ranges	*array.IntArray;
 }
 
 func (cclass *CharClass) Type() int { return CHARCLASS }
@@ -135,8 +135,8 @@ func (cclass *CharClass) Print() {
 		print(" (negated)");
 	}
 	for i := 0; i < cclass.ranges.Len(); i += 2 {
-		l := cclass.ranges.At(i).(int);
-		r := cclass.ranges.At(i+1).(int);
+		l := cclass.ranges.At(i);
+		r := cclass.ranges.At(i+1);
 		if l == r {
 			print(" [", string(l), "]");
 		} else {
@@ -147,14 +147,14 @@ func (cclass *CharClass) AddRange(a, b int) {
 
 func (cclass *CharClass) AddRange(a, b int) {
 	// range is a through b inclusive
-	cclass.ranges.Append(a);
-	cclass.ranges.Append(b);
+	cclass.ranges.Push(a);
+	cclass.ranges.Push(b);
 }
 
 func (cclass *CharClass) Matches(c int) bool {
 	for i := 0; i < cclass.ranges.Len(); i = i+2 {
-		min := cclass.ranges.At(i).(int);
-		max := cclass.ranges.At(i+1).(int);
+		min := cclass.ranges.At(i);
+		max := cclass.ranges.At(i+1);
 		if min <= c && c <= max {
 			return !cclass.negate
 		}
@@ -164,7 +164,7 @@ func (cclass *CharClass) Matches(c int) bool {
 
 func NewCharClass() *CharClass {
 	c := new(CharClass);
-	c.ranges = vector.New();
+	c.ranges = array.NewIntArray(0);
 	return c;
 }
 
@@ -220,7 +220,7 @@ func (re *RE) Error(err *os.Error) {
 
 func (re *RE) Add(i Inst) Inst {
 	i.SetIndex(re.inst.Len());
-	re.inst.Append(i);
+	re.inst.Push(i);
 	return i;
 }
 
@@ -574,7 +574,7 @@ func (re *RE) DoParse() {
 func Compiler(str string, ch *chan *RE) {
 	re := new(RE);
 	re.expr = str;
-	re.inst = vector.New();
+	re.inst = array.New(0);
 	re.ch = ch;
 	re.DoParse();
 	ch <- re;

test/vectors.go

--- a/test/vectors.go
+++ b/test/vectors.go
@@ -6,7 +6,7 @@
 
 package main
 
-import vector "vector"
+import "array"
 
 
 type S struct {
@@ -21,7 +21,7 @@ func (p *S) Init(val int) *S {
 
 
 func test0() {
-	v := vector.New();
+	v := array.New(0);
 	if v.Len() != 0 {
 		panic("len = ", v.Len(), "\n");
 	}
@@ -34,7 +34,7 @@ func test1() {
 	\ta[i] = new(S).Init(i);\
 	}\
 
-\tv := vector.New();
+\tv := array.New(0);\
 	for i := 0; i < len(a); i++ {
 		v.Insert(0, a[i]);
 		if v.Len() != i + 1 {

コアとなるコードの解説

このコミットの核となる変更は、Go言語の正規表現エンジン(regexpパッケージ)が内部で利用する動的なデータ構造を、vectorからarrayへと切り替えた点にあります。

regexp.goファイルは、正規表現のパース、コンパイル、実行に関わるロジックを実装しています。この中で、正規表現の命令列を格納するRE構造体のinstフィールドや、文字クラスの範囲を格納するCharClass構造体のrangesフィールドが、動的に要素を追加・参照する必要があるデータ構造として使われていました。

変更前は、これらのフィールドは*vector.Vector型でした。vector.New()で新しいvectorインスタンスを作成し、vector.Append()で要素を追加し、vector.At()で要素にアクセスしていました。

変更後は、*array.Array型(または*array.IntArray型)に切り替わっています。これに伴い、インスタンスの作成はarray.New(0)array.NewIntArray(0)に、要素の追加はarray.Push()に、要素へのアクセスはarray.At()に変更されています。特にCharClass.rangesAtメソッドの呼び出しでは、型アサーション(int)が不要になっていることから、array.IntArrayがより型安全な整数専用の配列として設計されていたことが伺えます。

この変更は、Go言語の初期設計におけるデータ構造の選択と進化を示しています。vectorという概念が、よりGo言語の哲学に合致するarray(そして最終的にはスライス)へと洗練されていく過程の一端を垣間見ることができます。arrayパッケージが提供する機能は、後のスライスの基盤となる固定長配列の概念に近いものであったと考えられます。

make.bashの変更は、このデータ構造の変更に伴うビルドシステムの調整です。サブシェルでのmake install実行は、ビルドスクリプトの実行環境をクリーンに保つための一般的な改善であり、このコミットの主要な目的であるvectorからarrayへの移行をサポートするものです。

関連リンク

参考にした情報源リンク