プログラミング素人のはてなブログ

プログラミングも電気回路も専門外の技術屋の末端が勉強したことや作品をアウトプットするブログ。コードに間違いなど見つけられたら、気軽にコメントください。 C#、Python3、ラズパイなど。

paizaのレベルアップ問題集でGoの基本文法を習得する

Go 基本文法総ざらい

一通り使ってみたところで、基本文法を総ざらいするために、paizaのレベルアップ問題集(Bランクレベルアップ)をGoで解いてみました。
以下、すべてpaizaでの採点は100点となる回答例であり、基本文法を確認した結果です。
paizaではスキルチェックの回答は公開NGですが、レベルアップ問題集に関してはOKです。

5以上の整数の合計

5以上の整数の合計 | レベルアップ問題集 | プログラミング学習サイト【paizaラーニング】

改行区切りで整数がn個入力されるので、n個の整数のうち、5以上のものをすべて足し合わせた値を出力してください。

//if文の使い方、基本の数値計算をする問題
package main

import "fmt"

func main() {
	var n, a int
	ans := 0
	fmt.Scan(&n)
	for i := 0; i < n; i++ {
		fmt.Scan(&a)
		if a >5 {
			ans +=a
		}
	}
	fmt.Println(ans)
}

足すか掛けるか

足すか掛けるか | レベルアップ問題集 | プログラミング学習サイト【paizaラーニング】

2つの整数の組がn個与えられるので、各組の計算結果を足し合わせたものを出力してください。
各組の計算結果は次の値です。
・2つの整数の組を足し合わせたもの
・ただし、2つの整数が同じ値だった場合は、掛け合わせたもの

//入力を受取り数値としてif文を使う、また基本の数値計算をする問題
package main

import "fmt"

func main() {
	var n, a, b int
	ans := 0
	fmt.Scan(&n)
	for i := 0; i < n; i++ {
		fmt.Scan(&a, &b)
		if a == b {
			ans += a * b
		} else {
			ans += a + b
		}
	}
	fmt.Println(ans)
}

文字列を切り取る

文字列を切り取る | レベルアップ問題集 | プログラミング学習サイト【paizaラーニング】

スペース区切りの2つの整数と、文字列が入力されるので、2つの整数の範囲の部分文字列を出力してください。

/*
スライスの使い方とスペースを含む文字列の取得
また、Pythonに似た感じでスライス操作s.Text()[0:a-1] が可能。
*/
package main

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

func main() {
	var a, b int
	fmt.Scan(&a, &b)
	s := bufio.NewScanner(os.Stdin)
	s.Scan()

	fmt.Println(s.Text()[a-1:b])
}

指定範囲だけ大文字

指定範囲だけ大文字 | レベルアップ問題集 | プログラミング学習サイト【paizaラーニング】

文字列 s の a 文字目から b 文字目を大文字にして、文字列 s を出力してください。
文字列 s に含まれる記号やスペースは、変形せずそのまま出力します。
最後は改行し、余計な文字、空行を含んではいけません。

/* 
strings.ToUpper()という関数を使う。
また、スペースを含む文字列を取得するにはfmt.Scan()では使い勝手が悪く、s := bufio.NewScanner(os.Stdin)をつかうことで改行までを取得できる。
*/
package main

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

func main() {
	var a, b int

	fmt.Scan(&a, &b)
	s := bufio.NewScanner(os.Stdin)
	s.Scan()
	ans := s.Text()[0:a-1] + strings.ToUpper(s.Text()[a-1:b]) + s.Text()[b:]

	fmt.Println(ans)
}
//別解。置き換え前と置き換え後の配列(スライス)を用意してreplaceする。
arr := [...]string{"a", "b", "c", "d", ***}
arr2 := [...]string{"A", "B", "C", "D", ***}

s2 := s.Text()
for n, _ := range arr {
	replaced := strings.Replace(s2, arr[n], arr2[n], -1)
	s2 = replaced
	fmt.Println(replaced)
}

文字列の重複カウント

文字の重複カウント | レベルアップ問題集 | プログラミング学習サイト【paizaラーニング】

文字列 s の中に、文字 c が出現する個数を数えて出力してください。
最後は改行し、余計な文字、空行を含んではいけません。

//strings.Count(s, c)を使う
package main

import (
	//"bufio"
	"fmt"
	//"os"
	"strings"
)

func main() {
	var s, c string
	fmt.Scan(&c)
	fmt.Scan(&s)

	fmt.Println(strings.Count(s, c))
}

文字と整数の組のソート

文字と整数の組のソート | レベルアップ問題集 | プログラミング学習サイト【paizaラーニング】

1行目に行数を表す整数 n、続く n 行の各行で「文字」と「整数」の組が空白区切りで入力されます。
n 個の組を、「整数」の値で昇順に並べ変え、「文字」を出力してください。

/*
「文字」と「整数」を一旦Slice(リスト)にそれぞれ格納し、整数をsortする。
sortされたSliceを順番に取り出し、もとのSliceをdeep copyしたk2からその値を探しそのindexを取得する。
そのindexの「文字」を順番に出力する。
Pythonであれば辞書型に格納して、辞書をソートして順番に取り出せばいいが、Golangにはそのような機能が組み込みでは無い。
*/
package main

