よーでんのブログ

One for All,All for わんわんお!

AtCoderの入力受取で躓いた話

ABC170_Cにて

GolangAtCoderやってるときに躓いたので書く。

ABC170のC問題を解き、自信満々に提出したときの話。
3AC,12WA。入力例にあげられているサンプルも2つ、WAをだしていた。

「提出する前に確認したはずなのにどうして」と再度確認するも、サンプルは正答できている。

手元と提出したもので結果が変わっている

試しに「コードテスト」のページで実行してみる。
(今回の件に関係ない処理を省略した、入力を受け取って出力するだけのコード)

// 警告: このコードを長時間直視しないでください。
package main

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

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    var n int
    scanner.Scan()
    fmt.Sscan(scanner.Text(), &n)
    p := make([]int, n)
    for i := range p {
        fmt.Scan(&p[i])
    }
    fmt.Printf(p)
}

/* input
5
4 7 10 6 5
*/

「これは酷い」と思うかもしれない。というか改めて見ると自分でも引くくらい酷い。
コードがぐちゃぐちゃなのは許してほしい。

nを受け取る時点では「scannerでサッと読み込もう」としたものの、
[]intscanerで受け取るのが面倒で結局fmt.Scanを使ってる。
(scannerだと、[]string[]intに変換する必要がある)

普通に書いてたらこんな統一性のないことにはならないだろう。
ただ、私は既に書いてあるnの受け取りを統一するのが面倒だった。

inputの値をセットして、実行してみる。
手元で出力されるのは[4 7 10 6 5]。しかし、コードテストでは[0 0 0 0 0]だった。

ちなみにこれは2つの入力受け取りをscannerfmt.Scanどちらかに統一することで解決できる。
ぐちゃぐちゃだからこそ今回の事件(?)が起きた。

なぜ結果が変わるのか

最初に疑うのはバージョンの壁。
試してみる。

$ go version
go version go1.14.4 linux/amd64

ローカルのバージョンは1.14.4。

package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Println(runtime.Version())
}

/* output
go1.14.1
*/

AtCoder上のバージョンは1.14.1。ちょっとだけ違う。
一応試す。

$ goenv install -l
(snip)
 1.14.1
 1.14.2
 1.14.3
 1.14.4

1.14.1があることを確認する。

$ goenv install 1.14.1
$ goenv global 1.14.1

1.14.1をインストール、バージョンを切り替える。
goenv rehashとかで反映できるっぽいですが、私はBashを開き直しました。

$ go version
go version go1.14.1 linux/amd64

しっかりバージョンが変わっている。さて、実行してみよう。

$ go run main.go 
5
4 7 10 6 5
[4 7 10 6 5]

ちゃんと受け取れる。

ふむ。。。。

「じゃあ実行方法が違うのかな」となり、
とあるサイトで競プロ的なことをしたときに制約として以下のようなことが書いてあったことを思い出した。

提出されたコードは以下のように実行されます。
go run main.go < input.txt

(go runではなかったかも。詳しくは覚えてない)
AtCoder上でも同じかどうかはわからないが、これをローカルで試してみる。

$ cat > input.txt
5     
4 7 10 6 5
$ go run main.go < input.txt
[0 0 0 0 0]

ビンゴ。
2列目の入力が受け取れなくなってる。

これが今回の原因。

あとがき

今回のようなぐちゃぐちゃなコードでも書かない限り遭遇しないと思うので、綺麗なコードを書こう。