はじめに
こんにちは、麻菜結と申します。人が新たなプログラミング言語を学ぶときは、当然ですがそれまでに学んできた知識と照らし合わせて吸収するのが普通と思われます。「この言語のこの機能、この言語で言い換えるとこんなかんじだよなぁ」といった風ですかね。自分はこの言い換える説明の仕方には大きな価値を感じていて、あらゆる言語でそんな説明してくれないかなと思っています。というわけで、この記事では「Pythonはそこそこ慣れててるんだけど、Rustでこんな操作したいとき、どうしたら良いんだろう?」という方に向けて、ちぎったパンで目印をつけるがごとく足跡を残していく記事です。
今回は競技プログラミングでありがちな、数列を渡された時どうやって配列にしまおうかという話をしたいと思います。
環境
使う環境はWindowsです。そこまでクリティカルな話はしないのであんまり気にしなくていいと思われますが。
PS C:\> cargo --version
cargo 1.43.0 (3532cf738 2020-03-17)
PS C:\> python --version
Python 3.8.2
想定される問題A
今回想定する入力は以下のものです。数列aを格納するリストを考えます。
n # 数字の数
a1 a2... an # 数列
例えば、
4
5 9 10 6
Python的思考
Python的思考というと主語がでかいですが、まずは数字の数n
を捨て、数列の文字列にsplitをかけた後intに変換をしてそれらをmapで拾い集める、ような考え方が一番簡単でしょうか。
input()
n = list(map(int, input().split()))
print(n)
Rustで解くなら?
標準入力マクロについては考えず、関数で格納するなら、
fn main(){
input();
let n: Vec<i32> = (&input()).split(" ").map(|s| s.parse().unwrap()).collect();
println!("{:?}", n);
}
fn input() -> String{
let mut buf = String::new();
std::io::stdin().read_line(&mut buf).unwrap();
buf.trim_end().to_string()
}
上のような書き方なら、難しいことなくすんなりわかると思います。
やっていることと言えば、同様にn
を捨て、数列の文字列を&str型として持ってきて、splitをかけた後parse(Rustは賢いので左側の型の表示をいい感じに解釈してi32にしてくれます)をして、unwrapで入力がおかしい場合を無視し、mapで値を集めて、collectで適切なコレクションにしてくれる、っていう感じですね。
input関数については、自分の過去の記事(上の例はtrimのとこ変わってる)をどうぞ。
想定される問題B
では以下の場合はどうしましょう。アルファベットは無視してaとbを二次元リストに格納する場合を考えます。
n # データの個数
x1 a1 b1 # x:アルファベット
x2 a2 b2 # a:数字
... # b:数字
xn an bn
例えば、
2
a 2 3
b 8 6
Python的思考
nをループ回数として保存しておいて、データの行に入ったら一文字目をはねて、二文字目以降を読み取っていくというのが一般的な考え方でしょうか。
n = int(input())
l = []
for _ in range(n):
l.append([int(e) for e in input().split()[1:]])
print(l)
Rustで解くなら?
fn main(){
let raw = input();
let n: i32 = raw.parse().unwrap();
let mut v: Vec<Vec<i32>> = Vec::new();
for _ in 0..n {
let raw = input();
let s: Vec<&str> = raw.split(" ").collect();
v.push([1, 2].iter().map(|i| s[*i as usize].parse::<i32>().unwrap()).collect());
}
println!("{:?}", v);
}
fn input() -> String{
let mut buf = String::new();
std::io::stdin().read_line(&mut buf).unwrap();
buf.trim_end().to_string()
}
let raw = input();
と書いておくとraw
に標準入力の値を縛っておけるのでこの言語特有の貸し借りに関して管理が楽になります。
見てもらいたいのは[1, 2].iter().map().collect();
のあたりで、横幅が固定されているタイプの入力にはこのような書き方をするとかなり楽になります。
[]
で固定長の配列を使い、それをiterを通してmapに注ぎ入れることで、要素のアクセスの順番や場所の制御が簡単になります。この例だとgetとか使ってもいけそうですが、うまくいかんかったという敗北宣言を残しておきます。
結論
mapを使いこなせ!
collectとunwrapを忘れるな!
おわりに
今回は、頭が切り替えられない人間が異世界でわちゃわちゃしてみたという感じでしたがいかがでしたでしょうか。これからもこんな感じの気づきをQiitaに残せたら良いな…自分が忘れたときの為に。なおシリーズものみたいなタイトルをしていますが、Rustを覚える->atCoderで使う->Qiitaにあげておくというプロセスを踏まないといけないので次があるかわかりません。麻菜先生の次回作にご期待ください。
ここまでお読みくださってありがとうございました。役に立ったなら幸いです。