イノベーション エンジニアブログ


株式会社イノベーションのエンジニアたちの技術系ブログです。ITトレンド・List Finderの開発をベースに、業務外での技術研究などもブログとして発信していってます!


このエントリーをはてなブックマークに追加

PHPerがGolangを試してみた 前編

概要

私はPHPerなのですが、Golangを試してみました。
難しいと思った点など書いていければと思います。

  • Hello World

  • コンパイル処理

  • パッケージ・関数定義

  • 変数

  • 定数

  • 真偽型

  • null値

  • 配列型

  • スライス型

  • マップ型

  • 条件分岐

  • ループ処理

  • 構造体

  • インターフェース型

〜後編へ続く〜

Hello World

Hello Worldに関しては、特に迷う部分はありませんでした。

// vi helloworld.go

// メイン処理のパッケージ名
package main

// fmtをインポートしています
import "fmt"

// メインの処理を記述していきます
func main() {
	// 文字列を標準出力します
	fmt.Println("HelloWorld")
}

fmt というパッケージを使わないと標準出力ができないのだな。
と思ったくらいです。

あとは Println は Java っぽいなとも思いました。

コンパイル処理

コンパイル処理はPHPにはありませんが、
Java のように行えばよく、特に迷うことはありませんでした。

// コンパイル
go build helloworld.go
// 実行
./helloworld

パッケージ・関数定義

パッケージはPHPで言うところの名前空間のようなものと私は捉えました。

// sample01.go

// パッケージ名記述
package pkg01

// 関数定義
func aaa() {
	// 何か処理
}

関数は func と宣言します。

//sample02.go

// 同一ディレクトリにて別ファイル名で同じパッケージを指定
package pkg01

func bbb() {
	aaa() // 同一パッケージの先程のaaa関数が呼び出される
}

尚、
同一パッケージのスクリプトファイルは「必ず同じディレクトリ内に」格納されていなくてはいけません。

パッケージをインポート

// 1つだけインポートする場合
import "pkg01"

// 複数インポートする場合
import (
pkg01
pkg02
pkg03
)

また、
小文字で始まるメンバーはprivate、
大文字で始まるメンバーはpublicになります。

package pkg01

// privateになる
func aaa() {
}

// publicになる
func Bbb() {
}

パッケージの考え方は特に違和感なく入っていくことができました。

変数

var aaa int = 1 // 型と初期値を指定
var bbb int     // 型のみ指定(※型固有の初期値が設定される)
var ccc = 1     // 型推論(※初期値の型が自動的に指定される)

ソースのトップレベルで宣言すればグローバルスコープ、
関数内で宣言すればローカルスコープになります。

尚、ローカル変数のみ以下のように記述可。

aaa := 1

変数は型指定や、型推論といった考え方がありますが、
特に違和感なく入っていくことができました。

定数

const aaa string = "hoge"

定数も特に違和感はありませんでした。

真偽型

true と false

こちらはPHPと全く同じでした!

null値

nil がそれに相当するようです。

個人的にはLispを思い出しました。
特に違和感は無いのではないでしょうか。

配列型

配列型は、PHPの配列とは異なりイミュータブルです。
後述のスライス型はPHPの配列に似ていると思われます。

// 初期化
var aaa [3]int //※PHPでは型指定とか配列長指定はできません

// 初期値代入
bbb := [3]string{"hoge","fuga","foo"} //※同じく配列長は変えられません

// 配列長省略
ccc := [...]string{"hoge","fuga","foo","bar"} //※配列長がこの場合4で固定になる

// インデックスキー指定
ddd := [...]string{1:"hoge", 2:"fuga"} //PHPで近い表現だと、$ddd = [1=>"hoge", 2=>"fuga"];

スライス型

可変長の配列型みたいなもの。
こちらのほうがPHPの配列に近いと思いました。

var aaa []int
bbb := []string{"hoge","fuga","foo"} //PHPで近い表現だと、$bbb = ["hoge","fuga","foo"];

PHPerの私には気づくのに時間がかかりましたが、
PHPの配列はかなり特殊で、
型がまぜこぜになった配列とか、
複雑怪奇な連想配列、
何でもかんでも連想配列にブチ込む手法など、
かなりPHPは異端なのだなと。
PHPの配列はスッキリ簡単に書けるところは素晴らしいと思っております。

マップ型

PHPの連想配列的なものと思います。

