🐭

goの基本文法 No.1

2020/09/28に公開

2020-09-27執筆
A Tour of Goに沿ってGoの文法をまとめます。
これまでにJavaScriptやPythonなどのリッチ言語しか使ったことがなかった自分のためにまとめたものです。
なのでこれまでに上のようなリッチ言語を触ったことがあり、これからGoを触ってみようと考えている人の参考になればと思います。
文章の構成は、基本的に

  1. 概要
  2. コード
  3. コードの説明

の構成にしているつもりです。概要で簡単な説明をし、コードで例を示して、さらにコードの説明で細かい説明をするといった構成です。

基本的な構造

全体

// code:1-1
// ①
package main
// ②
import "fmt"
// ③
func main() {
	fmt.Println("Hello, golang!!")
}
  1. Goのプログラムはパッケージで構成されています。プログラムはmainパッケージから開始されます。
  2. Goはいろいろな機能を外部のパッケージからインポートすることができます。ここでは、文字を出力するパッケージ"fmt"をインポートしています。
  3. 実際に実行される処理を記述するmain関数です。プログラムを実行するとここに記述した処理が実行されます。今回はfmtパッケージ内にある文字を出力する関数であるPrintln関数を用いて「Hello, golang!!」の文字列を出力する処理を行います。

パッケージは例えばJavaScriptでnodeモジュールをrequire文で読み込んだり、Pyhtonのimport文でモジュールを読み込むのと感覚的には一緒です。ただし、これらの言語で元々用意されているので読み込む必要のない出力などの基本的な関数もGoでは読み込む必要があるということを頭に入れておいてください。

packageについて

// code:1-2
package mymath
// Sqrt 引数xを受け取りその平方根を返す。
func Sqrt(x float64) float64 {
	z := 0.0
	for i := 0; i < 1000; i++ {
		z -= (z*z - x) / (2 * x)
	}
	return z
}

package名をmain以外のものにするとパッケージとして別のプログラムから呼び出すことができます。細かい方法は別の機会に紹介します。今回はこのようなことも出来るという紹介です。

importについて

// code:1-3
// ①
import "fmt"
import "math"
// ②
import (
	"fmt"
	"math"
)

複数のパッケージを読み込むことができます。書き方は上のように

  1. 別々に書く方法
  2. まとめて書く方法

があります。

// code:1-4
import (
	. "fmt" // ①
	m "math" // ②
)
func main() {
	Printf("Now you have %g problems.\n", m.Sqrt(7))
}

このように

  1. ドットを使い省略
  2. パッケージ名の前に文字列を指定することでその文字列を呼び出しに使う(エイリアス)

ことができます。


Goは扱うデータが型を持っています。型によって後述する変数が持てる型や関数が引数や返り値として持つ型が決まります。

  • 論理型
    • bool
  • 数値型
    • 符号付整数型 : int8, int16, int32, int64
    • 符号無し整数型: uint8, uint16, uint32, uint64
  • byte型
    • byte(alias for uint8)
  • 浮動小数点数型
    • float32, float64
  • 複素数型
    • complex64, complex128
  • 記号型(文字を表す記号)
    • rune(alias for int32 unicodeのコードポイントを表す)
  • 文字列型
    • string

JavaScriptのようなリッチ言語では宣言した変数にはどのようなタイプの値でも代入することが出来ました。これは一見すると便利に感じますが、危険な面もあります。安全にプログラムを運用するために型は存在します。最初は扱いづらいですが慣れると逆に便利に感じるでしょう。


変数

通常の変数宣言

何らかの処理結果の値を別の処理で使うなど、値を保持したいことがあります。その際に使用するのが変数です。変数は

var 変数名 型
var 変数名 型 = 初期値

という形で宣言します。

var s string = "hello"

上ではsという文字列型の変数にhelloという文字列を代入しています(文字列はシングルクォーテーションかダブルクォーテーションで囲む必要があります)。

var x, y, z int

上のように同じ型であれば一度に宣言することも可能です。

