sagantaf

メモレベルの技術記事を書くブログ。

go言語の文法基礎をサンプルとともに(part1)

はじめに

A Tour of GoでGo言語の基本を学んでみましたが、言葉や具体例がいまいち分かりづらい部分もあったため、自分なりにまとめました。 (今の自分のレベルに対する備忘録の側面が強いため、逆に分かりにくい部分もあるかもしれません…)

これはパート1です。


基本的なプログラムの書き方とimportの方法

Goのプログラムは、全てパッケージに属している必要があります。

最初にPackageを作成して、それをmainで実行させる流れです。

そのため、コードの最初にpackage mainがないとエラーになります。

// これがないとエラーになる
package main

// functionとしてmainを書き、その中に実行させたい処理を書く
func main() {
      (処理を書く)
}

別のパッケージを使う場合は、下記のようにimportします。

package main

// import文
import (
        "fmt"
        "math"
)

func main() {
    (処理を書く)
}

import文は分けて書くこともできますが、()で囲った方が読みやすくなります。

import "fmt"
import "math"

例えばmathパッケージは下記のように使います。

package main
import (
        "fmt"
        "math/rand"
)
func main() {
        fmt.Println("ランダム数値:", rand.Int())
}

ここではランダムな数値が出力されます。

出力例:

ランダム数値: 12345

rand.Int()のようにimportしたパッケージのメソッドを使う時は、頭文字を大文字にする必要があります。

小文字のパッケージは内部用として使うことになっており、外部からはアクセスできないようになっているため、自分でパッケージを作成し、他のプログラムから利用するときには注意が必要です。

下記公式ページにて言及されています。

The Go Programming Language Specification - The Go Programming Language


同じパッケージに属するファイル間では、importの必要なく変数や関数を自由に利用できます。

その代わり、同一のディレクトリに格納し、名前の重複を避ける必要があります。また、同一ディレクトリに別のパッケージを格納することはできません。

src
┣ processing
┃ ┣ code1.go
┃ ┗ code2.go
┗ getdata
  ┗ code3.go

code1.go

package processing
func merge(){
   処理…
}

code2.go

package processing
func Replace(){
   merge()
   処理…
}

code3.go

package getdata
import "processing"
func getCsv(){
   // merge() // エラーになる
   // processing.merge() // 小文字の関数にはアクセスできないためエラーになる
   processing.Replace()
}


print文の基本

Println, Printf

fmtパッケージにてprint文が用意されています。

fmt.Printlnを使うことで、最後に改行されてprintされます。

fmt.Printfを使うことで%を使って出力ができます。

fmt.Println(math.Sqrt(7)) // 49が出力され、改行される
fmt.Printf("4のルートは、 %g です。", math.Sqrt(4)) // 2が出力される

fmt.Printでもprintできますが、改行されず、%も使えません。

fmt.Printf("4のルートは、 %g です。", math.Sqrt(4))

上記の出力は、

4のルートは、 %g です。2

となってしまいます。

ちなみに%gは、浮動小数点数(float64など)や複素数(complex128など)の時に利用します。%tは論理値(bool)、%dは整数(int, int8, uint, uint8など)、%sは文字列(string)として使えます。

Sprintln, Sprintf

fmt.Sprintffmt.Sprintlnは挙動は上記2つとほぼ同じですが、標準出力するのではなく、stringをreturnしてくれます。そのため%dなどを使った結果を変数に格納したり、ファイルに保存したりできるようになります。

result_string1 := fmt.Sprintf("4のルートは、 %g です。", math.Sqrt(4))
result_string2 := fmt.Sprintf("9のルートは、 %g です。", math.Sqrt(9))
fmt.Println(result_string1, result_string2)

下記のように出力されます。

4のルートは、 2 です。 9のルートは、 3 です。

仕様は以下です。

fmt package - fmt - pkg.go.dev


関数の基本(Functions)

関数の書き方は以下のようになります。

func 関数名( 変数 型, 変数 型, …) 返り値の型 {
    関数の中身
}

変数名の後ろに型を書くところが他の言語と異なる点です。

いくつか例を見てみます。

package main
import "fmt"

// 2つの引数を足す関数
func add(x int, y int) int { return x + y }
// 2つの引数を掛ける関数
func mul(x, y int) int { return x * y}
// 文字を入れ替える関数
func swap(x, y string)(string, string){ return y, x }

func main(){
   fmt.Println(add(3, 4)) // 7
   fmt.Println(mul(3, 4)) // 12
   fmt.Println(swap("first", "second")) // second first
}

mul関数のように、同じTypeが続く場合には省略して書くこともできます。

func add(x int, y int)
   ↓
func add(x, y int)

swap関数のように返り値を複数設定することもできます。

