GoLang

第2回「プログラミング言語Go」オンライン読書会

雨ですね。九州地方のかた大丈夫でしょうか。。

昨日、IntelliJ IDEAもっているお友達にワンツーマンでTypescriptのライブコーディングしてもらったんだけど、ライブコーディング用のプラグインとか、日本語を自動で英語の変数に変換してくれるプラグインとかが素晴らしすぎて、GoLand期限きれちゃったしなー(最近vs codeで書いている)、Flutterもできるし、どうしよう、全部いり買っちゃおうか!という気になっている。

本日は 第2回『プログラミング言語Go』オンライン読書会 でした。

  • 見知った方が何人か🥳
  • 適当ですが、メモをとった。

最初に

  • 自己紹介

そして前回の続きから...。読書会後に自分で調べたことも書いてあるので、間違ってかいてあるとこありましたらごめんさい。

第1章チュートリアル

1.6 URLからの並行な取得

P.22 gopl.io/ch1/server1

func main() {
	http.HandleFunc("/", handler) // each request calls handler
	log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
  • ListenAndServeは、エラー以外返さない。
  • ので、Fatalにしてる。

P.23 gopl.io/ch1/server2

  • コードを実行すると、ブラウザーで Count 2 ってでる。
  • なんでか分かる人ー?
func main() {
	http.HandleFunc("/", handler)
	http.HandleFunc("/count", counter)
	log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
	mu.Lock()
	count++
	mu.Unlock()
	fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}
func counter(w http.ResponseWriter, r *http.Request) {
	mu.Lock()
	fmt.Fprintf(w, "Count %d\n", count)
	mu.Unlock()
}
  • ブラウザーによって、faviconを取りに行くのがあるらしい。
  • よってハンドラーが余分に呼び出されてカウントしちゃう。

curlで動かしてて気づかなかったー、とコメントしている人がいた。

P.27 型宣言と名前付き型っていまいわない

  • defined type って名前に変わった。
  • 定義済の型?
  •  ポインタへの演算はない。

P.28 go doc に ハイフンsrc をつけると...

これはすごい、ごいすー

🎁 go doc -src http.ListenAndServe
package http // import "net/http"

// ListenAndServe listens on the TCP network address addr and then calls
// Serve with handler to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// The handler is typically nil, in which case the DefaultServeMux is used.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}
~ via C base 
🎁

ソースコードが飛び出してくる...

普通に打つと

🎁 go doc http.ListenAndServe
package http // import "net/http"

func ListenAndServe(addr string, handler Handler) error
    ListenAndServe listens on the TCP network address addr and then calls Serve
    with handler to handle requests on incoming connections. Accepted
    connections are configured to enable TCP keep-alives.

    The handler is typically nil, in which case the DefaultServeMux is used.

    ListenAndServe always returns a non-nil error.

~ via C base 
🎁

 

第2章 プログラム構造

int とか変数にできちゃう

  • これはあかん
  • int := false とかかけちゃう

参照型が言語仕様から消えた

P.32

「インターフェースと参照型(スライス、ポインタ、・・・」という記述があるが、言語仕様がかわり、参照型という表現はなくなった

go has no ’reference types’

  • 2013年に reference types が言語仕様から消えている。
  • じゃあ、今はなんていうのだろ。

makeでつくるもの

  • map
  • slice
  • チャネル

mekeで作れるものが参照型って感じだったんだけど、とかおっしゃられていたかな。

チャネルは、データが空になった時点でfalseになるの。チャネルの話はふんふん聞いていたけど、よくわからなかったー。

サンプルコード

var b, f, s = true, 2.3, "four"
  • ↑こういう宣言は普通やらない
  • 型がそろっていないから!

 := って宣言なんだよー

宣言もできちゃう代入だと思っていたけど、本には「:= 宣言であり〜」ってかいてある。

レキシカルブロク(後述)内

in, err := os.Open(infile)
out, err := osCreate(outfile)

↑2行目。 outは新しく宣言されているから、:=  を使ってもコンパイルエラーにならない。

f, err := os.Open(infile)
f, err := os.Create(outfile) // エラー

↑2行目のfは、1行目の := で宣言されているから、2行目のfを := で宣言するとコンパイルエラーになる。

とても丁寧に解説いただいて、ぼんやり書いていたところ(たまにエラる)がすっきり🍎、解決。

スタック

  • コンパイラやOSが自動でメモリ割り当てや解放など、おせわしてくれる領域
  • コンパイルやリンカーが走った時点でサイズが決まる

ヒープ

  • アプリケーションががメモリが必要になったときに使う領域
  • いらなくなったら解放する
  • 自前でやる必要がある

P.35 ローカル変数のアドレスを返すとき

こんなコード

var p = f()

func f() *int {
    v := 1
    return &v
}
  • ローカルが変数のアドレスを返す関数
  • 安全らしい
  • ポインタpはローカル変数をずっと指す

違和感。GoはOKらしい。

  • f 呼び出すごとに、別の値が返される
  • ローカル関数が変数のアドレスを返すときは、スタックにとらず、ヒープにとる(Javaはスタックにとる)

時間あったら読んでみたい https://segment.com/blog/allocation-efficiency-in-high-performance-go-services/

P.37 new

  • あまり使わない。
  • 無形関数は構造体をよく使うから。
  • 構造体リテラル構文のが柔軟だから。

P.38 変数の生存期間

このコード

for t := 0.0; t < cycles*2*math.Pi; t += res {
    x := math.Sin(t)
    y := math.Sin(t*freq + phase)
 :
 :
}
  • 変数 tは、forが開始するごとに生成される。
  • x、yはループが繰り返されるたびに生成される。
  • GCはポインタと他の種類の参照をたどることで、対象となる変数へ到達し、メモリ領域を回収する。
  • ローカル変数は存在していればルートになる可能性がある。

うーん、なるほどわからん。

  • コンパイラはローカル変数をスタックかヒープどちらかに生成するか選択できる。
  • 選択は、varとかnewによって決まるわけではない。

P.40 代入

こんなコード

count[x] *= scale
  • Goの場合同じ型じゃないとNG
  • Javaの場合は拡張される(キャストしちゃう)
v++
v--
  • 式ではない。

 

P. 47 インポート

import (
  "gopl.io/ch2/tempconv"
)
  • tempconv ディレクトリをあらわしている。
  • で、その下にパッケージがあるとう慣習。

P.49 パッケージの初期化

func init() { /* --- */ }
  • initは呼び出すことも参照することもできない。それ以外は普通の関数。
  • 1つのソースにinit何個書いても良い。
  • 宣言されている順に実行される。
  • 初期化は下位から上位へおこなわれる、すなわちmainが最後に初期化される。

レキシカルブロック・レキシカルスコープ

  • ソースコードで明示的に波括弧で囲まれていない宣言のグループ化もこの概念に含めることができる。
  • for, if, switch, switchのなかのcaseそれぞれにレキシカルブロックが存在する。

ユニバーサルブロック

  • ソースコード、パッケージ

スコープ

こんなケース

for i := 0; i < len(x); i++ {
    x := x[i]
    if x != '!' { 
        x := x + 'A' - 'a'
        :
        :
    }
}
  • 3行目のxは2行目のXをみてて
  • 4行目のxは新しいx

if x := f(); x == 0 {
    :
} else if y := g(x); x == y {
    :
} else {
    :
} ← 最後までxは生きてる。抜けると消滅。

f.Start() ←fは未定義のためコンパイルエラー
f.Close() ←エラー

でもf使いたいなーってとき

if x := f(); x == 0 {
    :
} else if y := g(x); x == y {
    :
} else {
    f.Stert() ← なかにいれちゃう。
    f.Close()
}

グローバルで宣言するパターン

var cwd string

func init() {
    cwd, err := os.Getwd() // コンパイルエラー
    if err != nil{
        log.Fatalf("err %v", err)
    }
}
  • グローバルでcwd が宣言されているけど
  • cwd err := os.Getwd()の箇所
  • cwdは新しく宣言された。使われていないからエラーになる。

こちらのパターン

var cwd string // 初期化されない

func init() {
    cwd, err := os.Getwd()
    if err != nil{
        log.Fatalf("err %v", err)
    }
    log.Printf("hoge %s", cwd) // 使われるので、エラーにならない
}
  • log.Printf("hoge %s", cwd) で使われているからコンパイルエラーにならない。
  • でもグローバル変数が初期化されていないため、バグとなる

:=を使わなければ良い。

var cwd string

func init() {
    var err error
    cwd, err = os.Getwd()
    if err != nil{
        log.Fatalf("err %v", err)
    }
}

 

そのほか

ブログかくのに力尽きたので簡単に

alias declarations

  • 新しい言語仕様、Polar = polar どちらも同じですよと宣言できる
  • 新規でコード書くときには使わないけど、リファクタリングなどに使ったりする。
  • ほかのパッケージをもう一度宣言したいときに使うことがある。
  • 同じpolarとうパッケージをPolarという型をもういちど宣言する(全く同じ方を宣言できる)Goの1.9
  • 普段つかわないけど、type aleas で使おうってときがある。
  • 循環参照になっちゃうときに定義しなおしたりする。

Goでベンチマーク書くときの注意点

  • コンパイラーが関数呼び出しを消してしまうことがあるので、ベンチマークが正確に出せないことがある。
  • 無駄な計算をしないよう、コンパイル時に最適化されてしまう。
  • 言語仕様によってはCPUに該当する命令があれば、そっち使っちゃったりするから(Javaにもある)

ちゃんと動くようにするには

  • ローカル変数にちゃんとに保存
  • グルーバルな変数に保存する
  • 関数内にリテラルを書かない
  • bitsパッケージ?。最適化をさけるためのコードが書かれている(のでみてみるといいかも???)

おわりに

  • 読書会おわってからこの記事かいた。3時間かかった。ちょっと覚悟してたけど、あと回しにするとやらなそうだから。
  • 読書会なかなか濃かった。すばらしい。
  • 予習せずに参加してしまった。次は練習問題軽く解いてから参加したいなー。
  • いっしょに参加した、みっちーさん、メモ共有してくれた。ありがとうございます。うれしい。
  • 13〜17時まで10分休憩を2回くらいはさみながらの長丁場。前回よりしんどくなかった。ブログ入れると8時間・・しぬ・・
  • でも楽しかったー 🥰ウフフ-

柴田さんちょっと前に救急車に運ばれたとかで心配しましたが、本日はお姿がみれて安心しました。お大事になさってください。

 

 

 

 

-GoLang