0
0

More than 1 year has passed since last update.

TourOfGoをやってみた

Last updated at Posted at 2023-03-29

GOの基本が知りたくなった

以前、少し書いた(多分interfacesの辺り)が手違いで消してしまった。
やる気が失われたので、基礎をすっ飛ばして
自分が欲しい物を作ってみて学習するスタイルへシフトした。
いや、基本は学んどこう。と反省して復習兼ねて最初からもう一度やる。

TourOfGoやる

Packages

GOのプログラムはpackageで構成される。
プログラムのエントリポイントはmainパッケージである必要がある。らしい

パッケージ名はインポートパスと同じ名前にする必要がある。
package/hoge という場所に作るものはhogeというパッケージにする。

Imports

書き方は2種類

  • グループ化して書く方法(基本的にこれでよかろう)
import (
	"fmt"
	"math"
)
  • 1行ずつ書く方法
import "fmt"
import "math"

Exported names

goにおいて最初の文字が大文字で始まるものは外部から参照可能なものである。という意味になる
(他の言語でいうところのパブリックですね)

package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println(math.pi)//←これはありえない。「math.Pi」になる
}

Functions

関数の書き方

基本

func 〜〜〜で書く。
多くの多言語との違いとして引数と戻り値の型を名前の後ろに書く。という所。

func add(x int, y int) int {
	return x + y
}

同じ型の引数が2つ以上ある場合は省略できる

