Practice of Programming このページをアンテナに追加 RSSフィード

2015-06-21

[]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 とかチャンネルとか触ってみると思います(書くかは分かんないけど)。

2013-09-12

[] 翻訳 CPAN::Meta::Spec、Module::CPANFile、Minilla、Milla::Tutorial、cpanm、Carton、plenv、plenv-contrib

CPANまわりのツールたちの開発が活発なので、翻訳しようと前々から思ってたのですが、ようやく翻訳しました。

最初は、cpanfile とMinillaだけのつもりだったんですが CPAN::Meta::Specも文章中に出てくるし、Minilla::Tutorialは、Milla::Tutorialから取ってるとのことだし...ということで、Milla::Tutorialも訳しました...と、芋づる式に色々訳す結果となりました。

追記: cpanm も訳しました。

追記2: Carton も訳しました。

追記3: plenv & plenv-contrib も訳しました。

2013-06-15

[] scalarコンテキストでのsortの振る舞いは未定義

何年も使ってるのに、基本的な関数知らないとか!とか、思った。

同僚がはまっていたので知ったのですが、sort のスカラコンテキストでの振る舞いは未定義だそうです。実際問題、sort をスカラで受けるとか意味わからない。ちゃんと警告も出ます。

Useless use of sort in scalar context at -e line 1.

そもそも、リストをスカラで受けるのは、かなり嫌な感じになるため、経験的に避けます。

 my $item = qw/a b c/;

$item に入るのは、リストの最後の要素、'c' なのですが、意図してこんなコードは書きませんよね。

ちなみに、

 my ($item) = qw/a b c/;

の場合は、$item に入るのは、'a' です。

grep, map に関しては、要素数が取れるので、また違った感じですが、要素数を取る目的ではあんまり使った覚えがない。grep を scalar コンテキストで使う場合は、だいぶ昔は if の中とかで使ってた気がするけど、そういうケースでは、List::Util の any のほうが良いですね。

if (any {$_ > 1} @array) {
  # ...
}

map をスカラコンテキストで使った覚えがないなぁ。使うケースが思いつかない。


で、sort なのですが、(Ubuntu 13.04で、Perl 5.16.3 と 5.12.3 で試しました)

 my $item = sort qw/a b c/;

$item は空になります。perldoc -f sort では、

sort SUBNAME LIST

sort BLOCK LIST

sort LIST

リストコンテキストでは、LIST をソートし、ソートされたリスト値を返します。 スカラコンテキストでは、sort() の振る舞いは未定義です。

http://perldoc.jp/func/sort

スカラコンテキストでは、sort() の振る舞いは未定義」なので、空になる保証もないわけですが。

そもそも、リストコンテキスト以外は意味ないからsort自体の処理に入らないようです。

最初の警告の説明には、以下のように書かれています。

Useless use of sort in scalar context

(W void) こんな風に、ソートをスカラコンテキストで使いました:

my $x = sort @y;

これは全く便利ではないので、perl は現在のところ最適化して取り除きます。

http://perldoc.jp/docs/perl/perldiag.pod

以下の通り。

 $ perl -e 'scalar sort {die($a) } (1,2); ' # scalar context
 $ perl -e 'sort {die($a) } (1,2); ' # void context
 $ perl -e 'my @x = sort {die($a) } (1,2); ' # list context
 2 at -e line 1.

dieしているのは、最後のものだけでした。

どうでも良いのですが、リスト操作系の関数スカラコンテキストでは意味無さげな reverse はどうなんだろうと思ったら、こんな感じでした。

 print scalar reverse "dlrow ,", "olleH";    # Hello, world

どこで使うのかと小一時間悩みましたが、回文かどうかチェックするのが簡単でした(引用: http://kaibun21.jp/works/13200/)。

 perl -CA -e 'print join("", @ARGV) eq reverse(@ARGV);' かいあたえ しつくしくつし えたあいか

もはや、記事名となんの関係もないけど、最後にまとめると、

リスト ... 最後の要素
配列 ... 要素数
sort ... 未定義
grep ... 要素数
map ... 要素数
reverse ... 引数の順番も中身もひっくり返す

て、感じでした。

2012-12-30

[]Teng::Plugin::SearchBySQLAbstractMore 0.10 と 0.11 をリリース

https://metacpan.org/module/Teng::Plugin::SearchBySQLAbstractMore

0.10 は先日ご報告をうけた、search_by_sql_abstract_more メソッドの挙動が、search と違うよっていうバグの修正です(ちなみに、僕はこのバグに依存したコードを書いちゃってたので、修正が必要でした orz)。

0.11 はTengのバージョンが0.17に上がって、_execute メソッドじゃなくて、execute を使いなさいとwarningが出るようになったので、その修正です。

なんで、0.11 は、Teng 0.17 を要求します。別に上げても問題ないと思いますが、一応注意ということで。

(うちのプロダクション環境で、Teng 0.17を動かしてますが、特に問題はありません)。

2012-12-08

[]Teng::Plugin::SearchBySQLAbstractMore 0.09 リリース

https://metacpan.org/module/Teng::Plugin::SearchBySQLAbstractMore


変更点は、create_sql_by_sql_abstract_more ていうのを生やしたのと、Teng::Plugin::SearchBySQLAbstractMore::Pager::Countの改良。


create_sql_by_sql_abstract_more は、そのまま、Tengのsearch形式の引数か、SQL::Abstract::Moreのselectの引数で、単にSQL生成するだけのものです。


Pager::Countは、改良したのですが、レコード多いと使えないので、MySQL使ってるなら、Pager::CountOrMySQLFoundRows か、Pager::MySQLFoundRowsを使ったほうが良いと思います。

(前者は、通常は COUNT(*) を使って、GROUP BY を使ってる時のみ、SQL_CALC_FOUND_ROWSを使います)

まぁ、SQL_CALC_FOUND_ROWS もあんまり評判は良くないですけどね。