Ruby におけるコードの多様性
Ruby の世界では、ひとつのことを実現するのにさまざまの記法が提供されている。シンプルな課題を解決するコードであっても、人それぞれの色が表に出てくる。だから、プルリクエストが来たときに、どうしても「私ならこうする」ということを考えがちで、その個人的な理想に対するギャップをコメントしてしまうことが多い。けれど、どのコードであっても
- 要件を満たしている
- 計算量のオーダーに差がない(またはデータサイズが極めて小さい)
- 可読性がある
なら、特に議論せずに、そのコードを受け容れるのが良いと思った。人種の違いや宗教の違いを受け容れようとする風潮が社会に生まれてきたように、コードの多様性も受け容れることが好ましいような気がしている。
たとえば、下記の課題があるとする。
# 入力例
Member = Struct.new(:name, :score, :active)
MEMBERS = [
Member.new("santa", 70, true),
Member.new("shiro", 100, true),
Member.new("nana", 100, false),
Member.new("eleven", 2000, true)
]
# 期待する出力(json)
# - 属性 active が true のレコードのみを出力
# - 属性 name をキーとし、score を値とする
# - score が 100 を超えている場合は不正値としてメッセージを出力
{
"santa": 70,
"shiro": 100,
"eleven": "wrong score!"
}
私が良いと思うコードを3つ書いてみた。どれも入出力の要件を満たしていて、計算量のオーダー上は差がなく、可読性がある。私にこのようなレビューリクエストが来たとしたら、コメントなしにそのまま承認すると思う。
def summary0(members)
result = {}
members.each do |member|
if member.active
if member.score > 100
result[member.name] = "wrong score!"
else
result[member.name] = member.score
end
end
end
result
end
def summary1(members)
members.each_with_object({}) do |member, result|
next unless member.active
score = member.score
score = "wrong record!" if score > 100
result.merge!({ member.name => score })
end
end
def summary2(members)
members
.filter(&:active)
.to_h {|member| [member.name, member.score] }
.transform_values {|score| score > 100 ? "wrong score!" : score }
end
問題が単純すぎる上に、データ型が固定されているからあまり議論することはないけれど、それでもコードレビューが荒れたりすることはある。each_with_object
を使うのがエレガントで、ローカル変数を使って計算結果を保持するのは醜いことだと言う人もいる。手続き的なコードがダサいという人もいる。
たぶんそんなことはどうでもいい。結局、時と場合による。好みによる。そして好みは変わっていく。それなら、コードの多様性を受け入れて、みんなで楽しくプログラミングするほうが生産的なのではないかと思う。