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

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

このコミットは、Go言語の仕様書 doc/go_spec.html にビットクリア演算子 &^ および複合代入演算子 &^= の導入に関する変更を加えています。

コミット

commit cd04ec95ea0a7f31798889101a29bc20658b6b56
Author: Rob Pike <r@golang.org>
Date:   Wed Mar 11 21:59:05 2009 -0700

    bit clear: &^ and &^=
    
    R=gri
    DELTA=5  (3 added, 0 deleted, 2 changed)
    OCL=26155
    CL=26157

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

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

元コミット内容

このコミットは、Go言語に新しいビット演算子である「ビットクリア」演算子 &^ と、それに対応する複合代入演算子 &^= を導入するものです。具体的には、Go言語の仕様書 doc/go_spec.html を更新し、これらの演算子を文法、演算子の優先順位、および演算子の説明に追加しています。

変更点の内訳は以下の通りです。

  • doc/go_spec.html&^ および &^= を演算子リストに追加。
  • mul_op (乗算演算子) の定義に &^ を追加。これにより、&^ が乗算演算子と同じ優先順位を持つことを示唆。
  • 演算子の優先順位テーブルにおいて、優先順位6のグループ(*, /, %, <<, >>, &)に &^ を追加。
  • 演算子の説明セクションに &^ を「bitwise nand」(ビットごとのNAND)として追加。
  • 複合代入演算子の例として i &^= (1<<n) を追加。

変更の背景

Go言語は、システムプログラミングに適した言語として設計されており、ビット演算は低レベルな操作や効率的なデータ操作において非常に重要です。既存のビット演算子(& ビットAND, | ビットOR, ^ ビットXOR)に加えて、特定のビットをクリアする操作は頻繁に必要とされます。

ビットクリア操作は、一般的に x & (~y) のように、対象のビットパターンを反転させてからAND演算を行うことで実現できます。しかし、このパターンは冗長であり、可読性を損なう可能性があります。&^ 演算子を導入することで、この一般的な操作をより簡潔かつ直感的に記述できるようになります。これにより、コードの表現力が向上し、開発者がビット操作をより効率的に行えるようになることが背景にあります。

前提知識の解説

ビット演算

ビット演算は、数値の個々のビット(0または1)に対して直接操作を行う演算です。主に整数型に対して適用され、低レベルなプログラミング、ハードウェア制御、データ圧縮、暗号化、アルゴリズムの最適化など、様々な場面で利用されます。

  • AND (&): 両方のビットが1の場合にのみ1を返します。 例: 1010 & 1100 = 1000
  • OR (|): どちらか一方または両方のビットが1の場合に1を返します。 例: 1010 | 1100 = 1110
  • XOR (^): どちらか一方のビットが1で、もう一方が0の場合に1を返します。両方が同じ場合は0を返します。 例: 1010 ^ 1100 = 0110
  • NOT (~): ビットを反転させます(1を0に、0を1に)。Go言語では単項の ^ がビット反転(補数)として機能します。 例: ^1010 = 0101 (ただし、これは符号付き整数の表現に依存します)
  • 左シフト (<<): ビットを指定された数だけ左に移動させます。右側には0が埋められます。 例: 0010 << 2 = 1000
  • 右シフト (>>): ビットを指定された数だけ右に移動させます。左側に埋められるビットは、符号付き整数か符号なし整数かによって異なります(符号拡張またはゼロ埋め)。 例: 1000 >> 2 = 0010 (符号なしの場合)

ビットクリア操作

特定のビットを0に設定する操作を「ビットクリア」と呼びます。これは、ビットマスクを使用して行われます。例えば、数値 x の特定のビットをクリアしたい場合、そのビットが1であるマスク y を用意し、x & (~y) のように計算します。ここで ~yy のビットを反転させたものです。

