Practice of Programming

プログラム とか Linuxとかの話題

Go言語さわってみた

一年前(2014年5月末の作成日の.goなファイルがあった)くらいにGo Tourを途中までやったけど、ほとんど覚えてなかったです。
ディレクトリとか環境変数とかは、前若干コード書いたので、それっぽく残ってました。emacsのgo-modeも入ってた。
という状況から、ちょうど一週間くらいたった感じです。

作ったもの

https://github.com/ktat/go-coloring テキストを正規表現で色付けするもの。そういうツールってあるっけ?
https://github.com/ktat/go-pager ↑のやつにlessっぽくしようとしたpagerを組み込んでいたけど、分離した(go-termboxを利用)。

コードは、まぁ、まだまだアレというか間違ってる/分かってない可能性が高いです。

参考にしたところ/したいところ

golang.org には書いてるけど、golang.jp には書いてないこともあったので、
golang.org 見たほうが良いんじゃないかなとか思う。

作法

変数/関数の命名規則などが慣習も含め決まっているので先に読んだほうが良いと思う。

https://golang.org/doc/effective_go.html#names

例えば、セッターは

 SetParamName

だが、ゲッターは

 GetParamName

ではなく、単純に

 ParamName

とする。

パブリックなものは大文字から、プライベートなものは小文字の名前をつける(メソッドも構造体の変数も)

gofmt というコマンドがあるので、それで整形を行う(必ず)。インデントはタブです。

コンパイル時のチェック

使ってないパッケージが import されてたり、宣言されただけで使ってない変数とかがあると、コンパイル通らない。

ファイル構成

詳しくはこちらをご覧ください。
https://golang.org/doc/code.html

 go/src/github.com/ユーザー名/プロジェクト名/ソースコード.go

にしておいて、

 GOPATH=/home/USER_NAME/go

としておくと、$GOPATH/src/ $GOPATH/pkg/ 以下にソースコードコンパイル済みのライブラリがあると想定する。

 import (
   "github.com/mattn/go-getopt"
 )

のように import すると、

 /home/USER_NAME/go/src/github.com/*/
 /home/USER_NAME/go/pkg/ARCHITECTURE/github.com/*/

以下の *.go, *.a ファイルが読まれるもよう。

コンパイル

  % go build 名前.go

  名前

の実行ファイルができあがる。

即時実行したければ、

 % go run ファイル名 引数

とすればよい。今のところ go run ばっか使ってます。

クォート

シングルクォートはルーン(rune)というもの(int32のalias)で、ユニコードのコードポイント

 fmt.Println('a') // 97 == 0x61

ダブルクォートはstring 。byte(uint8 の alias)の配列

 "12345"
 "12345"[1] == '2'

以下のような比較はエラー

 "あ"[0] == 'あ' // overflows uint8

"あ"[0] の0番目のバイト(uint8)と 'あ'(rune(int32のalias))の比較になるため

バッククォートは複数行にまたがって書ける。

  fmt.Println(`...
 ...
 `)

println

プリントデバッグで使ったりしますが、長い文字列を渡すと

 [string too long]

と文句言われる。

 fmt.Println(`...
 ...
 `)

は問題ない。

main

実行したいというときは、main package に main 関数をつくればよい。

// test.go
package main
    
func main () {
    // なんか書く   
}

そして、以下で実行できる。

go run test.go

変数宣言

 var hoge string = "abcd"
 hoge := "abcd"

:= は型推論してくれる。
最初の代入で使うものなので、同一スコープで := を再度使うとエラー。var hoge の後に := 使ってもエラー。