短い変数宣言と型推論

c, python, java := true, false, "no!"

:=を使うことでvarと型を省略して変数を宣言することができます。その際代入する値から推測してGoが自動的に型を決めます(型推論)。

ゼロ値

初期値を与えなかった場合は以下のようにそれぞれの型にあったゼロ値が入ります。

// code:1-5
func main() {
	var i int
	var f float64
	var b bool
	var s string
	var z complex128
	fmt.Printf("%v %v %v %q %v\n", i, f, b, s, z)
}
/*実行結果
0 0 false "" (0+0i)
*/

型変換

必要があれば、以下のようにして変数の型を変換することができます。

var a = T(b)

このようにして変数bの値をTという型に変換してaに代入することができます。

// code:1-6
func main() {
	var x, y int = 3, 4
	var z float64 = float64(x + y)
	fmt.Println(x, y, z)
}
/*実行結果
3 4 7
*/

例えば整数型を実数型に変換したりできます。

// code:1-7
func main() {
	var x, y string = 3, 4
	var z int = int(x) + int(y)
	fmt.Println(x, y, z)
}
/*実行結果
./prog.go:8:6: cannot use 3 (type untyped int) as type string in assignment
./prog.go:8:6: cannot use 4 (type untyped int) as type string in assignment
./prog.go:9:17: cannot convert x (type string) to type int
./prog.go:9:26: cannot convert y (type string) to type int
*/

文字を数値に変換するなど、常識的に無茶な型変換はエラーを返します。

定数

変数は宣言後も値を再代入できます。途中での再代入をさせない定数は以下のように宣言します。

const 定数名 =

計算

算術演算子

これまでの例で足し算などを断りなく使っていましたが、ここでGoで使用出来る算術演算子を書いておきます。足し算は文字列の結合にも使用できます。

名前 演算子
+
-
*
/
余り %

関数

概要

Goは関数の集まりとしてプログラムをなします。関数は

func 関数名(引数1, 引数2, ...) 戻り値の型 {
	// 処理
}

という形で宣言します。引数は必要な数だけを書いてあげてください。引数に関しても戻り値に関してもない場合は何も書きません。

// code:1-8
package main
import "fmt"
// ①
func add(x int, y int) int {
	return x + y
}
func main() {
	fmt.Println(add(42, 13))
}
/*実行結果
55
*/

code:1-8では①で足し算を行うadd関数を実装しています。ここでは引数に整数型のx, yを受け取ってそれを足した整数型x+yを返しています。
関数名も引数名も書く人の自由に名前をつけることができますが他の人が名前だけから機能を推測出来るように命名します。
今回は足し算を行う関数なのでadd, 引数名は数を扱うので数学でよく使うx, yを使用しています。

func add(x, y int) int {}

引数が同じ型の場合は最後にまとめて書くことができます。

複数の返り値

// code:1-9
func swap(x, y string) (string, string) {
	return y, x
}

func main() {
	c, d := swap("hello", "world")
	fmt.Println(c, d)
}
/*実行結果
world hello
*/

複数の返り値を持たせることもできます。上のswap関数はその名の通り引数に与えられた二つの文字列を与えられた順番と逆の順番で返す関数です。

名前付き返り値

// code:1-10
func split(sum int) (x, y int) {
	y = sum % 10
	x = sum - y
	return
}

func main() {
	fmt.Println(split(14))
}
/*実行結果
10 4
*/

返り値に名前をつけることでreturnの後に返り値を指定しなくてもreturnした時点でその変数の値を返すことができます。

無名関数

Goは無名関数も扱えます。

func(引数1 型,...) 返り値の型 {
	// 処理
}

またGoが扱う関数は第1級関数なので変数への代入も可能です。

// code:1-11
func main() {
	add := func(x, y int) int { return x + y }
	fmt.Println(add(12, 34))
}
/*実行結果
46
*/

code:1-11では2つの引数を足した値を返す無名関数をaddという変数に代入しています。

Discussion