この「コード」を額に入れて飾りたいkunai_S.N.
はじめに
近況報告になりますが、サムネイル制作者兼HP管理者だったましろさんが退会されたことで、私が今年の4月からましろさんのポジションにそのままスライドする形になりました。ですので、4月1日の「紫電改パート2」の動画からサムネイル制作を担当しています。最初は全く使い方が分からなかった「Adobe Illustrator」ですが、最近はある程度基本的なことはわかったような気がします。ましろさんからいただいたテンプレートを元に、毎回楽しくサムネイルを作っています。
Golang
最近はプログラミング言語のGoにハマっていて、Javaから浮気しそうな感じです。言語としては、Javaよりは若干レベルが低い感じがしました。ポインタの概念がある程度には低レベルですね。Javaより低レベルですけど、勉強の難しさはJavaより高い感じがします。Java8のドキュメントは日本語で転がっていますし、Javaの解説ブログ、動画の類いもたくさんあります。しかし、Goのドキュメントは多分英語しかないので、標準ライブラリの仕様を見に行く時に若干めんどくさいのです。まあ、日本語の解説ブログが全くないわけではないので、色々探してみると参考になるものがたくさんあったりします。
・Java8のドキュメント(日本語)
https://docs.oracle.com/javase/jp/8/docs/api/overview-summary.html
・Goのドキュメント(英語)
このブログを書いている10日くらい前から勉強を始めましたが、色々な方法で勉強しています。大学の課題を捌きつつ別のこともやっているので牛の歩みですが、主に下記の2種類の方法をやっています。
・「A tour of Go」をすすめてみる。
・競プロ(「AtCoder」とか「Paiza」)をやってみる。
「A tour of Go」
日本語で書かれたGo言語のチュートリアルです。ブラウザ上で記述して実行して結果が得られるので、実行環境をインストールしなくても使えるというのが利点でしょうか。現時点で、大体半分くらいやりました。
https://go-tour-jp.appspot.com/welcome/1
競プロ
以前Javaで書いたものをGoで書き直すとか、普通に新しい問題をやったりしています。あ、もちろん難しい問題は、そもそもJavaですら書けないのでやってません。Paizaで言うところのD,C,B問題、AtCoderの「AtCoder Beginner Contest」のA,B問題あたりをやっています。
この「コード」
結論から言えば、このブログのタイトルの「コード」とは、標準入力を受け取る一連の処理のことです。このブログを額に見立てて最後に飾っておきましょう。
勉強を始めてから昨日までは「fmt.Scan(&n)」で何も問題がなかったのですが、今日解いた競プロの問題では使えなかったのですよね。短い文字列であれば問題なくて、長いと不具合が生じるのではないかと、私は思ったりしました。これを解決するために、いろいろネットの海をさまよって、それっぽいコードを見つけてきました。しかし、意外とこのコードが複雑で理解に少し時間がかかってしまいました。
そもそも、標準入力から文字列を受け取るというのは競プロの問題では初歩の初歩といいますか、前提条件、出来ないやつは提出することを許さず、的なものです。したがって、この理解したものを消してしまうと、次に書く時に覚えていられる不安になりました。そういうわけで、この初心者コードを貼り付けておきたいと思うに至ったわけです。
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
// 標準入力を受け取るスキャナをつくる
scan := bufio.NewScanner(os.Stdin)
// 行区切りの読み込みから空白区切りの読み込みに変更
scan.Split(bufio.ScanWords)
// 一回読み込む、(1 10 100)であれば 1 だけ
scan.Scan()
// 一番新しい読み込んだトークンを、文字列で戻す
var str string = scan.Text()
// 文字列を数字に変換する。戻り値が(int, error)なので、エラーを捨てる
num, _ := strconv.Atoi(str)
// 表示
fmt.Println(num + 1)
}
追記 2023年5月26日
また壁にあたりました。
この額縁に入れて飾ったコードを少し修正する必要があると思ったので、少し追記します。
入力が多い時の対応
上記で飾ってあるコードは、標準入力から大量のデータを受け取るために作った物でした。コメントで言及いただいた記事や簡単に調べた感じ、違いはバッファーを持っているか持っていないか、らしいです。それにしても、「バッファー」って何なのでしょうか? パソコンに余り詳しく無いので、深く調べようとすると難しすぎて何も分からないか、戻って来られない可能性がありそうです。Javaのドキュメントの読み方はある程度わかるのですが、Go言語のそれは全然違うので、3時間程度で理解を諦めて切り上げてきました。「scan.Text()」ではトークンをそのまま戻してるだけなのですが、「scan.Scan()」では「scan.split()」を呼んで、トークンにデータを入れているようです。この関数には、「bufio.ScanWords」を指定しているのですが、これは引数に取ったデータをスペースまで読んでいるだけみたいです。
結局「NewScanner(os.Stdin)」で与えた引数が、スキャナ構造体中のメンバとかフィールドとか何かそういったものを「r」に格納しているだけなので、「scan.Scan()」から「r」を呼んでいる場所を探せば良いことに気がつきました。実際、「n, err := s.r.Read(s.buf[s.end:len(s.buf)])」という感じで呼ばれていました。しかしこの「r」は「io.Reader」の構造体(多分)なので、また別のソースコードを読みにいかなければならないことに気がつき、面倒くさくなって切り上げることになりました。
ごちゃごちゃしたものを色々書きましたが、今回追記したいことは入力のことでは無いので、この辺りで話は終わりにしましょう。
出力が多い時の対応
今回修正したいと思ったものは、出力関連のものです。入力が多いと実行時間が長くなってしまうというのは分かっていましたが、出力もまた、あまりにも多いと実行時間が長くなってしまうのです。解決方法としては、バッファーとやらを挟んでやるために「bufio」を使ってやるだけだったので、そこまで難しいことではなかったです。その辺りを修正したものを、もう一度下のところに置いておきましょう。
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
// 標準入力を受け取るスキャナをつくる
scan := bufio.NewScanner(os.Stdin)
// 行区切りの読み込みから空白区切りの読み込みに変更
scan.Split(bufio.ScanWords)
// 一回読み込む、(1 10 100)であれば 1 だけ
scan.Scan()
// 一番新しい読み込んだトークンを、文字列で戻す
var str string = scan.Text()
// 文字列を数字に変換する。戻り値が(int, error)なので、エラーを捨てる
num, _ := strconv.Atoi(str)
// 標準出力のためのものを作る
writer := bufio.NewWriter(os.Stdout)
// メイン関数が終了するときに出力する
defer writer.Flush()
// 出力するものを溜めておくやつ
fmt.Fprintln(writer, num)
}
この記事をシェアする
コメント
自分も気になったので調べてみました。
入力されるデータ数が多いとfmt.Scanは使えないっぽいですね。
↓以下の記事がとても参考になりました。
https://qiita.com/ren510dev/items/38fe6d09831d08fde537