いろいろ備忘録日記

主に .NET とか Go とか Python絡みのメモを公開しています。

Goメモ-149 (Goでのループの書き方)

概要

以下、自分用のメモです。

他の言語で開発した後にGo触ると、たまに「この場合のループの仕方どうやるんだっけ??」ってなるので、自分用にここにメモメモ。

基本形 (インデックス付き)

他の言語でもある for (var i = 0; i < xxx; i++) のやつ。

package loops

import "github.com/devlights/gomy/output"

// BasicForLoop は、他の言語でも同じように存在する基本的な for-loop についてのサンプルです.
func BasicForLoop() error {
    var (
        items = []string{
            "go",
            "java",
            "dotnet",
            "python",
            "flutter",
        }
    )

    // 他の言語と同じように Go にも インデックス 付きの for-loop がある
    for i := 0; i < 5; i++ {
        output.Stdoutf("", "[%d] %s\n", i, items[i])
    }

    return nil
}

実行すると以下のようになります。

$ make run
go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: loops_basic_for_loop
[Name] "loops_basic_for_loop"
[0] go
[1] java
[2] dotnet
[3] python
[4] flutter


[Elapsed] 48.508µs

基本形 (foreach)

他の言語にもある foreach なループについて。

package loops

import "github.com/devlights/gomy/output"

// BasicForeach は、Go での foreach ループについてのサンプルです.
func BasicForeach() error {
    var (
        items = []string{
            "go",
            "java",
            "dotnet",
            "python",
            "flutter",
        }
    )

    for i, v := range items {
        output.Stdoutf("", "[%d] %s\n", i, v)
    }

    return nil
}

実行すると以下のようになります。

$ make run
go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: loops_basic_foreach
[Name] "loops_basic_foreach"
[0] go
[1] java
[2] dotnet
[3] python
[4] flutter


[Elapsed] 83.908µs

基本形 (whileループ)

Go には、while (xxx) {} な書き方は出来ません。ループは全部 for で記載します。

package loops

import (
    "github.com/devlights/gomy/output"
)

// WhileLoop は、GoでのWhileループについてのサンプルです.
func WhileLoop() error {
    // Go には、ループはすべて for で記載することになっている。
    // 他の言語にある while () {} は提供されていない。
    count := 5
    for count > 0 {
        output.Stdoutl("[count]", count)
        count -= 1
    }

    return nil
}

実行すると以下のようになります。

gitpod /workspace/try-golang $ make run
go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: loops_while
[Name] "loops_while_loop"
[count]              5
[count]              4
[count]              3
[count]              2
[count]              1


[Elapsed] 2.15111ms

無限ループ

Goでの無限ループの書き方はちょっと独特。ラベルを使うことが多いです。

package loops

import (
    "context"
    "fmt"
    "time"
)

// InfiniteLoop は、無限ループのサンプルです.
func InfiniteLoop() error {
    var (
        mainCtx, mainCxl = context.WithCancel(context.Background())
        procCtx, procCxl = context.WithTimeout(mainCtx, 3*time.Second)
    )

    defer mainCxl()
    defer procCxl()

    // Go では 無限ループ は以下のように for {} とだけ書く
LOOP:
    for {
        select {
        case <-time.After(200 * time.Millisecond):
            fmt.Print(".")
        case <-procCtx.Done():
            break LOOP
        }
    }

    fmt.Println("")

    return nil
}

実行すると以下のようになります。

$ make run
go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: loops_infinite
[Name] "loops_infinite_loop"
..............
(200ms毎に . が一つずつ出力されていきます)

[Elapsed] 3.000759326s

単純に指定回数だけループ (Range Loop)

たまに指定した回数分だけループしたいときがあります。インデックスもいらない。

可能な限りメモリの消費も少なくしたい場合は、以下のようにすると便利かも。

package loops

import "github.com/devlights/gomy/output"

