0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Goの基礎知識(概念や、プログラミング言語共通の考え方)(自分リファレンス用)

Last updated at Posted at 2021-02-05

#注意
更新型の記事です。
筆者の学習に合わせて更新されていきます。

Goのインストール

Golangより、ファイルをダウンロードし、手順に従いインストール
(mac, windows, linux全てに対応)

Goを始める

(Mac)
ターミナルで、絶対パスを利用し

cd /usr/local/go

へ移動、goディレクトリで

go version

と入力し、goのバージョンを確認できたら、インストールが完了している。

実際のコード

公式チュートリアルを参照
https://golang.org/doc/tutorial/getting-started

  1. ホームディレクトリに移動し
mkdir hello

と入力し、helloディレクトリを作成し、

cd hello

で、helloディレクトリへ移動

2.利用しているテキストエディター(vscodeやvimなど、ほとんどのテキストエディターに対応しているらしい)で、hello.goファイルを作成し、以下のコードを入力

hello.go
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
  • package main: パッケージは機能の塊の単位。mainパッケージを宣言している。
  • import "fmt": テキストをコンソールに出力する機能を持った、テキスト生成のためのパッケージをインポート
  • func main(): コンソールに出力するための処理を記述

fmtパッケージに定義されたPrintf関数をコールします。

なお、importが二つ以上になった場合は

hello.go
import (
  "errors"
  "fmt"
)

という風に、まとめる事ができる。

変数定義

hello.go
 //これが一番省略形
  message := fmt.Sprintf("Hi, %v. Welcome!", name) 
 //varで変数を定義、型を明記
  var message string 
  message = fmt.Sprintf("Hi, %v. Welcome!", name) 

一番省略形を用いた変数宣言は、自動で型を付与し、変数定義を行ってくれる。超すごい。
ただし...

これらは関数の内部でしか使用できません。

とのこと。

なお、一度定義しているにもかかわらず利用していない変数があると、エラーが起きるので注意

変数に値を代入・その値の更新

hello.go
  message = "Taro"

= は変数の代入・更新
:= は変数の定義

ロジック系

##配列

array

array.go
  var fruits [3]string
  fruits[0] = "apple"
  fruits[1] = "orange"
  fruits[2] = "strawberry"
  fmt.Println(fruits)
  //→[apple orange strawberry]

  primes := [6]int{2, 3, 5, 7, 11, 13}
  fmt.Println(primes)
  //→[2 3 5 7 11 13]

配列は、固定調とされている(?)

####slice

slice.go
package main

import "fmt"

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

	var s []int = primes[1:4] //slice, [ここから:ここまで]=[primesの1~3まで]
	fmt.Println(s)

  //→[3 5 7]
}

sliceはなの通り、範囲を指定して(スライスして)新たな配列を生成します。

新しい配列を作る事ができるに加えて、元の配列を操作することも可能です。

people.go

package main

import "fmt"

func main() {
	names := [4]string{
		"Saito",
		"Tanaka",
		"Yamada",
		"Fuji",
	}
	fmt.Println(names)

//→[Saito Tanaka Yamada Fuji]

	a := names[0:2]
	b := names[1:3]
	fmt.Println(a, b)
//→[Saito Tanaka] (0:2は、「2より下」を表す)
// [Tanaka Yamada](1:3は、「1以上3より下」を表す)


	b[0] = "XXX" //b[0]にXXXを代入
	fmt.Println(a, b)
	fmt.Println(names)

//→[Saito XXX]
// [XXX yamada]
//→[Saito XXX Yamada Fuji]
}

以上から、sliceで取得した配列の値を変えると、元の配列であるnameの値を変える事ができる事がわかります。つまり、この記事で説明されている「参照渡し」が、成立していると言えるでしょう。

####slice defaults

slice.go
a[0:10]
a[:10]
a[0:]
a[:]

以上のsliceの表現は、すべて同じ意味を表します。意味は
**「aの配列0番目から9番目(10より小さい番号)を取得し新たな配列を作成する」**です。

この事からsliceの範囲指定のデフォルト値について、3つのことがわかります。

  • 明示しない場合、最小値は0
  • 明示しない場合、最大値は元の配列の最後の値
  • 範囲を明示しない場合は、元の配の最初から最後まで

これは、A Tour of Goで、slice defaultというタイトルで紹介されています。

####sliceの長さと容量
長さは、配列にある実際の値の個数
容量は、その配列に値がいくつ入るかを示した値
結論、最小値の方をぶった切ると容量(cap)が減ります。
最大値からぶった切ると、長さ(len)だけ減ります。

len_cap.go
package main

import "fmt"

