2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rustでコードゴルフ

Last updated at Posted at 2021-04-10

普段 Anarchy GolfCode Golf でコードゴルフをやっている際に, 気付いたテクニックやTipsを自分なりにまとめました.
代替案やもっと良い書き方がありましたらお気軽にコメントお願いします.
※コード例にNGOKと付けていますが, NGの例も絶対に使わないというわけではありません.

動作確認環境

$ rustc -V
rustc 1.51.0 (2fd73fabe 2021-03-23)

出力

出力は全てprintマクロを使う.
改行する際は

print!("{}
",n)

とすることで, printlnマクロを使用するより1byte減らせる.

比較

割った余りが0か?

if n%3==0{}  // NG
if n%3<1{}   // OK

大きい数と比較

if n<832041{}  // NG
if n<1<<20{}   // OK

型注釈

let a:u8=0;  // NG
let a=0u8;   // OK
let s=a.iter().map(|&x|x*2).collect::<Vec<u32>>();  // NG
let s:Vec<u32>=a.iter().map(|&x|x*2).collect();     // OK

collectのような戻り値がジェネリックなものは, Vec<_>のように省略できる場合もある.

クロージャ

同じ処理をする場合は関数にしてしまった方がbyte数は少なくなる可能性もあるだろう.
その際に再帰しないのであれば, クロージャの方が型注釈が要らないので少なくなる.

fn f(n:i32)->i32{n*2}  // NG
let f=|n|n*2;          // OK

コマンドライン引数

実行時のパスを含めない場合は以下のように書く.
std::env::args()[1..]のようには書けない.

for a in std::env::args().skip(1){}

条件に一致するもののみ処理対象にするような問題では.skip(1)を省略できる.

for a in std::env::args(){}

ループ

C言語だとfor文一択だと思いますが, Rustでは場合によりけりです.
パターン毎に使用例を提示します.

+1の範囲なら=を使う

for _ in 0..n+1{}  // NG
for _ in 0..=n{}   // OK

for_eachは使わない

(0..10).for_each(|i|{});  // NG
for i in 0..10{}          // OK

逆順

for i in(0..10).rev(){}
//      ^ 空白を詰めれる

n回ループと決まっていない場合

while i<10{
    i+=if true{2}else{5}
}

rangeから暗黙的にイテレータにできるので以下も有効的

(0..10).fold(0,|a,x|{print!("{}",a);a+x})

mapはそのままでは標準出力されないことに注意.
collectすると出力される. 変数に束縛する必要はない.

(0..10).map(|n|{print!("{}",n);n}).collect()::<Vec<_>>()

変数

変数を複数使う場合はタプルを分解すると効率良い

let a=0;let b=1;let c=2;  // NG
let(a,b,c)=(0,1,2);       // OK

mutableな時は変数の使用回数によって使い分け

let(mut a,mut b,mut c)=(0,1,2);
let mut a=(0,1,2);

クロージャも使える

let(a,b)=(0,1);a+b  // NG
(|a,b|a+b)(0,1)     // OK

Rustはほとんどがではなくで構成されている.
つまり, for式やif式から返る値を有効活用できる.

forやwhileは()を返す必要がある

let m=for n in 0..10{n};   // NG
let m=for n in 0..10{n;};  // OK

()を返すという特性を活かして, セミコロンを省略

for n in 0..10{print!("{}",n)}

char

'A'はASCIIコードの10進数で65である.
文字列を一文字ずつ解析する場合は, charsよりbytesの方が良い場合もある.

match c as _{
    65=>(),
    66=>(),
    // ...
    89=>(),
    _=>()
}

文字列

to_string()よりもto_owned()into()が使えないか試す.
文字列リテラルをStringに変換する勇気も時には必要.

print!("{}",
match(i%3,i%5){
    (0,0)=>"FizzBuzz".into(),
    (0,_)=>"Fizz".into(),
    (_,0)=>"Buzz".into(),
    _=>i.to_string()
})

空白埋め

図形を描画する系の問題で使用する.

可変幅の場合

let n=42;
print!("{}"," ".repeat(n));  // NG
print!("{:<w$}","",w=n);     // NG
print!("{:<1$}","",n);       // OK

固定幅の場合

print!("{:^42}",n)

パターンマッチ

波かっこの後ろにカンマは要らない.

match a{
    0=>{}
    1=>{}
    _=>{}
}

一番最後にもカンマは要らない.

match a{
    0=>"a",
    1=>"b",
    _=>"c"
}

rangeを使用する場合は=が必要.

match c{
    'a'..='z'=>(),
    '0'..='3'|'6'..='9'=>(),
    '🍣'..='🍺'=>(),
    _=>()
}

if letのような論駁可能性はenum以外でも使える.

if matches!(n,2|3|5){}  // NG
if n==2||n==3||n==5{}   // NG
if let 2|3|5=n{}        // OK

キャスト

bool型は整数型へキャストすることで 0 1 に変換できる

v=if i<1{0}else{n-i};  // NG
v=(i>0)as u8*n-i;      // OK

ライフタイム

スコープとライフタイムは別物.

// aとbのライフタイムは同じ
let a;
{
    let b=&1;
    a=b
}
print!("{}",a)  // OK

必要以上のライフタイムを持つ変数へ束縛することでライフタイムを伸ばすことができる.
その際, 初期化されていない変数を参照しない限り, 宣言時の初期化やmutは必要ない.

let n;
print!("{}",
match(i%3,i%5){
    (0,0)=>"FizzBuzz",
    (0,_)=>"Fizz",
    (_,0)=>"Buzz",
    _=>{n=i.to_string();&n}
})

Never type

Rustには発散する ! という型がある.
具体的には break, return, panic そして loop等がある.
! は任意の型にキャストすることができるので, こんな活用もできる.

iが100以上の時にループから抜けたい

if i>99{break}  // NG
i>99&&break     // OK

unsafe

unsafeなコードで短縮できる場合もある.
コードゴルフで安全性は考慮する必要なし.

unsafe{
    static mut A:Vec<Vec<u8>>=vec![];
    unsafe fn f(){
        // something with using A
    }
}

Webサイト

様々な問題の各種言語での解答が載っている.
Rust以外の言語で使用しているアルゴリズムも非常に参考になる.

その他

  • キャスト時はas _にできないか試す
  • 実行時の警告はRUSTFLAGS=-Awarningsで無視できる

追記 - rustc 1.58.0 (02072b482 2022-01-11)

Rust 1.58からフォーマット文字列で変数を直接参照できるようになった.
1.58時点では識別子のみ有効であり、式の指定はできない。

print!("{}",i)  // NG
print!("{i}")   // OK
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?