// RangeLoop は、単純に指定回数ループするためのサンプルです.
func RangeLoop() error {
    // 単純に指定回数だけループしたい場合、[]struct{} を作って
    // ループさせるのが効率が良い. struct{} はメモリを消費しない.
    for range make([]struct{}, 3) {
        output.Stdoutl("", "hello")
    }

    // インデックスが欲しい場合
    for i := range make([]struct{}, 3) {
        output.Stdoutf("", "[%d] hello\n", i)
    }

    return nil
}

実行すると以下のようになります。

$ make run
go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: loops_range
[Name] "loops_range_loop"
hello
hello
hello
[0] hello
[1] hello
[2] hello


[Elapsed] 96.172µs

元ネタは以下です。

github.com

私の場合は自分用に以下の関数とかを定義して使ったりしてます。

package iter

// Range は、指定された回数分ループ可能な空スライスを生成して返します。
//
// 元ネタは https://github.com/bradfitz/iter/blob/master/iter.go です。
func Range(n int) []struct{} {
    return make([]struct{}, n)
}

// RangeFn は、Range に処理関数を指定できるバージョンです。挙動は同じです。
//
// 処理中にエラーが発生した場合、内部ループはそこで停止し
// (エラーが発生したインデックス, エラー) を返します。
func RangeFn(n int, fn func(i int) error) (int, error) {
    for i := range Range(n) {
        err := fn(i)
        if err != nil {
            return i, err
        }
    }

    return -1, nil
}

gomy/iter.go at master · devlights/gomy · GitHub

スライス(Slice) をループ

package loops

import "github.com/devlights/gomy/output"

// SliceLoop は、スライスのループについてのサンプルです.
func SliceLoop() error {
    var (
        items = []string{
            "golang",
            "java",
            "dotnet",
            "python",
        }
    )

    // スライスの foreach は、インデックスと値 となる
    for i, v := range items {
        output.Stdoutf("", "[%d] %s\n", i, v)
    }

    return nil
}

実行すると以下のようになります。

$ make run
go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: loops_slice
[Name] "loops_slice_loop"
[0] golang
[1] java
[2] dotnet
[3] python


[Elapsed] 58.79µs

マップ (Map) をループ

package loops

import "github.com/devlights/gomy/output"

// MapLoop は、 map のループについてのサンプルです.
func MapLoop() error {
    var (
        m = map[string]string{
            "go":     "fmt.Println",
            "java":   "System.out.println",
            "dotnet": "Console.WriteLine",
            "python": "print",
        }
    )

    // map の ループ は、key, value の値が毎ターン取得できる
    for k, v := range m {
        output.Stdoutf("", "[%s] %s\n", k, v)
    }

    return nil
}

実行すると以下のようになります。

$ make run
go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: loops_map
[Name] "loops_map_loop"
[go] fmt.Println
[java] System.out.println
[dotnet] Console.WriteLine
[python] print


[Elapsed] 59.103µs

チャネル (Channel) をループ

package loops

import "github.com/devlights/gomy/output"

// ChannelLoop は、チャネルをループさせる場合のサンプルです.
func ChannelLoop() error {
    var (
        ch    = make(chan string)
        quit  = make(chan struct{})
        items = []string{
            "go",
            "java",
            "dotnet",
            "python",
            "flutter",
        }
    )

    go func(ch chan<- string) {
        defer close(ch)

        for _, v := range items {
            ch <- v
        }
    }(ch)

    go func(quit chan<- struct{}, ch <-chan string) {
        defer close(quit)

        // チャネルを foreach ループする場合, インデックスは付かない
        for v := range ch {
            output.Stdoutl("", v)
        }
    }(quit, ch)

    <-quit

    return nil
}

実行すると以下のようになります。

$ make run
go get -d ./...
go run -race github.com/devlights/try-golang/cmd/trygolang -onetime -example ""
ENTER EXAMPLE NAME: loops_channel
[Name] "loops_channel_loop"
go
java
dotnet
python
flutter


[Elapsed] 695.753µs

参考資料

tour.golang.org

golang.org


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com