RubyのコードゴルフのTips

コードゴルフとはプログラムのソースコードをどれだけ短く書けるかを争う競技です。

自分は時々 [Anarchy Golf](http://golf.shinh.org/) や [yukicoder](http://yukicoder.me/) でゴルフしています。

この記事では自分がコードゴルフを行っている時のTipsでも紹介しようと思います。


# 余分な空白の削除

普通はソースコードの可視性を考えてインデントやスペース等を使用しますが、コードゴルフでは必要ないので削ります。

```ruby
# bad
puts "hello world"

# good
puts"hello world"
```


# 余分な括弧の削除

必要の無い括弧は消しましょう。

```ruby
# bad
puts("hello")
[1,2,3].map(&:to_s)

# good
puts"hello"
[1,2,3].map &:to_s
```


# 変数

基本的に変数は1文字で定義します

```ruby
# bad
word = 'ruby'

# good
w='ruby'
```

同じ値を宣言する場合にはまとめることが出来ます

```ruby
# bad
a=3
b=3

# good
a=b=3
```

ただし、代入する値が文字列や配列の場合には値に対する変更の影響を受けてしまうので注意が必要です。

#### 多重代入

Rubyでは多重代入が使えます、よくあるパターンは配列の値を各変数に割り当てたい時などに使用します。またsplat演算子 `*` を使うと値をまとめて受け取ることが出来ます。

```ruby
a,b,c=[1,2,3]      #=> a=1, b=2, c=3
a,b,*c=[1,2,3,4,5] #=> a=1, b=2, c=[3,4,5]
a,*b,c=[1,2,3,4,5] #=> a=1, b=[2,3,4], c=5
a,*b=1             #=> a=1, b=[]
```


#### グローバル変数

rubyでは変数名の先頭に `$` があるとグローバル変数になりますが、定義されていないグローバル変数を呼び出しても `nil` が返ってくるだけでエラーになりません

```ruby
$a #=> nil
```


#### 1文字でnilを取得する

`p` メソッドは何も出力しない場合に `nil` を返すのでこれを利用して1文字でnilを取得することが出来ます。

```ruby
p p #=> nil
```


# 短い方のメソッド名を使用する

Rubyは **同じ動作をするけどメソッド名が異なる** ものがたくさんあります。コードゴルフでは名前が短い方のメソッドを使用しましょう。

```ruby
# bad
[1,2,3].find_index(2) #=> 1

# good
[1,2,3].index(2) #=> 1
```


# ブロックは`do end` ではなく `{...}` を使う

```ruby
# bad
10.times do|n|puts"#{n} hello"end

# good
10.times{|n|puts"#{n} hello"}
```



# 改行コードをなるべく少なくする

改行コードが `\n` な環境では気にしなくて良いですが、Windows環境だと改行コードが `\r\n` で定義されているので、`;` でコードを区切って行数を減らします。

```ruby
# bad
puts'hello'
puts'world'

# good
puts'hello';puts'world'
```


# ドキュメントを読む

短く出来なくなって詰まったときは、ひとまずドキュメントを見直すことをおすすめします。`こんなメソッドあったんだ` とか `こんな引数とれたのか` とか `ブロックも取れるのか` みたいな新たな気付きがあるときがあったりします。


# 比較演算子はなるべく1文字で

`==` や `<=` や `>=` といった比較演算子は可能であれば `>` や `<` に置き換えましょう

```ruby
# bad
n <= 3

# good
n < 4
```

これとは別ですがRangeの `..` も `...` を使うことで短くなるパターンがあります。

```ruby
# bad
(0..n-1).each{|n|p n}

# good
(0...n).each{|n|p n}
```



# 論理演算子を条件式に

短絡評価を利用して論理演算子である `&&` と `||` を条件式として利用します。

```ruby
# bad
if true
  n=3
end

# good
true&&n=3
```

```ruby
# bad
unless false
  n=3
end

# good
false||n=3
```


# 三項演算子

```ruby
# bad
if 3>2
  333
else
  777
end

# good
3>2?333:777
```


# 入力

rubyで入力を受け付けるには色々な方法があります。ここでは下のようなテキストデータをどのように取得するかについて考えます

```
okinawa
ruby
golf
```

#### 3回 `gets` を実行する

```ruby
3.times do
  p gets
end
```

```
"okinawa\n"
"ruby\n"
"code golf\n"
```

`gets`を使うと1行情報が取得出来るので、それを3回実行してます。

#### $<.read

```ruby
p $<.read
```

```
"okinawa\nruby\ncode golf\n"
```

 渡された入力を全て受け取ることが出来ます。

#### *$<

```ruby
p *$<
```

```
"okinawa\n"
"ruby\n"
"code golf\n"
```

rubyのsplat演算子は対象のオブジェクトに対して `to_a` を実行するので、それを利用しています。結果として `$<.readlines` と同じ挙動になります。



## コマンドラインオプション


#### -n

`-n` オプションではgetsした値が特殊変数 `$_` に代入されます

```ruby
#!ruby -n
puts $_
```

```
okinawa
ruby
code golf
```



#### -p

`-p` オプションは `-n` とほぼ一緒の動作ですが、最後に `$_` に格納されている値を出力します。

```ruby
#!ruby -p
```

```
okinawa
ruby
code golf
```

これは下のコードと同じ動作です。

```ruby
while gets
  print $_
end
```

### -a

`-n` か `-p` と一緒に利用します。`-a` オプションが指定されていると読み込んできた各行に対して `split` が自動的に実行され、その結果が `$F` に格納されます。

```ruby
#!ruby -na
p $F
```

```
["okinawa"]
["ruby"]
["code", "golf"]
```

#### -l

読み込んできた各行に対して `chop!` が実行され、printの出力時に改行コードをつけてくれます。

```ruby
#!ruby -nl
p $_
```

```
"okinawa"
"ruby"
"code golf"
```


#### -r

`require` と同じ動作をします、問題によっては標準ライブラリが持っているメソッドを使用したほうが短くなるときもあるので、その時に使ったりします。

```ruby
#!ruby -rprime
p 3.prime? #=> true
```

## 出力

#### 数値の出力

数値系の出力は `p` メソッドが良いです

```ruby
p 3
```



#### その他の出力

他の出力には`puts` と `print` と `$><<` がありますが、文字数的に `puts` と `$><<` をよく使います。 `$><<` は出力したい要素との間にスペースを入れなくていいので、こちらのほうが短くなるケースが多いです。

```ruby
s="hello ruby"

puts s #=> hello ruby
$><<s #=> hello ruby
```


#### グローバル変数の出力

グローバル変数は間にスペースを入れなくても動作します。また、変数展開する際に `{}` を省略することが出来ます。

```ruby
$a='ruby'
puts$a
```

```ruby
$a='ruby'

# bad
puts"hello #{$a}" #=> hello ruby

# good
puts"hello #$a" #=> hello ruby
```




## リテラル

#### ハッシュ

```ruby
# bad
h=Hash.new

# good
h={}
```



#### 文字列

```ruby
# bad
puts "hello"

# good
puts :hello
```

シンボルは出力時に文字列に変換されます



# 文字列

#### 文字リテラル

`?` の後に1文字指定するとその文字列が返ってきます。

```ruby
?a  #=> "a"
?3  #=> "3"
?\n #=> "\n"
```

#### 文字列分割 (String#split)

`String#split` ではデフォルトで半角空白、改行などの空白文字で分割を行うので引数で指定する必要はありません。

```ruby
# bad
"hello world".split(" ")  #=> ['hello', 'world']
"hello\nworld".split(?\n) #=> ['hello', 'world']
"hello\tworld".split(?\t) #=> ['hello', 'world']

# good
"hello world".split       #=> ['hello', 'world']
"hello\nworld".split      #=> ['hello', 'world']
"hello\tworld".split      #=> ['hello', 'world']
```

#### 一桁の数値変換

```ruby
# bad
p "7".to_i #=> 7

# good
p "7".hex #=> 7
```

一桁の数値は `hex` を使うことで `to_i` より短く書くことが出来ます。


#### evalを使って数値変換

`eval` を使用したほうが短くなる場合もあります。（変数のスコープ的に定数で宣言しないと利用できませんが）

```ruby
n=%w(1 2 3)
a,b,c=n.map &:to_i
p [a,b,c] #=> [1, 2, 3]

eval"A,B,C="+n*?,
p [A,B,C] #=> [1, 2, 3]
```



#### 数値変換の際に基数を指定する

```ruby
p "100".to_i     #=> 100
p "100".to_i(2)  #=> 4
p "100".to_i(8)  #=> 64
p "100".to_i(16) #=> 256
```

`to_i` は引数に変換する基数を指定することが出来ます


#### 1文字ずつ分割

```ruby
# bad
"ruby".split('') #=> ['r', 'u', 'b', 'y']

# good
"ruby".chars     #=> ['r', 'u', 'b', 'y']
```


#### マッチ

```ruby
# bad
if 'ruby'=~/r/
  puts 'hello!'
end

# good
if 'ruby'[?r]
  puts 'Hello!'
end
```

#### 繰り返し

文字列に対して `*(数値)` の操作を行うとその指定した回数分の文字列が繰り返されます

```ruby
'hello'*0 #=> ''
'hello'*1 #=> 'hello'
'hello'*2 #=> 'hellohello'
```


# 数値

#### リテラル

```ruby
# bad
n=1000

# good
n=1e3 #=> 1000.0
```


#### 基数変換

`to_s` を使用して文字列に変換する際に引数で変換する基数を指定することが出来ます。

```ruby
16.to_s     #=> "16"
16.to_s(2)  #=> "10000"
16.to_s(8)  #=> "20"
16.to_s(16) #=> "10"
```


#### ビット値取得

数値に対して `[]` を使用すると指定した場所のビット値が取得できます。

```ruby
p 1[0] #=> 1
p 2[0] #=> 0
p 3[0] #=> 1
```


#### 演算の順番を変える

通常の演算では `+` より `*` が優先されるため、`+` のほうの演算を先に行いたい場合は `()` で囲む必要がありますが、`2+1` の部分を `3.*` の引数として渡すことで `()` で囲むより1byte短く書くことが出来ます。

```ruby
# bad
p 3*(2+1) #=> 9

# good
p 3.*2+1  #=> 9
```



# 配列

#### リテラル

```ruby
# bad
a=Array.new

# good
a=[]
```

```ruby
# bad
a=['hoge','piyo','fuga']

# good
a=%w(hoge piyo fuga)
```

```ruby
# bad
a=[1,2,3,4,5]

# good
a=*1..5
```



#### join

```ruby
# bad
[1,2,3].join(?+) #=> "1+2+3"

# good
[1,2,3]*?+       #=> "1+2+3"
```


#### uniq

```ruby
# bad
[1,1,2,2,3,3].uniq #=> [1,2,3]

# good
[1,1,2,2,3,3]|[]   #=> [1,2,3]
```


#### push

```ruby
# bad
[1,2,3].push(4) #=> [1, 2, 3, 4]

# good
[1,2,3]<<4      #=> [1, 2, 3, 4]
```


#### unshift

```ruby
# bad
[1,2,3].unshift(4) #=> [4, 1, 2, 3]

# good
[1,2,3][0,0]=4     #=> [4, 1, 2, 3]
```

#### compact

```ruby
p [nil,1,2,3,nil].compact #=> [1, 2, 3]
p [nil,1,2,3,nil]-[p]     #=> [1, 2, 3]
```


#### reverse

2つの要素においては `rotate` のほうが短いです。

```ruby
a=[1,2]

# bad
a.reverse #=> [2, 1]

# good
a.rotate  #=> [2, 1]
```

#### 配列内の合計値を求める

```ruby
# bad
[1,2,3].inject(:+) #=> 6

# good
eval [1,2,3]*?+    #=> 6

# very good (2.4 から可能)
[1,2,3].sum
```

# ビット演算

```ruby
# bad
n<-10

# good
n<~9
```

```ruby
n=3

# bad
(n+1)*3 #=> 12

# good
-~n*3   #=> 12
```

```ruby
n=3

# bad
(n-1)*3 #=> 6

# good
~-n*3 #=> 6
```



# 特殊変数

Rubyには `$` から始まる様々な特殊変数が存在しています。コードゴルフでは正規表現周りやコマンドラインオプションを有効にしたときに出てくるものがよく使われています。

#### $_

getsした値が格納されます、オプションで `-n` か `-p` を指定していると自動的に値が代入されます。



#### $*

`Object::ARGV` の別名ですが、ゴルフ的には最初から用意されている空の配列になります。

```ruby
# bad
a=[]

# good
$*
```


#### $`, $&, $'

`$&`は正規表現でマッチした文字列が格納され、 $\` にはマッチした文字列の前の文字列部分が格納され、 `$'` には マッチした文字列の後ろの文字列が格納されます。

```ruby
"hello123world" =~ /\d+/
p [$`, $&, $'] #=> ["hello", "123", "world"]
```


#### $1, $2, ... $n

正規表現のパターンマッチでn番目の括弧にマッチした文字列が格納されます。

```ruby
"123 456 789" =~ /(\d+) (\d+) (\d+)/
p [$1, $2, $3] #=> ["123", "456", "789"]
```


#### $/

デフォルトで改行文字が入っています。

```ruby
# bad
?\n

# good
$/
```



#### $<

定数 Object::ARGF の別名です。




# その他

カテゴリ分けに困ったTipsを書いていきます


#### Procの呼び出し

Procは `call` 以外に `[]` でも呼び出すことが出来ます

```ruby
func=->word{
  puts word
}

# bad
func.call('hello')

# good
func['hello']
```


## 参考ドキュメント

* [Rubyの起動](https://docs.ruby-lang.org/ja/latest/doc/spec=2frubycmd.html)
* [module Kernel](https://docs.ruby-lang.org/ja/latest/class/Kernel.html)
* [リテラル](https://docs.ruby-lang.org/ja/latest/doc/spec=2fliteral.html)