aaa := map[string]int{"hoge":111, "fuga":222}

aaa["hoge"] = 333 //※この辺はPHPに似ていると思いました。

fmt.Println(aaa["hoge"], aaa["fuga"]) // 333 222 ここも!

ここは違和感ありませんでした。

条件分岐

if文

if文は、
{ } カッコは省略できないようです。
その点も含めて特に違和感はありません。

むしろ好きです。

var aaa int = 1

if aaa == 1 {

} else if aaa == 2 {

} else {

}

switch文

switch文も良いなと思いました。

var aaa int = 1

switch aaa {
case 1:
	fallthrough
case 2:

default:

}

PHPと違うのはbreak記述が要らないというところです。
caseの終端は標準でbreakされます。
逆にbreakしたくない時は fallthrough と書くようです。

こちらの方が世間一般のswitchより自然に思えます。

以下のような、switchの判定値を省略した書き方も有効でした。

var aaa int = 1

switch {
case aaa == 1:
	fallthrough
case aaa == 2:

default:

}

// PHPで言うところの
switch (1) {
case $aaa == 1:
case $aaa == 2:
	break;
default:
}

条件分岐はとてもわかり易いと感じました。

ループ文

ループは for しかありません!
ですが、ちゃんとPHPで言うところの while foreach 的な書き方も存在しています。

// for文
for i:=0; i<10; i++ {
	fmt.Println(i)
}

// foreach文のようなもの
aaa := map[string]string{"hoge":"fuga", "foo":"bar"}
for k, v := range aaa {
	fmt.Printf("k=%s, v=%s\n", k, v) // k=hoge, v=fuga k=foo, v=bar
}

// while文のようなもの
i := 0
for i<10 {
	fmt.Println(i)
	i++
}

ループを抜ける break
ループ処理をスキップする continue

これはPHPと同じでした。

違和感なく入って行けました。

構造体

構造体はC言語のように使うことができます。
(個人的には)C言語に明るくないのでしっかりと学んで行きたい部分です。

// 構造体 User を定義
type User struct {
	name string
	age int
	token string
}

func main() {

	// 構造体を型として宣言
	var user User

    	// このように利用できる
	user.name = "Shirota"
	user.age = 39
	user.token = "hoge"

}

こんなように構造体に値を埋め込んで使えるようです。

また、 以下のように構造体に構造体を指定もできるようです。

type Administrator struct {
	User
	passwd string
}

PHPには無い概念と思います。

また、
Golangにはクラスが無いんだそうです。
なので、
パッケージがクラスと同等なのかと思ったのですが、
何やらそうではないとわかりました。

package user

type User struct {
}

このようにしてしまうと、以下のようなディレクトリ構成になり、冗長です。

models/
    user/
        user.go

ではクラスみたいなことをしたい時どうすればよいのか。
そこでGolangはメソッドを使うのだと捉えました。

理解が難しかったのは、
関数とメソッドは別物である。
ということです。

type User struct {
    name string
    age int
    token string
}

// メソッド定義
func (u *User) setToken() string {
    u.token = "fuga"
    return u.token
}

func main() {
    user := &User{"Shirota",39,""}
    user.setToken()
    fmt.Println(user.token) // fuga
}

Userという構造体に対して setToken というメソッドがある。
この関係がクラスに一番近いと感じました。

また、
クラスが無いので継承もありませんが、
継承のようなことを行いたければ、
先述した構造体の中に構造体を入れるという手法

type Administrator struct {
	User
	passwd string
}

を使えば、
AdministratorからUserのsetTokenを使えるようです。

この辺はコーディングをして理解を深めていきたいです。

インターフェース型

インターフェースは割りと違和感なく入っていけました。
ただ、
インターフェース「型」というのが特殊だなとは感じてしまいますが、
こちらも実際のコーディング経験を積んでものにしていきたいです。

type User interface {
	login()
	logout()
}

type Administrator struct {
	id int
	passwd string
}

func (adm Administrator) login() {
	// ログイン処理
}

func (adm Administrator) logout() {
	// ログアウト処理
}

func (adm Administrator) add() {
	// add処理
}

func main() {
	admin := Administrator{10001, "hoge"}
	var user User = admin

	user.login()
	user.logout()
	user.add() // エラーとなる
}

次回

  • 例外処理(※try catch finallyが無い)

  • 並列処理

の辺りと、
前編後編通しての所感を書きたいと思います。