例: x = 1011 (11) の2番目のビット(右から数えて0番目から始まる)をクリアしたい場合。 2番目のビットが1のマスクは 0100 (4) です。 ~01001011 (仮に4ビットで表現した場合) となります。 1011 & 1011 = 1011 となり、これは期待する結果ではありません。

正確なビットクリアは、クリアしたいビットが1であるマスク m を用意し、そのマスクを反転させたものと元の値をAND演算します。 例: x = 1011 (11) の2番目のビット(右から数えて0番目から始まる)をクリアしたい場合。 クリアしたいビットが1のマスク m = 0100 (4) ~m (ビット反転) は、例えば8ビット整数で考えると 11111011 となります。 x & (~m)00001011 & 11111011 = 00001011 となり、これも期待する結果ではありません。

Go言語の &^ 演算子は、x & (^y) と等価です。ここで ^yy のビット反転(補数)です。 つまり、x &^ yx のビットのうち、y の対応するビットが1であるものを0にする操作です。

例: x = 1011 (11) の2番目のビット(右から数えて0番目から始まる)をクリアしたい場合。 クリアしたいビットが1のマスク y = 0100 (4) x &^ y1011 &^ 0100 となります。 y の2番目のビットが1なので、x の2番目のビットを0にします。 結果: 1011 &^ 0100 = 1001 (9)

これは、x から y のビットが立っている部分を「取り除く」操作と考えることができます。

複合代入演算子

op= の形式で記述される演算子で、a op= ba = a op b と同じ意味を持ちます。例えば、x += 5x = x + 5 と同じです。これにより、コードをより簡潔に記述できます。

技術的詳細

このコミットは、Go言語の仕様に &^ および &^= 演算子を正式に導入するものです。

&^ 演算子の定義

Go言語の &^ 演算子は「ビットクリア」または「ビットごとのAND NOT」として機能します。これは x &^ y と書かれ、その結果は x のビットのうち、y の対応するビットが1であるものを0にし、y の対応するビットが0であるものは x のビットをそのまま残します。

数学的には x & (~y) と等価ですが、Go言語の ^ は単項演算子としてビット反転(補数)を意味するため、x & (^y) と表現されます。この演算子は、特定のフラグをクリアしたり、ビットマスクから特定のパターンを除外したりする際に非常に便利です。

演算子の優先順位

コミットでは、&^ 演算子を乗算演算子 (*, /, %, <<, >>, &) と同じ優先順位6に配置しています。これは、ビットAND (&) と同じ優先順位であり、ビット演算の一般的な慣習に沿っています。これにより、複雑なビット演算式でも意図しない結果になることを防ぎ、式の評価順序を明確にします。

複合代入演算子 &^=

&^=x &^= y の形式で使用され、x = x &^ y と同じ意味を持ちます。これは、変数の値を直接更新する際に簡潔な記述を可能にします。例えば、i &^= (1<<n) は、変数 in 番目のビットをクリアする操作を意味します。これは、i = i &^ (1<<n) と書くよりも簡潔で、ビット操作の意図をより明確に伝えます。

仕様書への反映

