AtCoder に登録したら解くべき精選過去問 10 問を V言語 で解いてみた

2023/3/30追記：
**本記事には古い内容が含まれています。現在のV言語を用いた新しい記事を投稿したので、最新の内容をお求めの方は以下のリンクからどうぞ。**

https://zenn.dev/lemoncmd/articles/e3cfd9a21e6022

---

最近何かと話題のV言語が6月22日にオープンソースで公開されたので、生まれたてのV言語君にはABSをやってもらいます。

V言語ホームページ https://vlang.io/
V言語GitHub https://github.com/vlang/v
ABS(AtCoder) https://atcoder.jp/contests/abs

# AtCoderにV言語はないが？？？？？？？？？
V言語はC言語と密接に関わっていて、Cで使えるライブラリはほぼ使えたり、Cのソースコードを吐いたりしてくれます。この吐き出されたソースコードを貼っつけて実行します。~~50000byte~~365000byte（2021/7/1現在）とかになりますが、気にしたら負けです。

また、V言語は出力は優秀なんですが、標準入力は文字列しか取れません（2021/7/1現在）。ライブラリが整ったらそれを使ったソースコードに更新します。が、今は仕方がないのでCからscanfを引っ張ってきます。面倒くさい、-＾＾-まるでどこかのJavaScr

2019/11/9追記：V言語がAtCoderの言語アプデで入りそうなんですが、安定でない0.1.xで大丈夫なのか…？私的には0.2.xを待った方がいいと思われるが。

2021/7/1追記：0.2.x来てますが、文法もまだ揺れてるし0.3.x待ちですかね。まぁ大丈夫だとは思いますが。ライブラリも欲しいところ。

