Qiita の新着記事を眺めていると,ときどき同じお題に取り組んだ結果を書いたものをいくつも目にすることがあります。
「模範解答」があったりするので,おそらく何らかの教材に取り組まれたのだろうと思います。
その「模範解答」のコードを見て首を傾げることがしばしばあったので,本記事で具体的に指摘してみたいと思います。
取り上げるのはすべて Ruby のものです。
たいがい出典が書かれていないので,原典を参照することができないのですが。
※本記事は,そういった記事および著者を批判する意図を微塵も持っていません。むしろ応援したい気持ちで書いています。
※また,取り上げた教材およびその提供者を侮辱したり非難したりするつもりもありません。不遜かもしれませんが改善に役立てば幸いです。
なお,教材によってはお題そのものがおかしかったりしますが,今回は取り上げません。
今回,三つのお題を取り上げました。引用したのは,それぞれのお題について,見つけることのできた最も新しい記事です。
例:Ruby の each の入れ子
お題は
fruits_price = [["apple", [200, 250, 220]], ["orange", [100, 120, 80]], ["melon", [1200, 1500]]]
という形で果物の価格リストが与えられていて
appleの合計金額は670円です
orangeの合計金額は300円です
melonの合計金額は2700円です
という出力を得る,というもの。
Qiita 上で tag:ruby fruits_price
で検索すると 30 件以上の記事がヒットします。
模範解答は,@yu-ki87 さんの記事 によれば
fruits_price = [["apple", [200, 250, 220]], ["orange", [100, 120, 80]], ["melon", [1200, 1500]]]
fruits_price.each do |fruit|
sum = 0
fruit[1].each do |price|
sum += price
end
puts "#{fruit[0]}の合計金額は#{sum}円です"
end
とのことです(原典が分からないので記事から引用することをお許しください;以下同じ)。
合計を計算するのに
sum = 0
fruit[1].each do |price|
sum += price
end
としていますが,Ruby で配列の数値の総和を求めるのにこんな面倒なコードを書くことはなく,
sum = fruit[1].sum
で事足ります。
(引用した記事でも記事主さんは sum
をお使いになっています。模範解答が複雑すぎることに戸迷われたのではないでしょうか)
これはおそらく教材が作られたのがかなり昔なのでしょう。
Array#sum が導入されたのは 2016 年 12 月にリリースされた Ruby 2.4.0 です。(本記事投稿時点で)もうすぐ 6 年になろうとしています。
Array#sum
が使えなかった Ruby 2.3 系の公式サポートが終了したのは 2019 年 3 月。つまり,3 年前には「使ってはいけないバージョン」になっていたのです。
この教材は,おそらく each
の入れ子に慣れさせるためのものだと思うので,sum
を使ってしまっては入れ子になりませんよね。それなら
apple
200円
250円
220円
orange
100円
120円
80円
melon
1200円
1500円
と表示させるお題でよかったのでは,という気がします。
それから,
fruits_price.each do |fruit|
end
のブロック内で fruit[0]
と fruit[1]
を使っていますが,こういうスタイルは初心者に真似してほしくないところです。
ブロックパラメーターを二つにして
fruits_price.each do |fruit_name, fruit_prices|
end
と書くべきでしょう。
(ブロックパラーメーター名は単に name
,prices
でもいいかと思います)
「初心者にはブロックパラメーターへの多重代入は難しい」という配慮のもと,あえて単一のブロックパラメーターにしたのかもしれません。
それはそれで理解できるのですが,模範解答を 2 段階で提示するとか,丁寧な解説を付けるといった方法もあったのでは,という気がします。
(原典を見ていないので,何か誤解があるかもしれませんが)
例:クラスとインスタンスの概念を用いたコード
@fujitacoma さんの記事 によれば,お題は,
class Article
def initialize(author, title, content)
@author = author
@title = title
@content = content
end
end
というクラスが与えられ,ここにコードを書き足して
著者: 阿部
タイトル: Rubyの素晴らしさについて
本文: Awesome Ruby!
という表示を得る,というもの。
これも検索するといくつも記事が出てくるので,どこかに教材があるのでしょう。
模範解答は
class Article
def initialize(author, title, content)
@author = author
@title = title
@content = content
end
def author
@author
end
def title
@title
end
def content
@content
end
end
article = Article.new("阿部", "Rubyの素晴らしさについて", "Awesome Ruby!")
puts "著者: #{article.author}"
puts "タイトル: #{article.title}"
puts "本文: #{article.content}"
だそうです。
ゲッターメソッド(インスタンス変数の値を読み出すメソッド)三つを真面目に定義していますが,attr_reader を使えば
attr_reader :author, :title, :content
だけで済みます。
こうしなかったのは,「これにはまだ早い」学習者向けだったからかもしれません。それなら理解できます。
この点については,適切な誘導(まずマジメに定義することを学び,次に attr_reader
で楽をすることを学ぶ)があるならそれでいいのでは,と思います。
それ以上に気になったのが,目的の出力を得るために,いちいちゲッターメソッドを一つ一つ呼び出しているところです。
このお題はおそらく「論文情報をオブジェクトで表現し,それを表示する」ということでしょう。
著者やタイトルをいちいち呼び出していたのではクラスを使う意味があまりないと思います。
論文情報を扱うクラスを作るのであれば,
class Article
def initialize(author, title, content)
@author = author
@title = title
@content = content
end
def show
puts "著者: #{@author}"
puts "タイトル: #{@title}"
puts "本文: #{@content}"
end
end
article = Article.new("阿部", "Rubyの素晴らしさについて", "Awesome Ruby!")
article.show
のように,全情報の表示を担うメソッドをクラスに持たせるべきではないでしょうか。
例:与えられた文字列の末尾の 2 文字を 3 回繰り返した文字列を出力
@itosyo4126 さんの記事 によれば,お題は
def extra_end(str)
# 処理を記述
end
# 呼び出し例
extra_end('Hello')
と書いて,lololo
を表示させるということのようです。
模範解答は
def extra_end(str)
char_num = str.length
right2 = str.slice(char_num - 2, 2)
puts right2 * 3
end
とのこと(引用した記事では「模範解答」とは書かれていませんが,他の方の記事で同じコードが模範解答として挙がっていました)。
String#slice の正しい使い方の例ではありますが,インデックスに負数を与えれば後ろから数えた位置を与えるので,
def extra_end(str)
right2 = str.slice(-2, 2)
puts right2 * 3
end
とすればいいのではないでしょうか。
str.slice(-2, 2)
はもっと簡潔に str[-2, 2]
とも書けるので,
def extra_end(str)
puts str[-2, 2] * 3
end
でよさそうに思います。
「末尾 2 文字」を「末尾 3 文字」に変更したりする可能性があるなら,変更箇所が二つある上記コードよりも,
def extra_end(str)
puts str[-2..-1] * 3
end
のほうがよいかもしれません。
Ruby 2.6(2018 年 12 月リリース;公式サポートは既に終了)で導入された「終端無し Range」を使えば
def extra_end(str)
puts str[-2..] * 3
end
と書けます。
String#slice
およびエイリアスの関係にある String#[]
にはさまざまな用法があり,初心者のうちからいっぺんに全部把握しようとするのは確かに難しいでしょう。
しかし,これも分かりやすい用法から始めて順に誘導するようになっているとよさそうに思います。
おわりに
教材が良くなっていってくれるとうれしいです。