func main() {
	n := []int{1, 3, 6, 10, 15}
	printSlice(n)

	// 長さを0にします。
	n = s[:0]
	printSlice(n)

	// 長さを4にします。
	n = s[:4]
	printSlice(n)

	// 容量を二つ減らします。
	n = s[2:]
	printSlice(n)

}

func printSlice(s []int) {
	fmt.Printf("len=%d cap=%d %v\n", len(n), cap(n), n)
//len(n)はsliceした後の、cap(n)はsの容量、nは元々の配列です。

//→len=5 cap=5 [1, 3, 6, 10, 15]
// len=0 cap=5 []
// len=4 cap=6 [1, 3, 6, 10]
// len=2 cap=4 [6, 10] //ここで、容量を減らしています。

}

ちなみに、%vはその変数や配列に指定されているデフォルトの値を返すものみたいです。
(参照:Go言語 - %Tや%vの書式で出力される文字列

####append
sliceに新しい値を追加するときに用いる

appned.go
package main

import "fmt"

func main() {
	var s []int
	printSlice(s) //→len=0 cap=0 []

	// 0を追加します。
	s = append(s, 0)
	printSlice(s) //len=1 cap=1 [0]

	// 必要に応じて、sの容量も増えていきます。
	s = append(s, 1)
	printSlice(s) //len=2 cap=2 [0 1]

	// 一つ以上の値を一度に追加することもできます。
	s = append(s, 2, 3, 4, 5, 6, 7)
	printSlice(s) //len=8 cap=8 [0 1 2 3 4 5 6 7]
}

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

make

goにおける配列は、上記で挙げたように2種類あります。
arrayは、一度指定したら変更することができません。これを、固定調と言います。
対してsliceは、arrayの値を値を変えたり、元々のarrayの長さや要領も変えることができました。これを、可変調と言います。

ここで気付いた人もいるかもしれませんが、sliceを元の配列なしに作成することができるのではないかと疑問に思いました。つまり、sliceそのものを最初から作成するためにはどうすればいいのでしょうか。
結論、makeを使います。

make.go
package main

import "fmt"

func main() {
	a := make([]int, 5) //make([]int, 5)でsliceを作っています。今回のsliceは、値はすべて0, 長さ/容量が5というsliceです。
	printSlice("a", a)
        //→a len=5 cap=5 [0 0 0 0 0]

	b := make([]int, 0, 5)
	printSlice("b", b)
        //→b len=0 cap=5 []
}

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

という感じでsliceを作成することができました。

##条件分岐
####条件分岐(if)

hello.go
  if 条件式 {
  条件式がtrueだった時の処理
} else {
  条件式がfalseだった時の処理
}

条件分岐(if/if else)

hello.go
  if 条件式 {
  ifの条件式がtrueだった時の処理
} else if 条件式 {
  else ifの条件式がtrueだった時の処理
}

####条件分岐 (switch)

hello.go
  n = 2
  switch n{
    case A:
    処理
    case B:
    処理
    case C:
    処理
    default: 
    処理

この記述は、**「nがA/B/C/それ以外の時、処理を行う」**という意味。
nの存在が必要ない場合は、省略も可能

##繰り返し

hello.go
for i := 1; i <= 14, i += 1 {
  処理
}

一つづつ解説すると

for i := 1;

で、変数iを定義し、初期値(繰り返しのスタートの値)とする

i <= 14

どの範囲まで(今回は、iが14になるまで)を定義

i += 1

どのくらいのスピードで(この場合は、iが1ずつ増加する)

####range
forの繰り返し機能を、sliceやmapの値に適応させます。(Rubyのeachメソッドに限りなく近いと思います。)

range.go
package main

import "fmt"

var nums = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
  for i, v := range nums {
    fmt.Printf("2**%d = %d\n", i, v)
  }
}

rangeは、二つの引数を持ちます。何番目か?を示すindexと、その番目の値は何か?を示すvalueの2点です。
今回は、iに「何番目か」, vに「その番目の値は何か?」を代入する形です。

また、もしindexかvalueのどちらかが必要ない時は

range.go
package main

import "fmt"

var nums = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
  for _, v := range nums {           //iを_(アンダーバー)に変更すると、indexは処理用の値として渡されなくなります。
    fmt.Printf("%d\n", v)   //value(値)だけが繰り返されます。
  }

  for i := range nums {
    fmt.Printf("%d\n", i)  //indexだけが繰り返されます。
  }
}

map

map ≒ rubyのハッシュ(キーバリューストア)のようなもの

map.go

messages := make(map[string]string)
//[key]valueの順番で、それぞれ指定する。

とする事で、キーとバリューの型を指定する。

##mapの値をいじる

map.go
package main

import "fmt"

