The Go Programming Language Specificationの翻訳、10回目です。
前回までの訳はGo言語仕様[日本語訳]にまとめてあります。
ステートメント
ステートメントは実行を制御します。
Statement = Declaration | LabeledStmt | SimpleStmt | GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt | FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt | DeferStmt . SimpleStmt = EmptyStmt | ExpressionStmt | IncDecStmt | Assignment | ShortVarDecl . StatementList = Statement { Separator Statement } . Separator = [ ";" ] .
ステートメントリスト(StatementList)の要素はセミコロンで区切られます。ただし直前のステートメントが次のときだけはセミコロンは省略可能です。
- 宣言リストが、閉じる丸括弧“)”で終わっているとき。もしくは、
- 閉じる波括弧“}”で終わっていて、それが式の一部ではないとき。
空ステートメント
空ステートメントは、何も行いません。
EmptyStmt = .
空ステートメントを加えることで、ステートメントリストをいつでも実質的にセミコロンで終了させることができます。
ラベル付きステートメント
ラベル付きステートメントは、goto
、break
、continue
ステートメントの宛先となります。
LabeledStmt = Label ":" Statement . Label = identifier .
Error: log.Fatal("error encountered")
式ステートメント
関数呼び出し、メソッド呼び出し、チャネル操作は、ステートメントの文脈内に記述することができます。
ExpressionStmt = Expression .
f(x+y) <-ch
インクリメント/デクリメントステートメント
“++”と“–”ステートメントは、型を持たない定数1
を使って、オペランドをインクリメントまたはデクリメントします。代入を伴うときは、オペランドは変数、ポインタ間接参照、フィールドセレクタ、インデックス式のいずれかでなければなりません。
IncDecStmt = Expression ( "++" | "--" ) .
以下の代入ステートメントは、同じ意味合いです。
Inc/Dec 代入 x++ x += 1 x-- x -= 1
代入
Assignment = ExpressionList assign_op ExpressionList . assign_op = [ add_op | mul_op ] "=" .
左辺の各オペランドは、アドレス指定可能か、マップのインデックス式、ブランク識別子のいずれかでなければなりません。
x = 1 *p = f() a[i] = 23 k = <-ch
代入操作 x
op=
y
において、opが二項算術演算子のとき、x
=
x
op y
と同等ですが、このときx
が評価されるのは一度だけです。また、このop=
はひとつのトークンを構成します。代入操作において、左辺、右辺双方の式リスト(ExpressionList)は、単一値となるひとつの式でなければなりません。
a[i] <<= 2 i &^= 1<<n
組み合わせ代入は、複数値となる操作の各要素を、変数リストに個別に代入します。これには2つの形式があります。1番目の形式では、右辺のオペランドは、関数の評価、チャネル、マップ操作、型アサーションのような、ひとつの複数値となる式です。左辺のオペランドの数は、右辺の値の数と一致しなければなりません。例えば、f
が2つの値を返す関数のときは、
x, y = f()
これは、1番目の値をx
に、2番目の値をy
に代入します。ブランク識別子を使うと、複数値となる式から返された値を無視することができます。
x, _ = f() // f()から返される2番目の値を無視
2番目の形式では、左辺のオペランドの数は、右辺の式の数と一致し、かつ各右辺の式はすべて単一値とならなければなりません。右辺のn番目の式は、左辺のn番目のオペランドに代入されます。このとき右辺の式が評価されるタイミングは、左辺のオペランドの何れかに代入が行われるより前に行われます。でなければ評価の順序規則から外れてしまうためです。
a, b = b, a // aとbを入れ替え
代入において、それぞれの値は代入先オペランドの型と代入の適合性を持たなければなりません。型を持たない定数をインタフェース型の変数へ代入するとき、その定数はbool
、int
、float
、string
型いずれかの値へ変換されます。どの型になるかは、値が論理値、整数、浮動小数点、文字列型定数のどれであるかに依存します。
ifステートメント
if
ステートメントは、2つに分岐したロジックを論理値型の式の値に従って実行します。式の評価結果がtrue
となるときは、if
側のロジックが実行されます。false
となるときに、else
が記述されていれば、それが伴うロジックが実行されます。条件が記述されなかったときはtrue
と記述したことと同等です。
IfStmt = "if" [ SimpleStmt ";" ] [ Expression ] Block [ "else" Statement ] .
if x > 0 { return true; }
シンプルステートメント(SimpleStmt)が、式(Expression)の直前にあるときは、式が評価されるより前にシンプルステートメントが実行されます。
if x := f(); x < y { return x; } else if x > z { return z; } else { return y; }
switchステートメント
switch
ステートメントは、複数に渡る分岐の実行を行います。どの分岐を実行すべきか判断するため、式または型指定と、switch
内のcase
とが比較されます。
SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .
switch
ステートメントには、式switch
と型switch
の2つの形式があります。式switch
のcase
には、switch
式の値と比較するための式が含まれます。型switch
のswitch
には、switch
式にて特殊な形式で示された型と比較するための型が含まれます。
式switch
式switch
においては、switch
の式が評価されたあと、case
式(定数である必要はない)が、左から右へ、上から下へと評価されていきます。switch
式と等しい最初のcase
が見つかると、それが伴うステートメントが実行され、それ以外のcase
はスキップされます。一致するcase
がないときにdefault
ケースがあれば、そのステートメントが実行されます。default
ケースは最大でも1つまでしか記述できませんが、switch
ステートメント内のどこにでも記述することができます。式が記述されなかったときはtrue
と記述したことと同等です。
ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" . ExprCaseClause = ExprSwitchCase ":" [ StatementList ] . ExprSwitchCase = "case" ExpressionList | "default" .
case
またはdefault
節内の最終ステートメントだけには、制御が次のcase
またはdefault
節の先頭ステートメントへと流れるべきであることを示す、fallthrough
ステートメント(§fallthroughステートメント)を記述することができます。これが記述されないときは、制御の流れはswitch
ステートメントの終わりへ移ります。
シンプルステートメント(SimpleStmt)が、式(Expression)の直前にあるときは、式が評価されるより前にシンプルステートメントが実行されます。
switch tag { default: s3() case 0, 1, 2, 3: s1() case 4, 5, 6, 7: s2() } switch x := f(); { case x < 0: return -x default: return x } switch { // 式が記述されていないので"true"として扱われる case x < y: f1(); case x < z: f2(); case x == 4: f3(); }
型switch
型switch
は、値の代わりに型を比較します。その他の点では、式switch
とほぼ同じです。型switch
は、予約語type
を型名の代わりとして使った型アサーションの形式を持つ特殊なswitch
式であることによって識別されます。case
はリテラルの型と、型アサーションの式が示す動的な型とを比較します。
TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" . TypeSwitchGuard = [ identifier ":=" ] Expression "." "(" "type" ")" . TypeCaseClause = TypeSwitchCase ":" [ StatementList ] . TypeSwitchCase = "case" TypeList | "default" . TypeList = Type { "," Type } .
型スイッチガード(TypeSwitchGuard)には、省略形式による変数の宣言を含むことができます。この形式が使われるとき、各case
/default
節内でその変数が宣言されます。リスト(TypeList)内に型をひとつだけ指定したcase
節では、この変数はcaseで
指定した型を持ちます。型を複数指定したときは、変数は型スイッチガードの式が表す型となります。
case
に指定する型をnil
(§事前宣言済み識別子)とすることもできます。このcase
は、型スイッチガード内の式がnil
インタフェース値であるときに選択されます。
下は、interface{}
型の値を返す関数f
を使った、型switch
です。
switch i := f().(type) { case nil: printString("f() returns nil"); case int: printInt(i); // i is an int case float: printFloat(i); // i is a float case func(int) float: printFunction(i); // i is a function case bool, string: printString("type is bool or string"); // i is an interface{} default: printString("don't know the type"); }
これは、下のように書き直すこともできます。
v := f(); if v == nil { printString("f() returns nil"); } else if i, is_int := v.(int); is_int { printInt(i); // i is an int } else if i, is_float := v.(float); is_float { printFloat(i); // i is a float } else if i, is_func := v.(func(int) float); is_func { printFunction(i); // i is a function } else { i1, is_bool := v.(bool); i2, is_string := v.(string); if is_bool || is_string { i := v; printString("type is bool or string"); // i is an interface{} } else { i := v; printString("don't know the type"); // i is an interface{} } }
シンプルステートメント(SimpleStmt)が、型スイッチガードの直前にあるときは、型スイッチガードが評価されるより前にシンプルステートメントが実行されます。
fallthrough
ステートメントは、型switch
においては許可されていません。
forステートメント
for
ステートメントは、ブロックの繰り返し実行を行います。繰り返しは、条件(Condition)、for節(ForClause)、range節(RangeClause)のいずれかによって制御されます。
ForStmt = "for" [ Condition | ForClause | RangeClause ] Block . Condition = Expression .
最も単純な形式のfor
ステートメントでは、条件の評価結果の論理値がtrue
となっている間、ブロックの実行を繰り返します。条件は繰り返しが行われる直前に毎回評価されます。条件が記述されなかったときはtrue
と記述したことと同等です。
for a < b { a *= 2 }
for節(ForClause)によるfor
ステートメントも条件(Condition)によってコントロールされますが、それに加えて、代入、インクリメント、デクリメントなどを行う初期化ステートメント(InitStmt)、またはポストステートメント(PostStmt)を記述できます。初期化ステートメントは省略形式による変数の宣言ですが、ポストステートメントはそうである必要はありません。
ForClause = InitStmt ";" [ Condition ] ";" PostStmt . InitStmt = SimpleStmt . PostStmt = SimpleStmt .
for i := 0; i < 10; i++ { f(i) }
初期化ステートメントが空でなければ、繰り返しの初回に条件が評価される直前に一度だけ実行されます。ポストステートメントはブロックを実行した直後、(ブロックが実行されたときだけ)実行されます。for節の各要素は空にすることができますが、条件だけを記述した場合を除き、セミコロンの記述が必要です。条件が指定されなかったときはtrue
と記述したことと同等です。
for cond { S() } is the same as for ; cond ; { S() } for { S() } is the same as for true { S() }
range節によるfor
ステートメントは、配列、スライス、文字列、マップ、チャネルから受信した値、これらの全エントリを使って繰り返しを行います。エントリ毎にまず、カレントのインデックスまたはキーをイテレーション変数に代入するか、もしくはカレントの「インデックスと要素のペア」または「キーと値のペア」をそれと対になるイテレーション変数に代入します。そのあとでブロックが実行されます。
RangeClause = ExpressionList ( "=" | ":=" ) "range" Expression .
range節の右側の式の型は、配列、スライス、文字列、マップ、配列へのポインタ、チャネルのどれかでなければなりません。チャネルのときを除いて、左辺の識別子リスト(ExpressionList)は、1~2個の式(代入と同じく、これらは変数、ポインタ間接参照、フィールドセレクタ、インデックス式のどれか)でなければなりません。各繰り返しにおいて、1番目のイテレーション変数にセットされるのは文字列または配列またはスライスのインデックス、マップのキーのいずれかであり、2番目のイテレーション変数があれば、それにセットされるのは1番目の変数と対応する文字列、配列要素、マップの値です。配列またはスライスのインデックスの型(これは常にint
)、要素の型、マップのキーおよび値の型は、対応するイテレーション変数に対して代入の適合性を持っていなければなりません。
文字列を扱うとき、range節は文字列内のユニコードポイントを繰り返します。連続する繰り返しの際、インデックス用変数にセットされる値は、文字列中のUTF-8エンコードされたコードポイントの連続したバイト列の先頭のインデックスであり、2番目のint
型の変数にセットされる値は、それと対応するコードポイントの値です。繰り返しの最中に無効なUTF-8シーケンスが現れたときは、2番目の変数は0xFFFD
(Unicode replacement character)となり、次の繰り返しのときに文字列内を1バイト進めます。
チャネルを扱うときは、識別子リスト(ExpressionList)には、識別子がひとつだけでなければなりません。ループはチャネルがクローズされるまでチャネル上に送られた値を受信し続けますが、チャネルがクローズされるまでは、送られたゼロ値は処理されません。
イテレーション変数が、range節(“:=”)によって宣言されるとき、この変数のスコープは、for
ステートメントの終了までです(§宣言とスコープ)。このときこれらイテレーション変数の型は、それぞれint
型と配列要素型のセット、もしくはマップのキーと値のセットのどちらかとなります。イテレーション変数がfor
ステートメント外で宣言されているとき、その変数の実行後の値は、最後に繰り返した状態です。
var a [10]string; m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}; for i, s := range a { // iの型はint // sの型はstring // s == a[i] g(i, s) } var key string; var val interface {}; // mの値の型は、valに対して代入の適合性がある for key, val = range m { h(key, val) } // key == マップの繰り返し内で最後に現れたエントリのキー // val == map[key]
繰り返し中に、まだ処理されていないマップエントリが削除されたときは、そのエントリが処理されることはありません。また繰り返し中に、マップエントリが追加されたときの動作は実装に依存しますが、ひとつのエントリが複数回処理されることはありません。
goステートメント
go
ステートメントは、独立した並列スレッドまたはゴルーチン(goroutine)として、関数またはメソッドの実行を同一アドレス空間で開始します。
GoStmt = "go" Expression .
この式(Expression)は、関数またはメソッドの呼び出しでなければなりませんが、通常の呼び出しとは異なり、プログラムは実行された関数の完了を待ちません。
go Server() go func(ch chan<- bool) { for { sleep(10); ch <- true; }} (c)
selectステートメント
select
ステートメントは通信可能な集合の中から、実行可能なものを選択します。switch
ステートメントに似ていますが、すべてのcase
で通信操作を行っている点が異なります。
SelectStmt = "select" "{" { CommClause } "}" . CommClause = CommCase ":" StatementList . CommCase = "case" ( SendExpr | RecvExpr) | "default" . SendExpr = Expression "<-" Expression . RecvExpr = [ Expression ( "=" | ":=" ) ] "<-" Expression .
select
ステートメント内のすべての送信・受信式において、チャネル式(送信式の右側の式も含めて)は、上から下へと順番に評価されます。select
ステートメントの結果としてcase
がひとつ以上実行可能となると、その内のひとつが選択され、それが伴う通信処理とステートメントが評価されます。どれも実行可能とならないとき、default
ケースがあれば実行されますが、default
ケースがないときは、通信のどれか1つが実行可能となるまで、ステートメントはブロックします。チャネルおよび送信式が複数回評価されることはありません。チャネルのポインタがnil
のときは、select
ステートメント内にそのcase
が存在しないことと同等ですが、送信のときは式だけは評価されます。
まず、select
ステートメント内のすべてのチャネルおよび送信式が評価されてから、その評価の二次的作用が通信に対して起こります。
実行可能なcase
が複数あるときは、平等かつ公平に選択が行われ、実行する通信がひとつだけ決定されます。
受信のcase
には、省略形式による変数の宣言を使って新しい変数を宣言することもできます。
var c, c1, c2 chan int; var i1, i2 int; select { case i1 = <-c1: print("received ", i1, " from c1\n"); case c2 <- i2: print("sent ", i2, " to c2\n"); default: print("no communication\n"); } for { // ランダムなビットシーケンスをcに送信 select { case c <- 0: // note: no statement, no fallthrough, no folding of cases case c <- 1: } }
returnステートメント
return
ステートメントは、それが記述されている関数の実行を終了し、必要であれば戻り値として単一または複数の値を呼び出し元に返します。
ReturnStmt = "return" [ ExpressionList ] .
戻り値を持たない関数のreturn
ステートメントは、戻り値を返してはいけません。
func no_result() { return }
戻り値を持つ関数から戻り値を返すには、3通りの方法があります。
- 戻り値として単一値または複数値が
return
ステートメントで明示的にリストされます。個々の式は単一値であり、かつ対応する関数の戻り値の型と、代入の適合性を持っていなければなりません。func simple_f() int { return 2 } func complex_f1() (re float, im float) { return -7.0, -4.0 }
return
ステートメントの式リストは、複数値を返す関数の(一回の)呼び出しです。これは関数から返されたそれぞれの値が、適切な型を持ったテンポラリの変数に代入され、その変数がreturn
ステートメントにリストされるかのように振る舞います。そのあとは、前のケースで説明した規則が当てはまります。func complex_f2() (re float, im float) { return complex_f1() }
- 関数の戻り値のパラメータに名前をつけているときは、
return
の式を空にすることができます(§関数型)。戻り値パラメータは、通常のローカル変数と同様に、その型のゼロ値(§ゼロ値)で初期化され、関数内で必要に応じて値の代入が行われます。return
ステートメントは、これら変数に格納されている値を返します。func complex_f3() (re float, im float) { re = 7.0; im = 4.0; return; }
breakステートメント
break
ステートメントは、最も内側にあるfor
、switch
、select
ステートメントの実行を終了します。
BreakStmt = "break" [ Label ].
ラベルが指定されているときは、そのラベルはfor
、switch
、select
ステートメントのどれかを伴っていなければならず、break
ステートメントによって、これらステートメントが終了します(§forステートメント、§switchステートメント、§selectステートメント)。
L: for i < n { switch i { case 5: break L } }
continueステートメント
continue
ステートメントは、最も内側のfor
ループのポストステートメントから次の繰り返しを開始します (§forステートメント)。
ContinueStmt = "continue" [ Label ].
オプションとして指定可能なラベルは、break
ステートメントのラベルと同じです。
gotoステートメント
goto
ステートメントは、指定したラベルを持つステートメントへ制御を移します。
GotoStmt = "goto" Label .
goto Error
goto
ステートメントの実行が原因で、それまでスコープ外だった変数が、スコープ内に入るようなケースは許されません。たとえば、この例などが相当します。
goto L; // BAD v := 3; L:
この例は、ラベルL
へジャンプすることで、変数v
の作成をスキップしてしまうため誤りです。
fallthroughステートメント
fallthrough
ステートメントは、式switch
(§式switch)の次のcase
節の先頭のステートメントへ制御を移します。このfallthrough
ステートメントが記述できるのは、式switch
内のcase
またはdefault
節の、空ではない最終ステートメントだけです。
FallthroughStmt = "fallthrough" .
deferステートメント
defer
ステートメントは、defer
ステートメントを記述している関数自体が復帰するまでの間、指定した関数の実行を先延ばしします。
DeferStmt = "defer" Expression .
defer
に指定する式は、関数またはメソッドの呼び出しでなければなりません。defer
ステートメントが実行される度に、関数呼び出しのパラメータは評価され、評価結果が保存されますが、関数の実行は行われません。保留された関数呼び出しは、defer
ステートメントを記述している関数から復帰する直前(戻り値があれば、その評価後)に、LIFOの順序で実行されます。
lock(l); defer unlock(l); // この関数から復帰する直前にunlockが行われる // この関数から復帰する直前に、3 2 1 0と出力される for i := 0; i <= 3; i++ { defer fmt.Print(i); }
Comments