Go言語でGO(勉強記録2)
Go言語でGOシリーズの2回目です。
基本文法の続き
RANGE反復
for文とrangeを組み合わせると、いわゆるforeachができるようです。
forの継続条件部分で、2つ変数を定義して、右辺でrange [配列]とする感じ。
定義する変数の1つめは配列番号、2つめは値が入ります。
func main() {
array := [...]string{"犬", "猫", "モルモット"}
for i, v := range array {
fmt.Println(i, v)
}
}

ちなみにrangeの左辺の変数を1つにすると、配列番号だけ取れます。
値ちゃうんかい!!
func main() {
array := [...]string{"犬", "猫", "モルモット"}
for a := range array {
fmt.Println(a)
}
}

値だけ取りたいときは、配列番号が入る変数名を _
にしましょう。
Pythonと同じですね。
package main
import "fmt"
func main() {
array := [...]string{"犬", "猫", "モルモット"}
for _, a := range array {
fmt.Println(a)
}
}
IF文の簡易ステートメント
if文の条件部の前に、処理を書くことができます。
func main() {
if yama := "山"; yama == "山" {
fmt.Println("yama is", yama) // yama is 山
}
}
スコープはifのブロック内に限ります。
func main() {
yama := "川"
if yama := "山"; yama == "山" {
fmt.Println("yama is", yama) // yama is 山
}
fmt.Println("yama is", yama) // yama is 川
}
ifのブロック内に限るとはいえ、外部の変数を上書きしたら反映されますけどね。
(IF文の簡易ステートメントの :=
を =
に変えた)
func main() {
yama := "川"
if yama = "山"; yama == "山" {
fmt.Println("yama is", yama) // yama is 山
}
fmt.Println("yama is", yama) // yama is 山
}
マップ
連想配列。よく使いそう。
よく使いそうだからか、作り方がたくさんあるみたいで、サイトによって書き方が違うのが非常に厄介です。
でも、使うのは多分2番目かな。
1.長いやつ
たぶん一番長いけどシンプルなやつ。
var 変数名 map[キーの型]値の型 = map[キーの型]値の型{}
func main() {
var pets map[string]string = map[string]string{}
fmt.Println(pets) // map[]
pets["dog"] = "犬"
fmt.Println(pets) // map[dog:犬]
}
2.短いやつ
1の書き方ができるなら、 := を使う書き方もできるはず。
できます。
変数名 := map[キーの型]値の型{}
func main() {
pets := map[string]string{}
fmt.Println(pets) // map[]
pets["dog"] = "犬"
fmt.Println(pets) // map[dog:犬]
}
3.makeを使って短縮
スライスのところでもチラッと出てきたmakeを使うやつ。
makeの構文は make(作る構造体, 要素数) だったので、これをスライスに応用すると、こうなる。
func main() {
pets := make(map[string]string)
fmt.Println(pets) // map[]
pets["dog"] = "犬"
fmt.Println(pets) // map[dog:犬]
}
,要素数 の部分は、あってもなくてもいいみたい。
たぶん事前に要素数を予約しておけば、スムーズにアイテムを追加できるんだと思います。
4.最初から初期値を放り込む
1と2に出てきた意味深な{}、この中に値を入れれば初期値になるのはご承知の通り。
func main() {
pets := map[string]string{"dog": "犬", "cat": "猫"}
fmt.Println(pets) // map[cat:猫 dog:犬]
pets["dog"] = "オオカミ"
fmt.Println(pets) // map[cat:猫 dog:オオカミ]
}
その他
存在しない要素を参照しようとすると、空文字が帰ってきます。
ランタイムエラーにはなりません。
func main() {
pets := map[string]string{"dog": "犬", "cat": "猫"}
fmt.Println(pets["dog"]) // 犬
fmt.Println(pets["bird"]) // (空文字)
}
構造体
いわゆるクラスの概念っぽい何か、らしい。
Goにはオブジェクト指向言語でいうクラスは存在しないとのこと。
構造体の定義
type 構造体名 struct{…}
の形。
package main
type pet struct {
english string
japanese string
cry string
}
func main() {
}
初期化
オブジェクトを生成する、に相当するやつ
1.varで変数を定義してからフィールドに入れる
シンプルやね
package main
import "fmt"
type pet struct {
english string
japanese string
cry string
}
func main() {
var dog pet
dog.english = "Dog"
dog.japanese = "犬"
dog.cry = "(∪^ω^)わんわんお!"
fmt.Println(dog.english, dog.japanese, dog.cry)
// Dog 犬 (∪^ω^)わんわんお!
}