doc/go_spec.html はGo言語の公式仕様書であり、このファイルへの変更は、これらの新しい演算子が言語の正式な一部として認められたことを意味します。文法定義 (mul_op の更新)、演算子優先順位テーブル、および演算子の意味に関する説明が追加され、Go言語のユーザーがこれらの新しい機能を利用するための正確な情報が提供されます。

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

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -297,6 +297,7 @@ The following character sequences represent operators, delimiters, and other spe
 *    ^     *=    ^=     &lt;-    &gt;     &gt;=    {    }\n /    <<    /=    <<=    ++    =     :=    ,    ;\n %    >>    %=    >>=    --    !     ...   .    :\n+     &amp;^          &amp;^=\n </pre>\n \n <h3>Integer literals</h3>\n@@ -2416,7 +2417,7 @@ log_op     = \"||\" | \"&amp;&amp;\" .\n com_op     = \"&lt;-\" .\n rel_op     = \"==\" | \"!=\" | \"&lt;\" | \"&lt;=\" | \">\" | \">=\" .\n add_op     = \"+\" | \"-\" | \"|\" | \"^\" .\n-mul_op     = \"*\" | \"/\" | \"%\" | \"&lt;&lt;\" | \">>\" | \"&amp;\" .\n+mul_op     = \"*\" | \"/\" | \"%\" | \"&lt;&lt;\" | \">>\" | \"&amp;\" | \"&amp;^\" .\n \n unary_op   = \"+\" | \"-\" | \"!\" | \"^\" | \"*\" | \"&amp;\" | \"&lt;-\" .\n </pre>\n@@ -2460,7 +2461,7 @@ operators, comparison operators, communication operators,\n \n <pre class=\"grammar\">\n Precedence    Operator\n-    6             *  /  %  &lt;&lt;  >>  &amp;\n+    6             *  /  %  &lt;&lt;  >>  &amp;  &amp;^\n     5             +  -  |  ^\n     4             ==  !=  &lt;  &lt;=  >  >=\n     3             &lt;-\n@@ -2505,6 +2506,7 @@ to strings; all other arithmetic operators apply to integers only.\n &amp;    bitwise and     integers\n |    bitwise or      integers\n ^    bitwise xor     integers\n+&amp;^   bitwise nand    integers\n \n <<   left shift      integer << unsigned integer\n >>   right shift     integer >> unsigned integer\n@@ -3021,6 +3023,7 @@ x = 1\n *p = f()\n a[i] = 23\n k = <-ch\n+i &^= (1<<n)\n </pre>\n \n <p>\n```

## コアとなるコードの解説

このコミットは、Go言語の仕様書 `doc/go_spec.html` を直接編集することで、新しいビットクリア演算子 `&^` とその複合代入形式 `&^=` を言語に組み込んでいます。

1.  **演算子リストへの追加**:
    ```diff
    @@ -297,6 +297,7 @@ The following character sequences represent operators, delimiters, and other spe
     *    ^     *=    ^=     &lt;-    &gt;     &gt;=    {    }\n /    <<    /=    <<=    ++    =     :=    ,    ;\n %    >>    %=    >>=    --    !     ...   .    :\n+     &amp;^          &amp;^=\n     </pre>
    ```
    この変更は、Go言語で認識される演算子、区切り文字、その他の特殊文字のリストに `&^` と `&^=` を追加しています。これは、これらの新しい記号が言語の構文の一部として有効であることを宣言するものです。

2.  **`mul_op` (乗算演算子) の定義への追加**:
    ```diff
    @@ -2416,7 +2417,7 @@ log_op     = "||" | "&amp;&amp;" .\n com_op     = "&lt;-" .\n rel_op     = "==" | "!=" | "&lt;" | "&lt;=" | ">" | ">=" .\n add_op     = "+" | "-" | "|" | "^" .\n-mul_op     = "*" | "/" | "%" | "&lt;&lt;" | ">>" | "&amp;" .\n+mul_op     = "*" | "/" | "%" | "&lt;&lt;" | ">>" | "&amp;" | "&amp;^" .\n \n unary_op   = "+" | "-" | "!" | "^" | "*" | "&amp;" | "&lt;-" .\n     </pre>
    ```
    `mul_op` は、Go言語の文法における乗算演算子のグループを定義しています。ここに `&^` を追加することで、`&^` が `*`, `/`, `%`, `<<`, `>>`, `&` と同じ優先順位を持つことを文法的に示しています。これは、Go言語のパーサーが `&^` をどのように解釈すべきかを定義する上で重要です。

