前回のあらすじ
以外とやる気があった。プログラミング言語を1から学ぶのは結構楽しい
1. Go 構造体
プログラミング知識が一切ない10年くらい前に授業で受けた記憶しかない構造体
久しぶりに見てClassと何が違うのか、と思って調べたらGoにはそもそもClassが無いらしい。マジ?
フィールドしか使えないClassだと思って勉強を進めてみることにする。
オブジェクト指向が出来ないので、ややこしくなりそうだけどこれはこれでいい勉強になりそう。
宣言もまあ普通の型宣言と余り変わらない。
// 同じ型なら1行で
type Hoge struct {
a, b int
}
// 違う型なら行を変える
type Fuga struct {
X int
Y string
}
// 宣言方法
var s0 Hoge = Hoge{1, 2} // 普通
var s1 = Hoge{2, 4} // ちょっと省略
s2 := Hoge{3, 6} // 超省略 func内で書くとき
s3 := {4, 8} // ちなみにこれはエラー
class と struct の違い
GO言語でクラスっぽいことをする
2. Go Map
今まさに学んだstructも値のデータ型として定義可能
ここは他言語と余り変わらなさそうかな?
var map1 = map[キーのデータ型]値のデータ型{}
map2 := map[キーのデータ型]値のデータ型{}
// struct sample
type Hoge struct {
a int
str string
}
var map3 = map[string]Hoge{
"hoge": Hoge{ 1 , "2" },
}
3. Go Function戻り値
ここはVBライクでかけるからとっつきやすかった。
複数の型を纏めて返せるのは態々専用のクラスを作らなくて済むので便利。
structもfunctionも戻り値に指定できるのはありがたいところ
// 関数名(引数名 引数の型) 戻り値の型
func hoge(str string) string {
}
// 関数名(引数名 引数の型) (戻り値の型1, 戻り値の型2)
func hoge(str string) (string, string) {
}
4. Go クロージャ
それ自身の外部から変数を参照する関数値です。 この関数は、参照された変数へアクセスして変えることができ、その意味では、その関数は変数へ"バインド"( bind )されています。
説明だとイマイチわからないけど動かしてみるとadder関数内の変数sum
はずっと値を持ち続けている。
何度も処理をし続ける且つ前回の処理の値を保持しておきたい等にクロージャを使って処理すると良いのか?
クロージャを使ったことがないので余りわからず。有識者の型教えてください。
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main(){
pos := adder()
pos(1) // sum = 1
pos(2) // sum = (1) + 2
pos(3) // sum = (1 + 2) + 3
}
A Tour of Go (Function closures)
5. Go スライス
配列をListみたいに可変長で使いたい時に使うものとして理解。実際はデータを格納しているわけではなく、元となる配列アドレスを見に行っているっぽい。
スライスを使うときはそこを気を付けて実装する必用がある。
// 例
names := [4]string{
"John",
"Paul",
"George",
"Ringo",
}
a := names[0:2]
b := names[1:3]
// この処理でnames[1], a[1], b[0]全てが更新される
b[0] = "XXX"
A Tour of Go (Slices are like references to arrays)
6. Go メソッド
Classが無い変わりに定義した型に対して関数を紐づけられる。型自体のメンバとして関数を使えるようにできるので処理フローがわかりやすくなる。
構造体をClassのように運用したいとき等に使うと良さそう。通常のメソッドで引数として渡すよりは、instance内のprivate関数を呼び出すような形の方が色々良いはず。
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// 引数も定義できる
func (v Vertex) addAll(f float64) float64 {
return v.X + v.Y + f
}
func main() {
v := Vertex{3, 4}
// 呼び出し処理は他言語のクラス内関数を呼び出しているのに近い
fmt.Println(v.Abs())
fmt.Println(v.addAll(2))
}
7. Go インタフェース
これまじで分からん。
参考記事を見てようやくわかるようなわからないようなというレベルに。
勉強していけばわかるのかな。わかるようになったら記事を書きたい。
参考記事とか下記の動作確認用のサンプルのパターンになるシチュエーションが思いつかないので想像力が足りていないのかも。
一応hoge関数を大量定義しないで1つの関数に纏められるから省コードにはなるのか?
これを
// 型定義
type Type1 struct{ ... }
type Type2 struct{ ... }
// 型はそれぞれmethodXという同名だが違う処理を持つ
func (t Type1) methodX() string {
return "piyo"
}
func (t Type2) methodX() string {
return "fuga"
}
// 型が違うだけで処理が変わらない関数hoge, hoge2を定義
func hoge(t Type1){
x:= t.methodX()
...
}
func hoge2(t Type2){
x:= t.methodX()
...
}
func main(){
...
var t1 Type1
var t2 Type2
...
hoge(t1)
hoge2(t2)
}
こうする。
// interface定義
type If interface {
methodX() string // ここはメソッド定義と同じ型名、引数、戻り値を記載
}
// 型定義
type Type1 struct{ ... }
type Type2 struct{ ... }
// method定義
func (t Type1) methodX() string {
return "piyo"
}
func (t Type2) methodX() string {
return "fuga"
}
// インタフェースでhogeをひとくくりに
func hoge(itf If) string {
return itf.methodX()
}
func main(){
...
var t1 Type1
var t2 Type2
...
// t1,t2はともにItfのメンバであるmethodXをもっているので引数として渡せる。
hoge(t1)
hoge(t2)
}
8. Go 空のインタフェース
TypeScriptでいうところのany型
で変数を定義したいときに利用する。
(正直そんな未知の型が来る状況は避けるコーディングをしたいところではあるが、外部システムと接続する時に相手が何を送ってくるか分からないというようなケースに使えるか?)
var itf interface{}
itf = 0
itf = "hoge"
9 Go ルーン
シングルクォートとダブルクオートで文字列比較をしようとして型不一致エラーが起きたことで気づく。
rune型の説明を読んでみる
type rune = int32
rune is an alias for int32 and is equivalent to int32 in all ways. It is used, by convention, to distinguish character values from integer values.
訳:
runeはint32のエイリアスであり、すべての点でint32と同等である。慣例として、文字値と整数値を区別するために使用される。
まったく分からん。参考記事を探して読んでみる。
ひとつの文字は複数byteで表現される可能性があり、文字を表すbyteをまとめて読まないと正しい文字として認識できなくなってしまう。文字列の処理をUTF-16(つまり2byte単位)で行うJavascriptでは絵文字のような4byte文字にインデックスでアクセスしようとすると正しい文字を得ることができない。
そのため文字を数える単位としてはbyteではなくcode pointのほうが都合がいい。そしてGoではcode pointを単位として文字を扱うための仕組み、つまりruneを用意している。
要はUTF-8 / UTF-16 でByte表現を扱うと色々面倒なことが起きるからCodePoint値で文字を扱うための仕組みってことかな。ちがってたらスマソ
var r rune = 'r'
r := 'r'
fmt.Println(r) // 114 が出力される
fmt.Println(string(r)) // rが出力される
所感
色々知識が増えてきた。
ついでにPaizaでBランク問題を解いてきた。
頭の中で作ってるロジックをコードに落とすのに思ったよりも時間がかかることが敗因
Week3は学んだことの整理と計算量周りに目を向けて学んでいこうと思う。