Go言語と螺旋本で学ぶデータ構造とアルゴリズム(その0)


はじめに

「プログラミングコンテスト攻略のためのアルゴリズムとデータ構造」(通称:螺旋本)をGo言語で解いていくシリーズです。個人の勉強が目的で、主な目標は以下のとおりです。

  • Go言語に慣れる
  • アルゴリズムとデータ構造の理解を深める
  • 上記を理解した上で、競技プログラミング用のライブラリを作成する

なお、Go言語の基本文法については特に細かく書かないつもりです。

標準入出力

競技プログラミングをやる上で避けては通れないのが標準入出力だと思うので、今回はここについてまとめたいと思います。

簡単な問題であれば前回の記事でご紹介したfmt.Scan()及びfmt.Println()で十分なのですが、入出力の数が多くなると問題が発生するようなので、今回は別の方法を紹介します。

標準入力

bufio.Scannerを使います。
使い方としては、まずScan()で1行を読み取ります。Scan()は標準入力があればtrue, なければfalseを返します。その後にText()で文字列を返すことができます。

package main

import (
    "bufio"
    "fmt"
    "os"
)

// Scannerを生成
var sc = bufio.NewScanner(os.Stdin)

func main() {
    var a string
    if sc.Scan() {
        a = sc.Text()
    }

    fmt.Println(a)
}

整数や小数で受け取る場合はstrconv.Atoi()strconv.ParseFloat()で変換する必要があります。

これらはよく使う処理なので、関数にしておきます。なお、競技プログラミング用なので、戻り値(エラー、真偽値)の確認は割愛しています。

func next() string {
    sc.Scan()
    return sc.Text()
}

func nextInt() int {
    i, _ := strconv.Atoi(next())
    return i
}

func nextFloat() float64 {
    f, _ := strconv.ParseFloat(next(), 64)
    return f
}

複数行から受け取る場合の関数も作成しておきます。戻り値はスライスにしておきます。

func nextInts(n int) []int {
    ret := make([]int, n)
    for i := 0; i < n; i++ {
        ret[i] = nextInt()
    }
    return ret
}

func nextFloats(n int) []float64 {
    ret := make([]float64, n)
    for i := 0; i < n; i++ {
        ret[i] = nextFloat()
    }
    return ret
}

func nextStrings(n int) []string {
    ret := make([]string, n)
    for i := 0; i < n; i++ {
        ret[i] = next()
    }
    return ret
}

今までは行区切りで受け取っていましたが、競技プログラミングではスペース区切りの入力が与えられることも多いです。bufio.Scanner.Split(bufio.ScanWords)を事前に実行しておくと、これに対応することができます(Scan()が改行までではなく、スペースまでを読み取るようになります)。また、bufio.Scanner.Split(bufio.ScanLines)を実行すると行区切りに戻すことができます。

いつでも切り替えができるように、これも関数にしておきます。

func splitSpace() {
    sc.Split(bufio.ScanWords)
}

func splitLine() {
    sc.Split(bufio.ScanLines)
}

標準出力

bufio.Writerを使います。
これはfmt.Fprintlnfmt.Fprintfと併せて使います。fmt.Printlnfmt.Printfと使い方はほとんど同じですが、ここでは第一引数にbufio.Writerを与えてから出力するデータやフォーマットを与えます。
また、最後に必ずbufio.Writer.Flush()を実行する必要があります。忘れないようにdeferで先に書いておくのがよいかもしれないです。

package main

import (
    "bufio"
    "fmt"
    "os"
)

// Writerを生成
var wr = bufio.NewWriter(os.Stdout)

func main() {
    defer wr.Flush()
    fmt.Fprintln(wr, "Hello, World!")
}

参考

まとめ

今回は競技プログラミングの問題を解く上で必須と思われる標準入出力についてまとめました。
次回からは実際に問題を解いていきたいと思います。