import (
	//"bufio"
	"fmt"
	//"reflect"
	"sort"
	//"strconv"
	//"os"
	//"strings"
)

func main() {
	var n,d int
	fmt.Scan(&n)
	var s string
	
	k := make([]int, 0)    //key : type int  Empty slice(List)
	v := make([]string, 0) //value :type string  Empty slice(List)

	for i := 0; i < n; i++ {
		fmt.Scan(&s, &d)
		
		k = append(k, d) // int
		v = append(v, s)
	}
	k2 := make([]int, len(k))
	copy(k2, k) //deep Copy <--> k2:=k はshallow copy
	sort.Slice(k, func(i, j int) bool {
		return k[i] < k[j]
	})
	tmp := 0
	for i := 0; i < n; i++ {
		for j := 0; j < n; j++ {
			if k[i] == k2[j] {
				//fmt.Println(k[i])
				tmp = j
				break
			}

		}
		fmt.Println(v[tmp])
	}
}

文字と整数の組のソート2

文字と整数の組のソート2 | レベルアップ問題集 | プログラミング学習サイト【paizaラーニング】

1行目に行数を表す整数 n、続く n 行の各行で「文字」と「整数」の組が空白区切りで入力されます。
n 個の組について、「文字」の値が同じ組同士の数値を足しあわせてまとめ、まとめた数値の降順で、文字とまとめた数値の組を出力してください。
この際、まとめた数値は重複しません。

/*
文字を保存するsliceと数値を保存するsliceを作成。
先のsliceに該当文字があれば対応する数値のlistに加算、なければ要素をそれぞれ追加
数値のsliceをshallow copyし、ソート。
順番に取り出し、もとの数値sliceから対応する要素indexを取得、その要素indexの文字sliceを取り出し出力。
今回は数値の重複はないが、ある場合は出力時にsliceから削除していく必要があるはず
*/

package main

import (
	//"bufio"
	"fmt"
	"sort"
	//"os"
	//"strings"
)

func main() {
	var n, tmpd int
	var tmps string
	s := make([]string, 0)
	d := make([]int, 0)

	fmt.Scan(&n)
	for i := 0; i < n; i++ {
		flag := false
		fmt.Scan(&tmps, &tmpd)
		//文字列のsliceを探索し、一致したら数値の同じindexに加算する
		for count, s0 := range s {
			if s0 == tmps {
				d[count] += tmpd
				flag = true
				break
			}
		}
		if !flag {
			//無ければ要素を追加
			s = append(s, tmps)
			d = append(d, tmpd)
		}
	}
	//数値のsliceをコーピーしてもとのsliceをソート
	d2 := make([]int, len(d))
	copy(d2, d)
	sort.Slice(d, func(i, j int) bool {
		return d[i] > d[j]
	})

	//ソートしたsliceから順番に取り出し、もとのsliceのindexを探す
	var count int
	for _, i := range d {
		for c, j := range d2 {
			if i == j {
				count = c
				break
			}
		}
		//あえてprintf、改行はバックスラッシュn
		fmt.Printf("%s %d\n", s[count], d2[count])
	}
}

アルファベット探し

アルファベット探し | レベルアップ問題集 | プログラミング学習サイト【paizaラーニング】

1行目の英大文字 X から、2行目の英大文字 Y の範囲に3行目のアルファベット C が含まれていれば"true", そうでなければ"false"と出力してください。
X が Y よりもアルファベット順で後ろになる場合(X = 'G', Y = 'F'のときなど)も"false"と出力してください。

//それぞれのアルファベットがアルファベットの何文字目に当たるかを算出し、比較する

package main

import (
	//"bufio"
	"fmt"
	//"sort"
	//"os"
	//"strings"
)

func main() {
	var x, y, c string
	//改行区切りでもまとめて取得できる(c++と同じ)
	fmt.Scan(&x, &y, &c)
	//変数 [i]でi文字目のアスキーコードを取得できる
	if x[0]<=c[0]&& c[0]<=y[0] {
		fmt.Println("true")
	}else{
		fmt.Println("false")
	}
}

五目並べ

五目並べ | レベルアップ問題集 | プログラミング学習サイト【paizaラーニング】

5行5列の五目並べの盤面が与えられます。
盤面の各マスには、"O"か"X"か"."が書かれています。
"O"と"X"は、それぞれプレイヤーの記号を表します。
同じ記号が縦か横か斜めに連続で5つ並んでいれば、その記号のプレイヤーが勝者となります。
勝者の記号を1行で表示してください。
勝者がいない場合は、引き分けとして、"D"を表示してください。

/*
2次元配列(slice)に盤面を格納する
縦横斜めにカウントする
*/

package main

import (
	//"bufio"
	"fmt"
	"strings"
	//"sort"
	//"os"
	//"strings"
)