func main() {
  s := make(map[string]int)  //sに初期化した状態で代入

  s["English_points"] = 89 //89をEnglish_pointsに代入
  fmt.Println("Score", s["English_points"])  //→89
  s["Enlish_points"] = 59  //59をEnglish_pointsに再代入
  fmt.Println("Score", s["English_points"])  //→59

  delete(m, "English_points") //mのEnglish_Scoreキーを削除
  fmt.Println("Score", s["English_point"])  //→0

  v, ok := m["English_points"]  //vで値、okでキーが存在するかの結果をそれぞれ代入
  fmt.Println("Score", v, "Present?", ok)  //→Score 0 Present? false(English_pointsキーは削除されているから、falseが返される。)

}

関数

処理のまとまりのこと。

function.go
import "fmt"

func main() {
  question()
}

func question() {
  fmt.Println("質問を入力してください")
  fmt.Scan(&text)
  fmt.Printf("質問内容は%sですね", text)
}

今回、関数は二つ

  • main(questionを呼び出し)
  • question(質問を入力してもらい、それを確認する処理を担当)

このように、関数を別の関数内で呼び出す事ができる

引数と戻り値

function.go
import "fmt"

func main() {
  question(1, "なぜ空は青いのですか?")
}

func question(number int, text string) string {
  fmt.Println("Q%d: あなたの質問は%sですね", number, text)
  fmt.Scan(&text)
  fmt.Printf("%s", text)

  return "実は青く見えているだけなんだよ。"
}

さて、一つずつ解説を

まず、main関数の方で、実引数を伴わせながらask関数を呼び出します。

func.go
func main() {
  question(1, "なぜ空は青いのですか?")
}

今回は、1と”なぜ空は青いのですか?”という文章をask関数へ引数として渡しています。それぞれ、int型、string型ですね。そこで、受け取る側のask関数でも受け取る用意をします。

func.go
func question(number int, text string)

このようにquestionのカッコの中にint型のnumberと、string型のtextを仮引数として定義しました。
その仮引数を用いて、question関数内の処理を行います。

そして、その関数の最後の値(処理された結果の値)を、戻り値と言います。戻り値は今回は処理に関係なくreturnの先に定義されている”実は青く見えているだけなんだよ。”という文字列を返すことにします。この、returnの型を

func.go
func question(number int, text string) string {

の大括弧直前のstringで定義しています。

スコープ

rubyのローカル変数と同様、関数の中で指定された変数は、別の関数内で用いる事ができません。

defer

ざっくりいうと、関数の処理終わって直後に出力するように設定するよ。ってこと。

defer.go
package main

import "fmt"

func main() {
    defer fmt.Println("Sir")
    defer fmt.Println("world")
    
    fmt.Println("hello")
}

//出力結果
//→hello
// world
// Sir       

deferがある時は、下から上に読んでいくと出力の順番がわかる

deferへ渡した関数が複数の出力結果を持つ時

defer.go
package main

import "fmt"

func main() {
    defer fmt.Println("よーい")
    
    for i := 0; i < 10, i++ {
      defer fmt.Println(i) //for文で繰り返されるので、出力結果が複数になる。
    }
    fmt.Println("どん!")
}
//出力結果
//→よーい
// どん!
// 9 8 7 6 5 4 3 2 1 0 

複数あるときは、最後の出力結果から吐き出される(LIFO)
ちなみに、この状態をstackと呼ぶらしい。
※LIFO = Last_In_First_Out(最後から入り、最初で終わり)

//ポインタ(Pointer)
pointerは、変数がメモリ上で持つアドレスのところまでいき、いろいろ作業したりするイメージです。

pointer.go
package main

import "fmt"

func main() {
	y, k := 24, 4

	p := &y         // &をつけると、yのアドレスまでpが行ってくれます。
	fmt.Println(*y) // yまで行ったpに、*で「値を教えて」とお願いします。
	*p = 48         // yまで行った*pに、「yのアドレスにある(yという変数に格納されている)値を、48に変えて」と命令します。
	fmt.Println(y)  // pが変えてくれたyの値を出力します。

//出力結果
//→24
// 48
   
}

##構造体
こちらの記事によると

異なるデータの型の変数を一つにまとめたもの。

とあります。要は異なる型が集まった集合体のことです。RubyやPythonではクラスという概念があります。これは、なんらかの機能の塊を一つにまとめたものです。これに似た機能を、Goでは構造体が持ちます。

struct.go
package main

import "fmt"

type Student struct {
	name          string
	math, english float64
}

type User struct {
	gender string
	age    int
}

このように、"type 構造体名 struct {}"のなかに定義する変数を記述していきます。

参考文献

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?