2023/3/27追記：Language UpdateでV言語が候補に上がっていてLanguage Test 202301でV言語を使うことができます。なお、この記事のコードは古いため現在のV言語ではコンパイルできない可能性があります。現在AtCoderで使えるV言語での解答例は[こちら](https://atcoder.jp/contests/language-test-202301/submissions?f.Task=&f.LanguageName=V&f.Status=AC&f.User=lemoncmd)で確認できます。

# 入力
C言語のライブラリの関数は`C.hoge`で書けます。stdio.hはもう入ってるのでincludeする必要はありません。
また、Cの文字列はbyteの配列ですが、~~V言語のstringとして扱うには文字列の長さを含んでラップする必要があります。~~
2021/7/1現在はもっといい方法としてV言語のbyteの配列の0番目のポインタをCに渡し、bytestrメソッドでstringに変換できます。

```
fn C.scanf(&char, ...voidptr) int

fn scan_int() int {
    scanned := 0
    C.scanf(c'%d', &scanned)
    return scanned
}

fn scan_string(max int) string {
    arr := []u8{len: max}
    unsafe { C.scanf(c'%s', &arr[0]) }
    return unsafe { tos_clone(&arr[0]) }
}
```
# 解答

## 0問目 [PracticeA - はじめてのあっとこーだー（Welcome to AtCoder）](https://atcoder.jp/contests/abs/tasks/practice_1)

```
fn main() {
    num := scan_int() + scan_int() + scan_int()
    hoge := scan_string(100)
    println('$num $hoge')
}
```
入出力を問われている問題です。

変数の宣言には`:=`を、代入には`=`を用います。
V言語は文字列連結をしたり、文字列にリテラルを埋め込んだりできるので出力に関してはとても優秀です。

## 1問目 [ABC086A - Product](https://atcoder.jp/contests/abs/tasks/abc086_a)
```
fn main() {
    a := scan_int()
    b := scan_int()
    println(if a*b%2 == 0 {'Even'} else {'Odd'}) 
}
```
簡単な数値演算です。

V言語のif文はRustやKotlinみたいな最近の言語らしく、三項演算子のように値を返すことができます。

## 2問目 [ABC081A - Placing Marbles](https://atcoder.jp/contests/abs/tasks/abc081_a)
```
fn main() {
    mut ans := 0
    m := scan_string(3)
    one := `1`
    for i in m {
        if i == one {ans++}
    }
    println(ans)
}
```
簡単な文字列処理です。

`1`のバイトコードの取得にはバッククォートを用います。
配列や文字列はfor...in構文が使えます。

## 3問目 [ABC081B - Shift only](https://atcoder.jp/contests/abs/tasks/abc081_b)
```
fn main() {
    mut max := 1000000
    n := scan_int()
    for _ in 0..n {
        mut hoge := scan_int()
        mut num := 0
        for {
            if hoge%2 == 0 {
                num++
                hoge /= 2
            } else {
                break
            }
        }
        if num < max {
            max = num
        }
    }
    println(max)
}
```
2で割れる回数を求めてその最小値を出します。

~~**「いくらfor...inが使いたいからってnum := iなんてする必要ないじゃない！」**ごめんなさい、書きたかった[^1][^2]！~~
使わない数値は、特殊な変数`_`に放り込めばいいようになったようです。n回繰り返しにはこれを使えばよさげ
2019/11/9追記：`for i in start..end`という構文が書けるようになったようです。`[0].repeat(n)`とはおさらば


## 4問目 [ABC087B - Coins](https://atcoder.jp/contests/abs/tasks/abc087_b)
```
fn main() {
    a := scan_int()
    b := scan_int()
    c := scan_int()
    x := scan_int()
    mut ans := 0
    mut i := 0
    mut j := 0
    for i <= x/500 {
        j = 0
        for j <= (x-i*500)/100 {
            k := x - i*500 - j*100
            if a >= i && b >= j && c >= k/50 {
                ans++
            }
            j++
        }
        i++
    }
    println(ans)
}
```
500円玉と100円玉の枚数で全探索をします。

for文はCのwhile文のように使うことができます(これが普通)。

## 5問目 [ABC083B - Some Sums](https://atcoder.jp/contests/abs/tasks/abc083_b)
```
fn main() {
    n := scan_int()
    a := scan_int()
    b := scan_int()
    mut ans := 0
    for i := 1; i <= n; i++ {
        istr := i.str()
        mut sum := 0
        for c in istr {
            sum += int(c-`0`)
        }
        if a <= sum && sum <= b {
            ans += i
        }
    }
    println(ans)
}
```
1からnの数について、全ての桁を足します。
今回は数値を文字列に変換し、文字が表す数値を全て足しました。

Cのfor文みたいにもfor文を使うことができます。for文えらい万能やな

## 6問目 [ABC088B - Card Game for Two](https://atcoder.jp/contests/abs/tasks/abc088_b)
```
fn main() {
    n := scan_int()
    mut arr := []int{len: n, init: 0}
    for i in 0..n {
        arr[i] = scan_int()
    }
    arr.sort(a > b)
    mut ans := 0
    mut t := 1
    for i in arr {
        ans += t*i
        t = -t
    }
    println(ans)
}
```
降順にクイックソートして足し引きをします。

クイックソートは`array`構造体に組み込まれている`sort`関数を利用します。
`sort`は特殊な関数です。`a`と`b`という特殊な変数が渡され、括弧内に式を書けばそれがソートの条件文として機能します。

## 7問目 [ABC085B - Kagami Mochi](https://atcoder.jp/contests/abs/tasks/abc085_b)
```
fn main() {
    n := scan_int()
    mut arr := [0].repeat(n)
    for i in 0..n {
        arr[i] = scan_int()
    }
    arr.sort()
    mut ans := 1
    mut t := arr[0]
    for i in arr {
        if i != t {
            t = i
            ans++
        }
    }
    println(ans)
}
```
昇順にクイックソートして被りを抜いて数えます。

`sort`関数の条件文を省略すると勝手に昇順になります。

## 8問目 [ABC085C - Otoshidama](https://atcoder.jp/contests/abs/tasks/abc085_c)
```
fn main() {
    n := scan_int()
    y := scan_int()
    for i := 0; i <= n; i++ {
        for j := 0; j <= n-i; j++ {
            k := n-i-j
            if i*1000 + j*5000 + k*10000 == y {
                println('$k $j $i')
                return
            }
        }
    }
    println('-1 -1 -1')
}
```
お札の枚数で全探索します。

テンプレ文字列は文明の利器。

## 9問目 [ABC049C - 白昼夢 / Daydream](https://atcoder.jp/contests/abs/tasks/arc065_a)
```
fn main() {
    mut str := scan_string(100000)
    if str.replace('eraser','').replace('erase','').replace('dreamer','').replace('dream','').len == 0 {
        println('YES')
    } else {
        println('NO')
    }
}
```
・・・
　
　
　
・・・・・・**嘘解法です！！**でもreplaceを連ねて書けるの、きもちいい。

## 10問目 [ABC086C - Traveling](https://atcoder.jp/contests/abs/tasks/arc089_a)
```
fn main() {
    n := scan_int()
    mut t := 0
    mut x := 0
    mut y := 0
    for i in 0..n {
        t0 := scan_int()
        x0 := scan_int()
        y0 := scan_int()
        dt := t0-t
        dx := x0-x
        dy := y0-y
        if dx+dy > dt || (dt-dx-dy)%2 == 1 {
            println('No')
            return
        }
        t = t0
        x = x0
        y = y0
    }
    println('Yes')
}
```
歩数が足りるかどうかと、往復で時間をつぶしてたどり着けるか（偶奇でわかる）を判定します。

# 完走した感想
まだEarly Access版なので足りない部分は存在したものの、これからたぶん伸びていく言語なのでそこはたぶん期待できます。簡単な構文で書けて速度も速い、これは今後次第で競プロにピッタリな言語になりえるのではないでしょうか。

# 更新履歴
2019/7/4 いつの間にか文字のバイト値を取得する構文ができてたのでバッククォートに変更
2019/9/25 stringとかrepeatとかアンダーバーとか他にも色々更新が入ってたので更新
2019/11/9 `for i in start..end`を追加。`end`は`i`に含まれないのでいつものfor文の感覚で使える。
2021/7/1 0.2.xも出てるし重い腰を上げて更新するかとなったので。配列やunsafeなとこ周りが結構変わってます。


[^1]: V言語ではある値である長さの配列を`[]型名{len: 長さ, init: 初期値(省略可)}`で作成できます。
[^2]: V言語では宣言した変数を使用しなければ警告が出ます。以前は必ず使用しなければエラーになっていました。