3.  **演算子優先順位テーブルの更新**:
    ```diff
    @@ -2460,7 +2461,7 @@ operators, comparison operators, communication operators,\n \n <pre class="grammar">\n Precedence    Operator\n-    6             *  /  %  &lt;&lt;  >>  &amp;\n+    6             *  /  %  &lt;&lt;  >>  &amp;  &amp;^\n     5             +  -  |  ^\n     4             ==  !=  &lt;  &lt;=  >  >=\n     3             &lt;-\n    </pre>
    ```
    この変更は、Go言語の演算子優先順位テーブルを更新し、`&^` を優先順位6のグループに明示的に追加しています。これにより、`&^` がビットAND (`&`) と同じ結合性を持つことが明確になり、式がどのように評価されるかがユーザーに示されます。

4.  **演算子の説明への追加**:
    ```diff
    @@ -2505,6 +2506,7 @@ to strings; all other arithmetic operators apply to integers only.\n &amp;    bitwise and     integers\n |    bitwise or      integers\n ^    bitwise xor     integers\n+&amp;^   bitwise nand    integers\n \n <<   left shift      integer << unsigned integer\n >>   right shift     integer >> unsigned integer\n    </pre>
    ```
    このセクションでは、各演算子の意味と適用可能な型を説明しています。`&^` を「bitwise nand」(ビットごとのNAND)として追加し、整数型に適用されることを明記しています。これは、`x &^ y` が `x` と `y` のビットごとのAND NOT演算であることを明確に定義しています。

5.  **複合代入演算子の例の追加**:
    ```diff
    @@ -3021,6 +3023,7 @@ x = 1\n *p = f()\n a[i] = 23\n k = <-ch\n+i &^= (1<<n)\n     </pre>\n \n <p>\n    </pre>
    ```
    この変更は、複合代入演算子の使用例として `i &^= (1<<n)` を追加しています。これは、変数 `i` の `n` 番目のビットをクリアする一般的な操作を簡潔に記述する方法を示しています。`1<<n` は、`n` 番目のビットのみが1であるビットマスクを生成します。

これらの変更は、Go言語の構文解析器、コンパイラ、および開発ツールが `&^` および `&^=` を正しく認識し、処理するために不可欠な基盤を築きます。また、開発者に対してこれらの新しい演算子の存在と使用方法を公式に伝える役割も果たします。

## 関連リンク

*   Go言語の公式ドキュメント: [https://go.dev/doc/](https://go.dev/doc/)
*   Go言語の仕様: [https://go.dev/ref/spec](https://go.dev/ref/spec) (このコミットで変更された `doc/go_spec.html` の最新版)

## 参考にした情報源リンク

*   Go言語のビット演算子に関する一般的な情報:
    *   [https://go.dev/tour/basics/13](https://go.dev/tour/basics/13) (Go Tour: Constants, includes bitwise operations)
    *   [https://www.geeksforgeeks.org/bitwise-operators-in-go/](https://www.geeksforgeeks.org/bitwise-operators-in-go/) (Third-party explanation of Go bitwise operators)
*   NANDゲートとビットごとのNANDの概念:
    *   [https://ja.wikipedia.org/wiki/NAND%E3%82%B2%E3%83%BC%E3%83%88](https://ja.wikipedia.org/wiki/NAND%E3%82%B2%E3%83%BC%E3%83%88)
*   Go言語のコミット履歴 (GitHub):
    *   [https://github.com/golang/go/commits/master](https://github.com/golang/go/commits/master)
*   Go言語の初期の設計に関する議論(Go言語のメーリングリストやデザインドキュメントなど、当時の情報源)
    *   Go言語の初期の設計に関する情報は、現在の公式ドキュメントやGitHubリポジトリのissue/pull requestの議論、またはGo言語のメーリングリストのアーカイブに散見されます。具体的なリンクはコミットメッセージには含まれていませんが、当時の設計決定の背景を深く理解するためにはこれらの情報源が有用です。
        *   [https://groups.google.com/g/golang-nuts](https://groups.google.com/g/golang-nuts) (Go Nuts メーリングリスト)
        *   [https://go.dev/issue/](https://go.dev/issue/) (Go Issues)
        *   [https://go.dev/design/](https://go.dev/design/) (Go Design Documents)