func swap(x, y string)(string, string){ return y, x }


下記のように事前に変数をつけて返り値を設定することで、return文には何も書かなくてOKになります。

package main
import "fmt"
func sumave(x, y int) (sum, ave int){
   sum = x + y
   ave = (x + y) /2
   return
}
func main(){
   fmt.Println(sumave(5, 9)) // 14 7
}

ただし、長い関数で使うと読みにくくなるため、短いわかりやすい関数でのみ使うべきです。また、例えばswap関数をreturn文なしで実装しようとして、下記のように引数と返り値で同じ変数を使うと、エラーになってしまいますのでご注意ください。

func swap(x, y string)(y, x string) { return }

duplicate argumentというエラーが発生します。


変数の宣言(Variables)

変数はvarを使って宣言することで使えます。

package main
import "fmt"

// 基本的にはvarと型で挟みます
var flag bool
var moji string
var i int
// 初期値を設定した場合は型を自動で判別してくれます
var num, word = 2, "hello"
// importと同様にまとめて宣言することもできます
var (
      hoge = "fuga"
      geho = "gafu"
)

func main() {
        fmt.Println(flag, moji, i, num, word, hoge, geho)
}

出力は下記になります。

false  0 2 hello fuga gafu

初期値を何も設定していない場合は、型に沿ったデフォルト値が入ります。(上記の結果でいうと、falseと0の間に実は空白があります。)

各型に対するデフォルト値は下記です。

  • bool: false
  • string: 空文字列
  • int: 0

関数の中では、省略した形で変数を宣言できます。varと型を書かずに:=を使って定義するだけです。

package main
import "fmt"
func main(){
   num := 3
   fmt.Println(num) // 3
}

ただし、関数の外ではこの省略形は使えず、エラーになります。

package main
import "fmt"
num := 3
func main(){
   fmt.Println(num) // 3
}

上記のようにmain関数の外で変数を定義すると、

syntax error: non-declaration statement outside function body

というエラーが出ます。外で定義したい場合は、var num = 3とする必要があります。


定数の使い方

定数はconstを使って宣言できます。ただし、:=による宣言はできません。(変数として扱われます。)

package main
import "fmt"
const Pi = 3.14
func main() {
    const user = "太郎" 
    num := 3 // 変数扱いになる
    
    // user = "次郎" // cannot assign to userというエラーになる
    num = 5 // これはエラーにならない、つまり変数

    fmt.Println("Hello", user)
    fmt.Println("Luckey number:", num)
}

出力は、

Hello 太郎
Luckey number: 5

になります。


for と if と switch

for

for ループはC言語などと同じくセミコロン ; で3つの部分に分けて書きます。

for 初期化 ; 条件式 ; 後処理 { ループ処理 }

例えば、下記のように書きます。

func main() {
        sum := 0
        for i := 0; i < 10; i++ {
                sum += i
        }
        fmt.Println(sum)
}

初期化と後処理の部分は省略可能です。

sum := 1
for ; sum < 100; {
    sum += sum

セミコロンすら省略可能ですので、while文をforを使って表現できます。

sum := 1
for sum < 100 {
         sum += sum
}

if

基本的な書き方は他の言語とほぼ同じです。

func checkNum(x int) string {
        if x < 0 {
                return "minus"
        } else if x > 0 {
                return "plus"
        } else {
                return "zero"
        }
}

func main() { fmt.Println(checkNum(4)) }

条件の前に、変数の定義を書くことができます。ただし、その変数のスコープはif文の中だけです。

if v := math.Pow(x, n); v < limmit { // Powはべき乗の計算
  処理
}

switch

条件に一致したcaseのみを実行します。そのため、break文は不要です。どのcaseにも当てはまらなければdefaultが実行されます。

例えば下記のように使います。

func main() {
        fmt.Println("When's Saturday?")
        today := time.Now().Weekday()
        switch time.Saturday {
        case today + 0:
                fmt.Println("Today.")
        case today + 1:
                fmt.Println("Tomorrow.")
        case today + 2:
                fmt.Println("In two days.")
        default:
                fmt.Println("Too far away.")
        }
}

条件を省略することも可能です。

func main() {
        t := 10
        switch {
        case t < 10:
                fmt.Println("A digit")
        case t < 100:
                fmt.Println("Double-digits")
        default:
                fmt.Println("Over three digits")
        }
}

上記の例はif文でも表現可能です。

func main() {
        t := 10
        if t < 10 {
                fmt.Println("A digit")
        } else if t < 100 {
                fmt.Println("Double-digits")
        } else {
                fmt.Println("Over three digits")
        }
}

参考

続きは…

part2はこちらです↓

sagantaf.hatenablog.com