見出し画像

Call-by-Value, for-range string, for-loop gives copy, Closure | Go日記

for-range で string をループすると byte ではなく rune を返す

s := "ありがとう"
for i, r := range s {
    fmt.Println(i, r, string(r))
}

0 123543 124266 123649 1239212 12358
What we are seeing is special behavior from iterating over a string with a for-range loop. It iterates over the runes, not the bytes.

Bodner, Jon. Learning Go (p.118). O'Reilly Media. Kindle 版.

for ループはコピーを提供する

package main
import (
	"fmt"
)
func main() {
	vals := []string{"a", "b", "c"}
	for _, v := range vals {
		v = "X"
		fmt.Println(v) // X
	}
	fmt.Println(vals) // [a b c]
}

Closure

package main
import (
	"fmt"
)
func main() {
	f := foo()
	fmt.Println(f("John"))     // Hi, John
	fmt.Println(foo()("Papa")) // Hi, Papa
}
type r func(string) string
func foo() r {
	foomt := "Hi, %v"
	return func(s string) string {
		return fmt.Sprintf(foomt, s)
	}
}

foo の中で foomt 変数の値を無名関数の中に閉じ込めて、関数を値として返している。

また、r 型は関数の形を定義しているので、foo() の戻り値のアノテーションとして置ける。見た目がスッキリする。

Go is Call by Value

『Learning Go』5. Function, Go is Call by Value ~ 6. Pointers の前半から抜書き。

Go is Call-by-Value language.
It means that when we supply a variable for a parameter to a function,

Go always makes a copy of the value of the variable.

...

If a pointer is passed to a function.
The function gets a copy of the pointer.
This(the copy of the pointer) still points to the original data, which means that the original data can be modified by called function.

Go は関数への値を渡すとき Call-by-Value で一貫している言語。

ポインターの Value はなにかといえばアドレスである。
Go でポインターを渡すということは、呼ばれた関数の中でポインターの指す値が変更されることを明示的に示している。
ポインター引数があれば、それは「変更するぞ」という宣言なので、破壊的に変更されることを前提としてその関数を使用する。
関数を書くときにポインター引数を設置するならば、それはそのポインターの指すオリジナル値を変更するという意思表示とする。

Go の場合は、何を変更して、何を変更しないかを明示的に意図してコーディングしていく。ある変数がどの時点で生まれて、どのタイミングで変更され、どこで寿命が尽きるのかを見極める必要がある。Go はそれが見極められるような言語仕様になっている(と、今のところ感じる。実務で使いだして実感できるかはあとで検証する)。当然変数のスコープがでかくなると追いきれなくなる。変数の受け持つスコープの範囲も、より小さくなるように、もしくは、より単純になるように、見極めながらコーディングすることが重要。

Sample code:

SN

参考書籍


この記事が気に入ったらサポートをしてみませんか?