func add(x int, y int, a string, b string) int {
って書いてエラーではないが
のように書けば良い
func add(x, y int, a, b string) int {
	return x + y
}

複数の戻り値を返す関数

()で囲んで複数書く

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

Named return values

関数の宣言部分で戻り値の変数を宣言できる。

func split(sum int) (x, y int) {//←ここで宣言されているので内部では宣言せずに利用できる
	x = sum * 4 / 9
	y = sum - x
	return x, y
    //↑これは実は省略できる。
    //↓省略パターン。return x, y と同意味。
    return
    //変数名のチェックまでは行われず型の一致だけチェックされる。
    return 1, 1
    とか
    return y, x
    とかでもOK
}

(正直意味わからなくなりそうなのであまり使いたくないかも。と思った)

Variables

変数宣言
varです。
同じ型の変数はカンマで連結して複数一括で宣言できる(引数と一緒)

var c, python, java bool

Variables with initializers

変数の初期化子の話

var i, j int = 1, 2

この時に型を省略することが出来ます。との事

var i, j = 1, 2
//この際には初期化子から型が推論され自動セットされる。
//goをかじったばかりの私はi, j := 1, 2 って出来ないの?と思ってやったら出来なかった。
//↑関数の中ならできる。関数外(パッケージ直下)では出来ない
//関数の外で変数を宣言するときはvarが必要。

Short variable declarations

関数の中ならvar省略して宣言できますよ。の話。

func main() {
	var i, j int = 1, 2
    //↑こう書き換える事が出来る
    //i, j := 1, 2
	k := 3
	c, python, java := true, false, "no!"

	fmt.Println(i, j, k, c, python, java)
}

Basic types

型の話

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 の別名

rune // int32 の別名
     // Unicode のコードポイントを表す

float32 float64

complex64 complex128

Zero values

型によって初期値が決まっているから初期化子を与えずに変数宣言してもnullにはならんよ。という話
型ごとの初期値

数値型(int,floatなど): 0
bool型: false
string型: "" (空文字列( empty string ))

Type conversions

型変換の話。
基本 変換したい型(値) でできる。

//例
i := 42
f := float64(i)
u := uint(f)

Constants

定数の話。

定数は、文字(character)、文字列(string)、boolean、数値(numeric)のみで使える。
定数は const を使って宣言必須 (変数の様にvar省略して書く。という記法は無い)

package main

import "fmt"

const Pi = 3.14

func main() {
	const World = "世界"
	World = "hoge"
	fmt.Println("Hello", World)
	fmt.Println("Happy", Pi, "Day")

	const Truth = true
	fmt.Println("Go rules?", Truth)
}

For

ループの話。
GOではループはforのみ。
これがwhileでありforでありforeach。

func main() {
	sum := 0
    //いわゆるforとして使う場合はいわゆるfor,括弧がないくらいか
	for i := 0; i < 10; i++ {
		sum += i
	}
	fmt.Println(sum)
}

For continued

forの初期化と後処理ステートメントは任意です。
for ; i < 10; {
って書けますよ。さらに;が省略可能で
for i < 10 {と書けますよ。という話。

Forever

無限ループさせたい時の話。
条件を全部省略すれば無限ループになる

for true {
    //無限ループ内処理
}

としたくなるが

for {
    //無限ループ内処理
}

で良い

If

IFの話。

if x < 0 {
    return sqrt(-x) + "i"
}

これだけ。括弧は不要

If with a short statement

if文の中にもforみたいなステートメントが書けますよ。という話

	if v := math.Pow(x, n); v < lim {
		return v
	}
    //このvはifのカッコ内だけが使える。

If and else

if文ないのステートメントで宣言している変数はelseでも使えますよ。という話

	if v := math.Pow(x, n); v < lim {
		a := 1
		return v
	} else 
		fmt.Printf("%g >= %g\n", v, lim)
	}

Switch

breakは無いんだよ。という
(各caseのセクションが終わると自動的にブレイクする)

func main() {
	fmt.Print("Go runs on ")
	switch os := runtime.GOOS; os {
	case "darwin":
		fmt.Println("OS X.")
	case "linux":
		fmt.Println("Linux.")
	default:
		// freebsd, openbsd,
		// plan9, windows...
		fmt.Printf("%s.\n", os)
	}
}

Switch with no condition

条件のないSwitch

IF~ELSE~IF~ELSE~IF系をわかりやすく書くことが出来る(かも)

    if t.Hour() < 12 {
		fmt.Println("Good morning!")
    } else {
        if t.Hour() < 17 {
    		fmt.Println("Good afternoon.")
        } else {
    		fmt.Println("Good evening.")
        }
    }
    //↑みたいな腐ってきているIFをスッキリと書くことが出来る↓

	switch {
	case t.Hour() < 12:
		fmt.Println("Good morning!")
	case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
	}

Defer

deferをつけて呼び出した関数はfunctionの最後に実行される。

func main() {
	defer fmt.Println("world")

	fmt.Println("hello")
}

Stacking defers

関数内で複数Deferが使われる場合、それらはスタック(後入先出、LIFO)されて最後に実行される。
最初いきなりTourOfGoやったときは、これ何に使うん??って思ったけど普通に便利だった。
多言語でのfinaly的な用途で使える。

例えば↓ファイルAをファイルBに書き出すfunction
それぞれファイルを開いた後にすぐdeferをつけてファイルクローズの関数を呼んでいる。
これで関数終了時にはファイルがクローズされる事が約束される。
(いや、何に使うん?からあら便利ねーっに変わった)

func main() {
	var reader *bufio.Reader
	var writer *bufio.Writer

	read_file, _ := os.OpenFile("A", os.O_RDONLY, 0600)
	defer read_file.Close() //←最後にファイルAを閉じる
	reader = bufio.NewReader(read_file)

	write_file, _ := os.OpenFile("B", os.O_WRONLY|os.O_CREATE, 0600)
	defer write_file.Close() //←最後にファイルを閉じる
	writer = bufio.NewWriter(write_file)

	for {
		line, err := reader.ReadBytes('\n')
		if err == io.EOF {
			return
		}
		writer.Write(line)
		writer.Flush()
	}
}

Pointers

ポインタの宣言

*(アスタリクス)で宣言する。

var p *int //pはint型の変数を指すポインタである。

変数のポインタをポインタ変数に代入する時

&を使う
i := 42
p = &i //&をつけることでpにはiのポイントが入る。

ポインタの先にある値(変数の中身を使いたい時)

*を使う

fmt.Println(*p) // ポインタpを通してiから値を読みだす
*p = 21         // ポインタpを通してiへ値を代入する

Structs

構造体。(最初クラスか?と思ったけどクラスとはちょっと話が違う様だ…)

type Vertex struct {
	X int
	Y int
}

Struct Fields

ドットを使うことでプロパティにアクセスできます。

type Vertex struct {
	X int
	Y int
}

func main() {
	v := Vertex{1, 2}
	v.X = 4
	fmt.Println(v.X)
}

Pointers to structs

構造体のフィールドはポインタを通してアクセスすることが出来る
↓どういうことか

func main() {
	v := Vertex{1, 2} //変数としての実態はv
    p := &v           //pにはvのポインタが入っている
	p.X = 1e9         //←これ
    //↑普通に考えるとpはポインタであってstructではないのだからp.Xなんてエラーになりそうなもんだが使える。
    //↑このp.Xは(*p).Xと書くことも出来る。これはまぁそうだろうな。という感じ
    //↑これでは面倒くさかろう(まぁ確かに…)ということで、p.Xでも使えるようにしてくれている模様。

	fmt.Println(v)
}

Struct Literals

structリテラルは、フィールドの値を列挙することで新しいstructの初期値の割り当てを示しています。

正直、日本語がよくわからない…

↓どういうことか

type Vertex struct {
	X, Y int
}

var (
    //↓これらのこと
	v1 = Vertex{1, 2}  // has type Vertex
	v2 = Vertex{X: 1}  // Y:0 is implicit
	v3 = Vertex{}      // X:0 and Y:0
	p  = &Vertex{1, 2} // has type *Vertex
)

func main() {
	fmt.Println(v1, p, v2, v3)
}

構造体を使う時の初期化方法の話だった
Vertex{1, 2}ただ値をカンマで区切ると宣言順に格納
Vertex{X: 1}フィールドの値を指定して初期化することもできる
Vertex{}していなければ対象の型の初期値が入る(例だとintなので0)
&Vertex{1, 2}実体となった構造体のポインタを返す

Arrays

配列の話

[n]T 型は、型 T の n 個の変数の配列( array )を表します。

頭が悪いのでこういう書き方をされるとすぐわからなくなる。
↓例を見たらすぐわかった。

var a [10]int

配列は 要素数+型で宣言する
int型の配列[10]int

//varを省略する場合{}は必須
a := [2]int{}
//→[0 0]
a := [2]int{1,1}
//→[1 1]
a := [2]int{1}
//→[1 0}

Slices

可変長の配列の話
宣言時に要素数を指定すると配列、葉数を指定しないとスライスになる。

func main() {
    //配列
	primes := [6]int{2, 3, 5, 7, 11, 13}

    //配列からスライスに
	var s []int = primes[1:4] //配列名[スタートインデックス:終了インデックス]で配列から指定した範囲のスライスを生成出来る
	fmt.Println(s)
}

Slices are like references to arrays

配列から値を引っ張った場合に、スライスに入っている値はコピーではなく参照なんですよ。という話
スライスの値を変更すると参照元の配列も変更される。

Slice literals

	q := []int{2, 3, 5, 7, 11, 13}

Slice defaults

スライスするときの省略形について

var a [10]int
//↓は全て同じ意味
a[0:10]
a[:10] //最初の数字を省略すると先頭を指したのと同意になる
a[0:] //2番めの数字を省略すると最後尾を指したのと同意になる
a[:] //なので両方省略すると全部

Slice length and capacity

スライスは長さ( length )と容量( capacity )の両方を持っています。という話

スライスの長さとは

スライスの要素数のこと

スライスの容量とは

スライスの最初の要素から数えたときの配列の要素数

サンプルコード

package main

import "fmt"

func main() {
    //printSliceでlen(長さ)cap(容量)スライスの中身を出力する。

	s := []int{2, 3, 5, 7, 11, 13}
	printSlice(s)
	//len=6 cap=6 [2 3 5 7 11 13]
    //↑配列{2, 3, 5, 7, 11, 13}から作成した長さ6、容量6のスライス

	// Extend its length.
	s = s[:2]
	printSlice(s)
	//len=2 cap=6 [2 3]
    //↑最初から2個目までで再スライスする。

	// Drop its first two values.
	s = s[2:]
	printSlice(s)
	//len=0 cap=4 []
    //↑2番目以降から最後までで再スライスすると何もなくなった
	
	s = s[:4]
	printSlice(s)
	//len=4 cap=4 [5 7 11 13]
    //↑値がなくなったように見えるが、省略形ではなく数値を指定したやると後方の値は復活する
    //おそらくもとになる配列が残っているから(多分)ポインタのため、後方には戻れない模様
}

func printSlice(s []int) {
	fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

Nill slices

sliceの初期値に関してはnil(NULL)になってます。という話。

Creating a slice with make

sliceはmakeで作れるんです。という話

	a := make([]int, 5)
    //[0 0 0 0 0]

    //3番目の引数としてスライスの容量を指定できる
	b := make([]int, 0, 5)
    //[]

Appending to a slice

スライスへ要素を追加するにはappendを使うんだよ。という話

Range

foreachみたいな事する時の話。
(途中)
(続く)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0