スコープはブロックスコープ(https://golang.org/ref/spec#Declarations_and_scope)なので、以下はセーフ。

	var str string = "abcde"
	println(str[3:4])
	{
		str := "eee"
		println(str)
	}

以下のように複数の宣言もできる。

 var (
   hoge string
   foo bool
 )

トップレベルで宣言するときは、:= は使えない。

 var hoge string
 var hoge string = "aaa"

blank idenitifier

"_"はblank idenitifier(https://golang.org/ref/spec#Blank_identifier)であり、無視される。

  i, _ :=  strconv.Atoi("12")

配列

	array := make([]string, 0)

追加するときは

	array = append(array, "hogehoge")

map

ハッシュ的なもの。
https://blog.golang.org/go-maps-in-action

  dict := map[string]string {
      "key": "value"
  }

  result := make(map[string]string)
  result["a"] = "b"
キーの存在チェック
 if _, ok := dict["key"]; ok {
    println("key exists")
 }

http://stackoverflow.com/questions/2050391/how-to-check-if-a-map-contains-a-key-in-go

キーの削除
 delete(mapVar, "a")
map の値に関数

はもたせられない(nested func not allowed と言われる)

 map[string]func

みたいな定義はできるけど、代入したらエラーになる。
定義できる意味ないんじゃ...?
牧さんからコメントいただいて、出来るということです。
http://play.golang.org/p/voWrwbQ2oe

 test := map[staing]func() {}
 test["a"] = func () {println(1)}
 test["a"]()

みたいに、引数とかを書いてなかったからだそうです。言われてみれば、そらそうだーという感じがしました。
funcの定義を書くわけなので、別のタイプの関数は取れません。

	test := map[string] func() {}
	test["aiueo"] = func() {println(123)} // これはいいけど
	test["akasa"] = func(x string) {println(x)} // これはダメ

最初にググってでてきた、interface を使う方法は、どんなタイプの関数でも入れれるといえば入れれる(使いドコロがあるのかは知らないけど)

	test := map[string] interface{} {}
	test["aiueo"] = func() {println(123)}
	test["akasa"] = func(x string) {println(x)}
	test["aiueo"].(func () )()
	test["akasa"].(func (x string))("a")

追記終わり

interaface とやらを使うらしい(http://stackoverflow.com/questions/6769020/go-map-of-functions)。

	colorFunc := map[string]interface{}{
		"red":    func(s coloring.String) string { return s.Red() },
		"green":  func(s coloring.String) string { return s.Green() },
		"blue":   func(s coloring.String) string { return s.Blue() },
		"yellow": func(s coloring.String) string { return s.Yellow() },
		"white":  func(s coloring.String) string { return s.White() },
		"cyan":   func(s coloring.String) string { return s.Cyan() },
		"black":  func(s coloring.String) string { return s.Black() },
		"purple": func(s coloring.String) string { return s.Magenta() },
	}

これを呼ぶときは、

	colorFunc["red"].(func (colorling.string) string)(val)

のように呼ぶ。

メソッドリストのない空のinterface は何が来てもOKだから、func も入るということかと思う。

型変換

	[]byte(str)
	string(someByte)

数値 <-> 文字列

  str := strconv.Itoa(12)
  i, err :=  strconv.Atoi("12")

文字列連結

 "hoge" + "fuga"

関数

  func 名前 (型) 戻り型 {
       return ...
  }

戻り値が複数の時は

  func 名前 (型) (戻り型,戻り型) {
       
       return ...
  }

戻り値にも名前をつけられる

  func 名前 (型) (名前 戻り型, 名前 戻り型) {
       
       return // 変数を渡さなくても良い
  }

パッケージの関数

 packageName.FunctionName()

で呼べる。

パッケージの型とメソッド

 package hogehoge

 type String string

 func (i String) Name String{
      // ...
 }

とかすると

 hogehoge.String("hoge").Name()

とかって使える。

ループ

for しかない

ノーマル
 for i := 0; i < len(str); i++ {

 }
無限ループ
 for {

 }

break で止める
continue で次のループへ
goto ラベルへ

break, continue も ラベルを取れる。
ラベルは以下のように記述。

 Loop: 
配列の取り出し

indexのみを取る

 for i := range array { }

indexと値

 for i, v := range array { }
map取り出し

キーのみ

 for k := range array { }

キー、バリュー

 for k, v := range map { }

if

 if len(str) == 0 {
 
 } else if ... {
 
 } else {
  
 }

条件は bool でないとダメ

switch

case のところは同一タイプじゃないとダメらしい。

        switch c {
        case 'f':
                fileName = OptArg
        case 'h':
                usage()
                os.Exit(1)
        default:
		if color, ok := colorMap[string(c)]; ok {
			replace = append(replace, fmt.Sprintf("(?P<%s>%s)",  color, OptArg))
		} else {
			os.Exit(1)
		}
        }

三項演算子

ありません。

substring

スライスで取る。

	var str string
	str = "abcde"
	println(str[0:3]) // abc
	println(str[3:4]) // d
	println(str[4:5]) // e

文字列分割

    strings.SplitN(str, sep, -1)

最後の引数はsplitする数。-1だと以下と同じ。

    strings.Split(str, sep)

http://golang.org/pkg/strings/#SplitN

文字列連結(strings.Join)

    strings.Join(array, sep)

http://golang.org/pkg/strings/#Join

正規表現

関数がいっぱいあるなー。

http://tip.golang.org/pkg/regexp/
http://tip.golang.org/pkg/regexp/syntax/
https://gobyexample.com/regular-expressions

regexp.Compile

変な正規表現渡しても死なないので、自分でハンドリング。

        re,regexpErr := regexp.Compile(pattern)

        if regexpErr != nil {
                log.Fatal(regexpErr)
		// しかし、log.Fatal や os.Exit は使わないほうがいいらしい
        }
regexp.MustCompile

変な正規表現渡すとエラーで死ぬ。
どっかからもらったんじゃなくて、自分で書いた正規表現はこっちで良いでしょう。

        re := regexp.MustCompile(pattern)
置換

以下、全て同じ

   regexp.MustCompile("^(a)(b)").ReplaceAllString("abcdef", "$2$1")
   regexp.MustCompile("^(a)(b)").ReplaceAllString("abcdef", "${2}${1}")
   regexp.MustCompile("^(?P<first>a)(?P<second>b)").ReplaceAllString("abcdef", "$second$first")
 (?P<名前>パターン)

は、名前付きキャプチャ。

マッチしたものを後で使う
	re := regexp.MustCompile("^(?P<first>a)(?P<second>b)")
	match := re.FindSubmatchIndex([]byte("abcdefab"))

	var dst []byte
	dst = re.ExpandString(dst, "$second$first", "abcdef", match)
	fmt.Println(string(dst))

http://astaxie.gitbooks.io/build-web-application-with-golang/content/ja/07.3.html

regexp関係のFuncと付く関数でやっても意味ないと思われる。

フラグは先頭につける
 (?フラグ)パターン

以下のような感じ。

 (?im)^[a-z]+$

エラーハンドリング

error という型がある。log.Fatal に投げるとエラーメッセージを出してくれる

	log.Fatal(err)
	// 既述ですが、log.Fatal や os.Exit は使わないほうがいいらしい

以下を読むと良いのかもしれません。
http://blog.golang.org/error-handling-and-go

ファイルの読み取り

	whole, err =  ioutil.ReadFile(ファイル名)

http://golang.org/pkg/io/ioutil/

標準入力全部読み

	whole, err = ioutil.ReadAll(os.Stdin)

http://golang.org/pkg/io/ioutil/

一行ずつ

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		var l = scanner.Text()
	}

http://qiita.com/hnakamur/items/a53b701c8827fe4bfec7

コマンドライン引数

flag パッケージ

標準で flag というパッケージががある(使わなかったので見てないです)

https://golang.org/pkg/flag/

go-getopt

mattnさんの mattn/go-getopt が使える

https://github.com/mattn/go-getopt

example に使い方あり。

https://github.com/mattn/go-getopt/blob/master/example/example.go

 OptEror = 0 

にすると、エラーメッセージ(おかしなコマンドライン引数を渡した時とか)がでなくなる。
おかしなコマンドライン引数を渡した時を判別する場合は、switch の default でやれば良いかも。

ポインタ

以下のような感じで戻り値を受け取らずに、files にファイルを突っ込める。
ただ、こういうケースでポインタ使うのは間違いらしい。
http://qiita.com/ruiu/items/e60aa707e16f8f6dccd8

  seekDir(&files, "./")
  
  func seekDir (files *[]string, dirName string) {
  	fileInfo, ioerr := ioutil.ReadDir(dirName)
  	errCheck(ioerr)
  	for i := 0; i < len(fileInfo); i++ {
  		if fileInfo[i].IsDir() == false {
  			*files = append(*files, dirName + "/" + fileInfo[i].Name())
  		} else {
  			seekDir(files, dirName + "/" + fileInfo[i].Name())
  		}
  	}
  }
 &変数

が、ポインタ

 *変数

で、デリファレンス

メソッド

型に対してメソッドを作れる

 type S string
 
 func (s *S) aaaa () {
      fmt.Println("=== S aaaa ===")
      fmt.Println(s)
 }
 
 func main() {
      var str S = "Type S"
      S.aaaa()
 }

struct で名前なしで型を埋め込むことで、埋め込んだ型のメソッドを委譲できる。

 type S2 struct {
      str string
      S
 }
  
 func (s *S2) bbbb () {
      fmt.Println("=== S bbbb ===")
      fmt.Println(s.str)
 }
 
 func main() {
      var str S2
      S2.str = "Type S2"
      S2.aaaa() // 委譲したもの
      S2.bbbb() // S2のメソッド
 }

以下のように "*S2" じゃなくて、"S2" のようにもかけるけど、そうすると、メソッド内でメンバを書き換えたりしても、呼び出し元は変わらない。

 func (s S2) cccc() string{
   s.str = "cccc"
   println(s.str) // cccc
   return s.str
 }
 
 var s S2
 s.str = "s2"
 println(s.cccc()) // cccc
 println(s.str)    // s2

Go言語はオブジェクト指向かどうかという話は、以下。
http://golang.jp/go_faq#Is_Go_an_object-oriented_language

インターフェイス

インターフェースはメソッドの羅列。インターフェースに定義されているメソッドを実装すれば、
インターフェースを受け取れる関数を受け取れる。
※ちなみに以下は微妙な例になっておりますが、書き直すのがだるいのでそのままです。

Animalizer インターフェースを実装してみた

Moveメソッドがインターフェースのメソッドとして定義されている。
AsAnimal メソッドは引数に Animalizer インターフェースを受け取れる。

  package animal
  
  import "fmt"
  
  type Animalizer interface {
  	Move()
  }
  
  type Animal struct {}
  
  func (a Animal) Move (){
  	fmt.Printf("move as animal %T\n", a);
  }
  
  func AsAnimal (a Animalizer) {
  	fmt.Printf("animal is passed %T\n", a)
  }
Humanizer インターフェースを実装してみた

Talk と Move メソッドがインターフェースのメソッドとして定義されている。
AsHuman メソッドは引数に Humanizer インターフェースを受け取れる。

  package human
  
  import "fmt"
  
  type Humanizer interface {
  	Talk()
  	Move()
  }
  
  type Human struct {}
  
  func (h Human) Move (){
  	fmt.Printf("move as human %T\n", h);
  }
  
  func (h Human) Talk (){
  	fmt.Printf("I'm human %T\n", h)
  }
  
  func AsHuman (h Humanizer) {
  	fmt.Printf("human is passed %T\n", h)
  }
使ってみる
  package main
  
  import (
  	"github.com/ktat/test/animal"
  	"github.com/ktat/test/human"
  )
  
  var a animal.Animal;
  var h human.Human;
  
  func main () {
  	a.Move()
  	h.Move()
  	h.Talk()
  	animal.AsAnimal(a)
  	animal.AsAnimal(h)
  	human.AsHuman(h)
  }

結果

move as animal animal.Animal
move as human human.Human
I'm human human.Human
animal is passed animal.Animal
animal is passed human.Human
human is passed human.Human

仮に、

  	human.AsHuman(a)

なんてことをすると、

 ./test.go:18: cannot use a (type animal.Animal) as type human.Humanizer in function argument:
         animal.Animal does not implement human.Humanizer (missing Talk method)

と怒られる。

インターフェイスに定義されたメソッドを実装することで、同一のインターフェースのものということになる。
ロボット型に人のインターフェースをもたせれば人として扱える。
なんか面白いですね。

ちなみに、命名規則的に interface は、-er でつけて、- なメソッドを持つ(Stringer は、Stringを持つ)となってるので、上記は変な例だと思います。

例えば、Stringer インターフェースは、

 func String string {}

なメソッドを持っています。

ついでに説明すると、適当な型に

 type AnyType {
   str string
 }
 
 func (a AnyType) String string {
    return a.Str
 }

のようなメソッドを作ってやれば
fmt.Printf は、Stringer インターフェースを受け取ることができるため、

 var a AnyType
 a.Str = "1234"
 fmt.Printf("%s", a)

と渡すことが出来ます。

なお、以下のようなメソッド定義だと、なんでも受け取れるようになります。

 func (h Human) Any (i interface{}) {
 
 }

なお、インターフェースを満たすというのはメソッドを満たせばいいだけなので、単純に型を埋め込んでやるだけでも、インターフェースのメソッドを満たすことが出来ます。

type Human struct {
	animal.Animal
}

インターフェースを満たすだけであれば、これだけでも良いわけですが、結果はこうなります。

move as animal animal.Animal
move as animal animal.Animal   // さっきは、 move as human human.Human
I'm human human.Human
animal is passed animal.Animal
animal is passed human.Human
human is passed human.Human

メソッドは委譲されていますが、もともと、

func (a Animal) Move (){
	fmt.Printf("move as animal %T\n", a);
}

という定義なので、a は Animal なのです。


次は、gorutine とかチャンネルとか触ってみると思います(書くかは分かんないけど)。