42

Google Go で、文字列は不変であると読みましたが、わかりましたが int ですか? 他のタイプはどうですか?少し年上のプログラマーとして、私は不変性の利点を知っていても可変性を好み、危険な生活を好みます。

どのタイプが可変または不変であるかを知ることは非常に役立ちます。


更新、私が最も心配しているのは、型が可変か不変かによって決まる実際的な問題です。Java の典型的な例のように、ループ内で String を作成し、10,000 回ループすると、10,000 個の String が作成され、後でガベージ コレクションされます。これは、実際に私が働いていた会社のプロジェクトで深刻な問題でした。

問題は、Go の不変性が場合によっては同じ問題を引き起こすかどうかです。

変数の扱い方に影響します。(または、そうだと思います)。


もう一度更新します。他の実際的な問題についても心配しています。何かが不変であることを知っているということは、並列であり、オブジェクトの 1 つの参照を更新しても他の参照を更新しないコードを書くことができるということです。しかし、時には危険なことをしたい、可変性が欲しい。

これらは、可変性と不変性の結果であり、コードの記述方法に影響します。

4

5 に答える 5

45

心配しないでください -- 本当にやりたいのであれば、Go は自分の足を撃つこともできます :-)

Go は Erlang とは異なります。これは、質問で得ているものかもしれません。

x := 1
x = 2

xの値を持つ1 つの変数 を1割り当て、それを に再割り当てし2ます -- ここでは追加のメモリは割り当てられません。

お気づきのように、文字列は不変であるため、文字列操作を行うとコピーが作成される可能性があります。文字データをその場で変更したい場合は、おそらくパッケージを[]byte介して変数を操作したいと思うでしょう。bytes

これに関する Russ Cox の投稿は、基本的なデータ構造に関するほとんどの質問に答えるはずです: http://research.swtch.com/2009/11/go-data-structures.html

他のコメンターが指摘したように、Go 関数の値のセマンティクスを確認する必要があります。最初は少し驚くかもしれません。

次の機能がある場合:

func (t MyType) myFunc() {
    // do something to set a field in t
}

そして、あなたはあなたのコードを呼び出します

myVar.myFunc()

tで見られるmyFunc()は実際には のコピーであるため、これが思いどおりにならないことに驚くかもしれませんmyVar

ただし、次のよう動作します。

func (t *myType) myFunc() {
    // do something to set a field in t
}

関数にはポインターのコピーがあり、そのポインターを介して基になる構造体にアクセスできるためです。

于 2011-11-05T17:12:41.160 に答える
12

私の意見では、まず次の 2 つの概念を分離する必要があります。

  • 数学オブジェクトとしての整数 (つまり、値)

  • タイプの変数int

答えは次のとおりです。整数変数は可変であり、整数値は不変です。

このビューは、文字列が不変であると述べている Go 仕様と一致しています。明らかに、文字列変数は変更可能です。

Go の変数 (概念として) は、少なくとも次のとおりです。

  • 名前付き変数 (例: var i int)
  • ポインタ経由でアクセス可能な変数

変更可能な Go オブジェクト:

  • 配列とスライス
  • マップ
  • チャネル
  • 外側のスコープから少なくとも 1 つの変数をキャプチャするクロージャ

不変の Go オブジェクト:

  • インターフェイス
  • ブール値、数値 ( type の値を含むint)
  • 文字列
  • ポインタ
  • 関数ポインタ、および関数ポインタに還元できるクロージャ
  • 単一のフィールドを持つ構造体

変更可能と考える人もいれば、不変と考える人もいる Go オブジェクト:

  • 複数のフィールドを持つ構造体
于 2011-11-05T15:14:42.623 に答える
2

「可変性」は、複合型、つまり「内部」部分を持ち、それを含むものとは独立して変更できる可能性があるものについて話す場合にのみ意味があります。文字列は自然に文字で構成されており、新しい文字列全体を割り当てる以外に、既存の文字列の文字を変更できるメカニズムが言語にないため、不変であると言います。

int の場合、可変性について話すのはあまり意味がありません。なぜなら、int の「コンポーネント」とは何でしょうか? まったく新しい int を割り当てることによって int を変更しますが、割り当ては「変更」とは見なされません。

可変性と参照対値型の問題の間には、いくつかの関連があります。意味的には、不変参照型と値型の間に違いはありません。なんで?int が実際には不変オブジェクトへのポインターであるとします (つまり*InternalIntObject、変更するための関数はありません)。InternalIntObject)。このようなポインターを変数に割り当てると、オブジェクトは不変であるため、同じ整数値を永久に表します (同じオブジェクトを共有する他のユーザーが変更することはできません)。これは整数値型と同じ動作です。代入演算子によって int を割り当てることができます。同様に、代入によってこれらのポインターを割り当てることができます。結果は同じになります。割り当てられた変数は、割り当てられたものと同じ整数を表します。唯一の違いは、結果を計算するためにポインターを逆参照するために、比較と算術演算子を再定義する必要があることです。

したがって、可変性は参照型に対してのみ意味があります。

あなたが尋ねたことに関しては、「変更可能な」型は一般に、文字列を除く参照型と見なされます:マップ、チャネル、スライス(スライスが指すデータに関して)、および何かへのポインター(変更できるため)ポインターが指す位置の値)。

于 2011-11-05T09:08:43.420 に答える
2

はい、不変という言葉は Go 仕様で一度だけ出てきます。そして、それは議論するときtype stringです。AssignabilityAddressabilityの 2 つの観点からもっと検討する必要があると思います。たとえば、Go は明らかに、エクスポートされていないプロパティを持つ型の別の値に変数を再バインドすることを禁止します。コピー コンストラクターを提供しないクラスの C++ のようなものですが、Go Pimplでは、哲学を伝えることでゴルーチンのシェアにふさわしく、ぎこちなく感じます。

于 2011-11-05T05:35:12.543 に答える
1

Your concern seems to be more about allocation than immutability. Immutability certainly impacts allocation by making it impossible to reuse memory. A clever compiler could conceivably reuse any "immutable" memory whose address it knows doesn't escape.

Aside from strings, be careful with interfaces. Anything larger than word size will have to be allocated when assigned to the interface (optimizations aside). Also, variables declared in a loop body whose addresses escape, including via a closure, will have to be allocated each time through the loop. Otherwise, though, an assignment is just an assignment. The value just gets copied into the memory represented by the variable.

If you use make or new in a loop, or any literal that produces a reference, allocation will have to happen (again, subject to optimization).

Basically, it all boils down to trying to reuse memory where you can, and hoping the compiler does it for you when you can't, if it makes any sense to do so.

于 2011-11-06T05:11:57.823 に答える