owned this note
owned this note
Published
Linked with GitHub
# RubyコードゴルフTips
Rubyはコードゴルフ向きの言語として有名です。
便利なメソッドが多く、いろいろ組み合わせることで驚くほど短くなったりもします。
思いついたアイデアや集めたテクニックを忘れてしまうともったいないので、まとめることにしました。
編集歓迎です。
* [入出力](#入出力に関するテクニック)
* [配列](#配列に関するテクニック)
* [文字列](#文字列に関するテクニック)
* [数値](#数値に関するテクニック)
* [その他](#その他のテクニック)
## 入出力に関するテクニック
### 1行入力
```ruby
gets
```
### すべて入力
```ruby
`dd`
```
これは`$<.read`よりも3B短い。
### 複数行入力
```ruby
a,b,c=*$<
```
`$<`は`ARGF`の別名。`IO`は行に対しての`Enumerable`であり、`IO#to_a`は入力のすべての行を配列で返す。
### 複数行入力(数値)
```ruby
a,b,c=$<.map &:to_i
```
`Enumerable#map`を使う。
### 1行に複数の入力(スペース区切り)
```ruby
a,b,c=gets.split
```
ただし、次の行が必要ない場合は
```ruby
$/=' '
a,b,c=*$<
```
でもよい。
行の区切りを表す`$/`をスペースにしている。ちなみに`$/`には文字列しか代入できない。
### 1行に複数の数値入力(スペース区切り)
```ruby
a,b,c=gets.split.map &:to_i
```
ただし、次の行が必要ない場合は
```ruby
$/=' '
a,b,c=$<.map &:to_i
```
の方が1B短い。
ただし、定数でもよい場合は
```ruby
eval"A,B,*C="+`tr ' ' ,`
```
がさらに1B短い。
### 複数行に複数の値入力
```ruby
eval"A,B,*C="+`dd`.split*?,
```
`eval`することで、Rubyで扱える値を直接変数に入れている。
このとき、変数名を大文字にする必要がある。実行のコンテキストが違うので、ローカル変数だと見えない。
ただし、特殊文字を入力できる場合は、 `\x00` から `\x0a` のいずれかを使い、
```ruby
eval"A,B,*C="+`tr -! ,`
```
と書くと3B短い。
### 出力する
```ruby
puts:hello
```
`IO#<<`に文字列以外を渡すと`to_s`を呼ぶため、識別子として有効ならシンボルを使うと1B短くなる。
```ruby
$><<:hello
```
`$>`は`$stdout`の別名。
`$><<`は改行しないため、改行したいときは`puts`を使う。
### 数値を出力する
```ruby
p x
```
`p`は`inspect`メソッドの結果を出力する。数値の場合、`$><<`を使うよりもこちらが2B短くなる。
### 複数行の出力をする
```ruby
puts array
```
`puts`に配列を渡すと、改行で区切って出力する。これは
```ruby
puts array.join(?\n)
```
と同じ動作になる。
数値の配列の場合は
```ruby
p *array
```
とすると良い。
### 出力のバッファリングをしない
```ruby
$<.sync=1
```
もしくは、出力後に
```ruby
$<.dup
```
`IO#dup`は副作用としてフラッシュする。
## 配列に関するテクニック
### joinする
```ruby
array*" "
```
`array.join(" ")`よりも6B短い。
### eachではなくmapを使う
`map`でも`each`と同じことができる。
## 文字列に関するテクニック
### 別の文字列を含むか判定する
`s`が`t`を含むか?
```ruby
s[t]
```
含む場合は `t` を返し、含まなければ `nil` を返す。
これは `s.include?(t)` よりも 9 B 短い。
### 置換する
```ruby
"helllo worlld".gsub /ll+/,'l'
#=> "hello world"
```
gsubにはパターンとして文字列か正規表現を渡せる。正規表現や置き換え先では`\1`のようにキャプチャしたグループを参照できる。
`str.gsub/pattern/`のように`gsub`と`/`をくっつけるとエラーになるので注意。
ブロックを渡すこともできる。
```ruby
"AAABBBBBCCCCCC".gsub(/(.)\1*/){$1+$&.size.to_s}
#=> "A3B5C6"
```
`$&`でマッチした部分全体を、`$1`で1番目のグループを参照できる。
ブロックを渡す場合や、メソッドチェーンを使う場合は括弧は省略できないので注意。
### 文字を置換する
```ruby
"uryyb, jbeyq".tr"A-Za-z","N-ZA-Mn-za-m"
"uryyb, jbeyq".tr("A-Z","a-z").tr"a-z","n-za-m"
#=> "hello, world"
```
1文字ずつ対応する文字に置換する。`a-z`のような範囲指定ができる。
### 末尾に特定の文字列があれば削除する
```ruby
"hello, world!!!!!".chomp"!!"
#=> "hello, world!!!"
```
デフォルトでは改行を削除するが、任意の文字列を削除できる。
### 文字列に変換する
```ruby
p (1..gets.to_i).count{/7/!~"%1$d%1$o"%_1}
```
`String#%`は`String#format`の別名で、値または配列を渡すと整形できる。
## 数値に関するテクニック
### `-x-1`を計算する
```ruby
~x
```
### `x+1`を計算する
```ruby
-~x
```
### `x-1`を計算する
```ruby
~-x
```
バイト数は変わらないが、より結合が強い。
### 割り算を切り上げる
```ruby
0--x/y
```
Rubyの除算は常にマイナス方向に切り捨てるので、2回符号反転すると切り上げになる。
### 比較
`<=`と`>=`は使わず、できるかぎり`<`と`>`を使う。
値が非負整数に限られるときは、`x==0`は`x<1`と書くことができる。
### `i`ビット目が立っているか確認する
```ruby
x[i]>0
```
`Integer#[]`は指定したビットが立っていれば`1`、そうでなければ`0`を返す。また、マイナスのインデックスに対しては常に0を返す。これは`x&1<<1>0`よりも2B短い。
ただし、決まったビットを調べる場合、たとえば`1`ビット目なら
```ruby
x&2>0
```
とした方が1B短い。
### `x<=0`のときだけ`y`を足す
```ruby
1[x]*y
```
ビットが立っているか確認するテクニックを使っている。`x<1?y: 0`よりも2B短い。
逆に、`x>0`のときだけ足したいときは、`x`が最大で何ビットあるかによって変わるが、`126[x]*y`のようにする。10進数で4桁を超える場合は、`x<1?y: 0`の方が良いかもしれない。
## その他のテクニック
### `a == b`, `a < b`, `a > b` で返す文字列を切り替える
```ruby
puts%w(Eq Gt Le)[a<=>b]
```
`-1` をインデックスにすると配列の最後を参照する。
### eval
```ruby=abc153_a.rb
eval"p 1+~-"+`dd`.split*?/
```
`eval`を使うと、コードと入力を組み合わせることができる。使いようによってはかなり強力。
### Zlib.deflate
```ruby=agc047_e
#!ruby -Knrzlib
eval Zlib.inflate'x�=�Q
� D��OS�c
]r� �����ݳ�By�����O{4�.��i�aQ(`}cB���I2e�ߣ��IeT�јL>������)u,�p�S�W��H~.�,�:
z:Ӊ��g�O7ʲ��vQ��1h�^<����=&�\�u7'
```
コードをDeflate圧縮する。圧縮前のコードが長め(200B程度)かつ同じ文字や繰返しが多いときに有効。
### ブロックの引数
Ruby2.7.1からはNumbered parameter(ナンパラ)が使える。
ブロックが多重になっているときは、その一番内側でのみ使える。
同じ変数を4回以上使うときは、普通の引数を使った方が短くなる。
### ドキュメントを読む
新しいテクニックを見つけるのに一番大事なのは、[ドキュメント](http://docs.ruby-lang.org/ja/latest/)を熟読する事。