しかしこのやり方だと、ポインタが生成されない?様子。
構造体を作る専用関数(コンストラクタ関数)を仕立てるときに、よく使うっぽい。
コンストラクタ関数は戻り値として、生成した構造体のポインタを返してやるものっぽい。
(2番目の生成方法だとポインタが戻ってくる)
package main
import "fmt"
type pet struct {
english string
japanese string
cry string
}
func newPet(english string, japanese string, cry string) *pet {
// var pet pet
// pet.english = english
// pet.japanese = japanese
// pet.cry = cry
// // ここまでを1行でまとめると、↓になる
pet := pet{english: english, japanese: japanese, cry: cry}
return &pet
}
func main() {
dog := newPet("Dog", "犬", "わんわんお")
fmt.Println(dog) // &{Dog 犬 わんわんお}
}
newPetという関数で、petを作って、そのポインタを返すようにしてあげた。
2.newを使う
Javaっぽい。
package main
import "fmt"
type pet struct {
english string
japanese string
cry string
}
func main() {
cat := new(pet)
cat.english = "cat"
cat.japanese = "猫"
cat.cry = "にゃーん"
fmt.Println(cat) // &{cat 猫 にゃーん}}
// newだと1行で初期化できないが、↓だと1行で初期化できる
human := &pet{"human", "ヒト", "にゃーん(社会性フィルター)"}
fmt.Println(human) // &{human ヒト にゃーん(社会性フィルター)}
}
メソッド
クラスじゃないけど、メソッドが定義できる。
ただ構造体の中にfuncを書くのではなく、「レシーバ引数」というのに小細工をする形になります。
package main
import "fmt"
type pet struct {
english string
japanese string
cry string
}
// ↓ここがレシーバ
func (p pet) crying() string {
p.cry = "ワン!"
return p.cry
}
func main() {
human := &pet{"human", "ヒト", "にゃーん(社会性フィルター)"}
fmt.Println(human) // &{human ヒト にゃーん(社会性フィルター)}
fmt.Println(human.crying()) // ワン!
fmt.Println(human) // &{human ヒト にゃーん(社会性フィルター)}
}
上のように書くと、crying()へは構造体を値渡しするようです。
(humanのPrintln結果が変わってない)
参照渡しするには、レシーバ部分の型名の頭に*
をつけます。
package main
import "fmt"
type pet struct {
english string
japanese string
cry string
}
// ↓ここに*をつける
func (p *pet) crying() string {
p.cry = "ワン!"
return p.cry
}
func main() {
human := &pet{"human", "ヒト", "にゃーん(社会性フィルター)"}
fmt.Println(human) // &{human ヒト にゃーん(社会性フィルター)}
fmt.Println(human.crying()) // ワン!
fmt.Println(human) // &{human ヒト ワン!}
}
ちなみに、上の例ではhumanはポインタが格納されている(いわゆる*T型)のですが、レシーバが値レシーバ(*なし)だと、勝手に値のコピーを取って渡す挙動をするようです。
型は暗黙変換してくれないけれど、なぜかレシーバは暗黙変換するみたい。
ポインタレシーバと値レシーバ、どっちがいいんだってのは、ここを読みましょう。
Javaやってた身としては、ポインタ渡しのほうがスッキリするような…いやそうでもないのか…?
埋め込み
Goにはクラスがないので当然だけれど、継承という概念もない。
構造体には継承という概念がない。
親構造体とか子構造体なんてものは、ない。
似たようなことはできる。それが「埋め込み」。
package main
import "fmt"
type human struct {
name string
}
type worker struct {
human
job string
}
func (h *human) say() string {
return "My name is " + h.name
}
func main() {
you := &human{"Tanaka"}
me := &worker{}
me.name = "Sato"
me.job = "SE"
fmt.Println(you.say()) // My name is Tanaka
fmt.Println(me.say()) // My name is Sato
}
見栄え上はhumanの子構造体がworkerだけれど、そんな概念はない(is-aの関係でしかない)。
でも、まあ、humanをレシーバとする関数をworkerが呼んでも上手くいくみたいなので、それっぽい使い方はできそう。
ディスカッション
コメント一覧
まだ、コメントがありません