func main() {
	var input string
	//m := make([][]string, 0)
	m := [][]string{}
	for i := 0; i < 5; i++ {
		fmt.Scan(&input)
		line := strings.Split(input, "") //sliceに1文字ずつ格納
		m = append(m, line)
	}

	//yoko
	for i := 0; i < 5; i++ {
		o := 0
		x := 0
		for j := 0; j < 5; j++ {
			if m[i][j] == "O" {
				o += 1
			} else if m[i][j] == "X" {
				x += 1
			}
		}
		if o == 5 {
			fmt.Println("O")
			return
		} else if x == 5 {
			fmt.Println("X")
			return
		}
	}
	//tate
	for i := 0; i < 5; i++ {
		o := 0
		x := 0
		for j := 0; j < 5; j++ {
			if m[j][i] == "O" {
				o += 1
			} else if m[j][i] == "X" {
				x += 1
			}
		}
		if o == 5 {
			fmt.Println("O")
			return
		} else if x == 5 {
			fmt.Println("X")
			return
		}
	}
	//naname
	o := 0
	x := 0
	for i := 0; i < 5; i++ {
		if m[i][i] == "O" {
			o += 1
		} else if m[i][i] == "X" {
			x += 1
		}
		if o == 5 {
			fmt.Println("O")
			return
		} else if x == 5 {
			fmt.Println("X")
			return
		}
	}
	//gyaku naname
	o = 0
	x = 0
	for i := 0; i < 5; i++ {
		if m[4-i][i] == "O" {
			o += 1
		} else if m[4-i][i] == "X" {
			x += 1
		}
		if o == 5 {
			fmt.Println("O")
			return
		} else if x == 5 {
			fmt.Println("X")
			return
		}
	}
	fmt.Println("D")
}

占い

占い | レベルアップ問題集 | プログラミング学習サイト【paizaラーニング】

次のような占いプログラムを作成してください。
「ユーザー」と「ユーザーに対応する血液型」、「血液型」と「血液型に対応する占い結果」が与えられます。
それぞれのユーザーに対応する占い結果を表示してください。
入力例の1つ目は、ユーザーの血液型についてのラッキーカラーの占いです。
入力例の2つ目は、ユーザーの星座についてのラッキーパーソンの占いになっています。
「血液型」として「星座」などのさまざまな文字列を利用できるようにすることで、他の占いにも対応する必要があることに注意してください。

/*
配列を使って線形探索する。
1行の入力を一次元配列tmp2として、これをappendして二次元配列を作成する。
*/

package main

import (
	//"bufio"
	"fmt"
	//"os"
	//"strconv"
	//"strings"
)

func main() {
	var n, m int

	fmt.Scan(&n)
	usr0 := make([][]string, 0)
	usr1 := make([][]string, 0)

	for i := 0; i < n; i++ {
		var tmp0, tmp1 string
		fmt.Scan(&tmp0, &tmp1)
		tmp2 := []string{tmp0, tmp1}
		usr0 = append(usr0, tmp2)
	}

	fmt.Scan(&m)
	for j := 0; j < m; j++ {
		var tmp0, tmp1 string
		fmt.Scan(&tmp0, &tmp1)
		tmp2 := []string{tmp0, tmp1}
		usr1 = append(usr1, tmp2)
	}

	for i := 0; i < n; i++ {
		fmt.Printf("%s ", usr0[i][0])
		for j := 0; j < m; j++ {
			if usr0[i][1] == usr1[j][0] {
				fmt.Printf("%s\n", usr1[j][1])
				break
			}
		}
	}
}
/*
2次元配列の作成時に以下の方法も可能に思えるが、LocalではOKだが、
paizaのテストではRuntime Errorや2周目以降値が取れないなどが発生。
原因不明。
*/
//別解
usr0 := make([][]string, 0)
usr1 := make([][]string, 0)
tmp := bufio.NewScanner(os.Stdin)
tmp.Scan()
input0 := strings.Split(tmp.Text(), " ")
usr0 = append(usr0, input0)
//別解2
var usr0 [100][2]string
var usr1 [100][2]string
tmp := bufio.NewScanner(os.Stdin)
tmp.Scan()
input0 := strings.Split(tmp.Text(), " ")
usr0[i][0] = input0[0]
usr0[i][1] = input0[1]

まとめ

Goの基本文法習得(を目指)しました。
Goは他の言語(特にPythonC++)と比較して、標準の関数が競プロを解くには「無い機能」が多く、想定されている解き方が簡単には実装できない部分が多くあるように思います。
(例えばPythonでは辞書は自動的にソートされているが、Goではそうではなく、しかも順番にアクセスしているつもりがランダムに出力される)

また、Goでは宣言したけど使われていない変数があるとErrorになります。
アルゴリズムを考えながら実装していると、結構こういうところで引っかかって、一旦コメントアウトするなどが必要だったりします。

./filename.go:17:11: xxxx declared but not used

このような場合、_=変数のように使われない変数に代入するとErrorを回避することができます。
mebee.info
Goの思想としては「アルゴリズムを考えてから実装しましょう、そのほうがBugが起きにくいですよ。」ということなのでしょう。