Go言語を学びました

Hugoのサイトを制作する以上、少なくとも知っておいたほうが良いと思い、Progateでレッスンを受けました。(有料会員です!)
学んだときのGo言語の印象をメモします。

PHPとだいたい一緒

phpと文法も似ているように感じました。
...といっても私はプログラミング言語PHPしか触ったことがないので、他の言語との比較ができないのですが、敷居は低い印象を受けました。

以下にPHPと違うところを挙げます。

変数を定義する時にデータ型も定義する

変数を定義する時に同時にデータ型を定義する必要があります。
定義しない場合は自動的に判別されます。

hello := "こんにちは"
var hello string = "こんにちは" と同じ。

:= 便利です。しかもfor文の中で変数定義の際は、:= を使わないとエラーになります。

for i := 1;  i <= 4; i ++ {
  println(i)
} 
// var i int = 1 と書くとエラー。

インポートしたPackageを使わないとエラーになる

Goには標準パッケージという、便利な機能を備えたものがあります。

標準パッケージ 機能
fmt コンソールに出力する
math/rand 乱数を生成する
strings 文字列の連結、置換など
image 画像処理

もっとたくさんあります。

パッケージ - Go 言語

ほとんどよくわからないパッケージばかり...「敷居が低い」は撤回します。

パッケージはインポートして使います。

package main
import "fmt"  // mainパッケージの中で fmt パッケージをインポートする
import "math/rand" // mainパッケージの中で math/rand パッケージをインポートする

func main() {
    fmt.Println("Hello world")    // コンソールに「Hello world」と出力
}

// math/rand パッケージを使っていないのでこのままでは↑の出力もなくエラーが出る。

これはphpと違うと思ったのですがどうなんでしょうか(←自信がなくなっている)。

定義した変数を使わないとエラーになる

package main
func main() {
  a := 100
  b := 200    // 変数bは使われていないためエラー(aの出力もない)
  println(a)
} 

となります。

Progateでは

使っていない変数の存在はバグ(不具合)の原因となることが多いため、Goではエラーを発生させて、バグを未然に防ぐ設計になっています。

とあります。他の言語でもありそうな仕様ですね。

値渡しと参照渡しがわかった

「HugoはGo言語でできているから」で始めたProgateのGo言語ですが、結果、学んだところでHugoがよくわかるようになるというものではありませんでした。

しかし、スライドに出てきた「値渡し」「参照渡し」のおかげで、これまでjavascriptにも出てきたこの考えが少し分かりそうです。

メモリに記録されている変数のアドレス = ポインタ

Go言語ではポインタといいます。

C言語でも出てきます。

プログラム初心者にC言語のポインタを不本意ながら教える羽目になったなら、こう教えると良いよ - 偏見プログラマの語り!

メモリからバイトの話まで、大事そうな部分なので熟読したいところです!
「メモリ空間」は方眼紙をイメージして、その各マス目にバイトが並んでいる...。

バイトの理解も必要ですね。

図にするとこんな感じです。
(Progateのスライドを真似て画像作りました。著作権大丈夫かな)

f:id:yokoyoko_115:20200320033838j:plain

変数はメモリの一部分に登録されていて、変数の住所のように、登録場所である「アドレス(ポインタ)」を持っています。
ポインタはで、16進数で表される事が多いです。
ポインタを変数に格納する際はポインタ型変数に入れて使います。

name := "John"
var namePtr *string = &name  // ↑の画像でいうと 0xc420010230 が入る

ポインタ型変数はデータ型の前に*を付けて定義します。
そして変数のポインタは変数名の前に&を付けると取得できます。

変数の値を出力する方法として、ポインタから操作する方法があります。

fmt.Println(*namePtr)

ポインタを使った変数の値への操作は、ポインタ型変数名の前に*を付けます。

値渡しは変数の値のコピー

関数の引数で変数の値を渡すとき、関数の外で定義した変数は、関数内の変数とは別物です。
↓で変数 a が2つありますが、caluculateスコープの a は、mainスコープの a の値をコピーして代入されたものです。

package main
import "fmt"

func main() {
  a := 1
  calculate(a)
  fmt.Println(a)  // 1のまま。2にはならない
}

func calculate(a int) {
  a += 1  // 2になっている
}

関数の外で定義した変数を関数の中から変更したい場合、ポインタが使えます。

package main
import "fmt"

func main() {
  b := 1
  calculate(&b)  // 変数 b のポインタを引数にする
  fmt.Println(b)  // 2になる
}

func calculate(bPtr *int) {  // ポインタ型変数 bPtr に変数 b のポインタを渡す
  bPtr* += 1  // mainスコープの変数 b の値を更新している
}

ポインタを使えば、スコープを越えてどこからでも変数にアクセスできる、ということでしょうか。
便利な半面、危険なにおいがします。...でもポインタは変数に対して一意な値(おそらく)なので、phpグローバル変数みたいに変数名が被る危険性などは考えずに使えるのでしょうか。
ポインタのベストな使い所はおいおい。

javascriptの場合、オブジェクトは参照型

私がもやもやしていた部分はどこだったか思い出そうと確認したところ、
javascriptの書籍、『javascriptの教科書』にはこう書かれています。

オブジェクトは参照型で、オブジェクト型の値を変数に代入すると、変数にはそのオブジェクへの参照(メモリ上の場所情報)が格納されます。このとき、変数はそのオブジェクトを 参照しているといいます。

...よけいこじらせてしまったかもしれません。
「代入」と「格納」の違いは何でしょうか。

javascriptの場合は、オブジェクトを変数に代入した時点で、メモリ上の場所情報(アドレス)が変数に格納されているようです。

Javascriptでの考え方

少し調べたらとんでもない記事が。
これまで信じてきた常識を覆すタイトルですが、誇張表現ではあると書かれています。

JavaScriptに参照渡し/値渡しなど存在しない - Qiita

「値の参照」腑に落ちました!言葉の定義の問題かなと。

背景:  
 データ型の種類でプリミティブは「値渡し」、オブジェクトは「参照渡し」と言われてきている

・変数に値を代入すると"値の参照"が変数に入る
・変数に値を再代入すると"値の参照"が置き換わる
・変数に変数を代入するとその変数に入ってる"値の参照"が入る

「変数に値を再代入すると"値の参照"が置き換わる」というのがびっくりしました。Go言語では違うのでしょうか。または
ポインタ = 値の参照
ではないかもしれません。

「参照」深いです。
他の言語を学べば、参照について深く考えられそうです。

今回も思いっきり脱線しましたが、他の言語をかいつまんで学ぶのもいいですね。