テラテイルで僕の苦戦した歴史が載っています。
https://teratail.com/questions/204044
備忘録として整理した内容で掲載します。同じく苦戦した方のお役に立てれば幸いです。
タイトルの内容を分かりやすく
レースの走行時間の計算をします。次にその走行時間を元に早い順に並べ替えたいときの方法を今回は述べています。
ソートした時に2度目の計算が行われ、1度目の計算を基準にしたいのにできないというドツボにハマってしまいました。
ゴールはタイム順に車体名が表示されること
下のスクショのような感じがゴールです。走行時間順に順位が表示されてますね。
ちなみにこの出力ができるまでに8時間くらい要したのですが端折って書きたいと思います。
同じ悩みを抱えてる方が15分くらいで実装できるように書くつもりですが、分からない場合はコメントください。
最終ソースコード
main.rb
が実行ファイルで、他のファイルをrequire
しています。
car.rb
のdef time
で距離の計算をしています。
車種ごとのファイルferrari.rb
honda.rb
nissan.rb
toyota.rb
はスルーでも大丈夫です。
「ブレーキしたら速度が半分になる」という条件入れてるので少々式は複雑ですがコメントアウトでコードの意味書いてるので、分からない場合はご連絡ください。
# main.rb
require_relative 'car'
require_relative 'ferrari'
require_relative 'honda'
require_relative 'nissan'
require_relative 'toyota'
def main #(type, speed, price, capacity, height, passenger)
h = Honda.new("Honda", 100, 100, 8, 100, 1)
n = Nissan.new("Nissan", 120, 50, 5, 100, 1)
f = Ferrari.new("Ferrari", 200, 2000, 2, 100, 1)
t = Toyota.new("Toyota", 100, 300, 2, 100, 1)
output_information(h,n,f,t) # ここで走行時間が計算されて保存される。car.rb の 「def time」 のelseで走行時間が計算される
puts '-----順位-------'
arr = [h, n, f, t]
puts arr.sort_by{|x| x.time}.map(&:type) # 保存した走行時間をソートして表示。 上記の「output_information(h,n,f,t)」で走行時間が既に計算されてるからここではその計算結果が表示される
end
def output_information(h,n,f,t)
puts "\t\t#{h.type}\t\t#{n.type}\t\t#{f.type}\t\t#{t.type}"
puts "加速\t\t#{h.speed}\t\t#{n.speed}\t\t#{f.speed}\t\t#{t.speed}"
puts "価格\t\t#{h.price}\t\t#{n.price}\t\t#{f.price}\t\t#{t.price}"
puts "定員\t\t#{h.capacity}\t\t#{n.capacity}\t\t#{f.capacity}\t\t#{t.capacity}"
puts "車高\t\t#{h.height}\t\t#{n.height}\t\t#{f.height}\t\t#{t.height}"
puts "乗員\t\t#{h.passenger}\t\t#{n.passenger}\t\t#{f.passenger}\t\t#{t.passenger}"
puts "走行時間\t\t#{h.time}\t\t#{n.time}\t\t#{f.time}\t\t#{t.time}"
end
if __FILE__ == $0
main
end
# car.rb
# parent class
class Car
attr_accessor :type, :speed, :price, :capacity, :height, :passenger
def initialize(type, speed, price, capacity, height, passenger)
@type = type
@speed = speed
@price = price
@capacity = capacity
@height = height
@passenger = passenger
end
def time #レースタイムを算出
if @time # @time が存在するならその時間を表示(1回目は存在しない)
@time
else # @time が存在しないなら計算する → 1回目だけ計算して2回目以降は計算結果だけでソートできるようにしてる
@distance = 10000 #走行距離
@brake_speed = @speed/2 #減速時の速度
random = Random.new
@brake_time = random.rand(1..5) #今回はブレーキを踏む回数を1〜5回でランダム設定
@time = @distance / (@brake_speed*@brake_time) + (@distance-@brake_speed*@brake_time) / @speed #タイム算出の計算式・・・ 減速時に進んだ時間 + 通常時に進んだ時間の和
end
end
end
class Ferrari < Car
end
class Honda < Car
end
class Nissan < Car
end
class Toyota < Car
end
if を使わないと走行時間と順位表示が合わない
この場合、2位はNissanのはずなのにフェラーリが2位に表示されています。

上手く行かない時のソースコードと原因
以下のコードだとmain.rb
の
output_information
で1度走行時間が計算されます。
arr = [h, n, f, t] puts arr.sort_by{|x| x.time}.map(&:type)
で2度目の計算がされます。
出力画面の走行時間に表示されている数字が1回目、arr
で順位表示させた時は2回目の計算がされているので順位が異なるのです。
require_relative 'car'
require_relative 'ferrari'
require_relative 'honda'
require_relative 'nissan'
require_relative 'toyota'
def main #(type, speed, price, capacity, height, passenger)
h = Honda.new("Honda", 100, 100, 8, 100, 1)
n = Nissan.new("Nissan", 120, 50, 5, 100, 1)
f = Ferrari.new("Ferrari", 200, 2000, 2, 100, 1)
t = Toyota.new("Toyota", 100, 300, 2, 100, 1)
output_information(h,n,f,t) #ここで1度目の計算がされる
puts '-----順位-------'
arr = [h, n, f, t]
puts arr.sort_by{|x| x.time}.map(&:type) #ここで2度目の計算がされて、ここの数字が順位表示に使われるので走行時間と順位が合わなくなる
end
def output_information(h,n,f,t)
puts "\t\t#{h.type}\t\t#{n.type}\t\t#{f.type}\t\t#{t.type}"
puts "加速\t\t#{h.speed}\t\t#{n.speed}\t\t#{f.speed}\t\t#{t.speed}"
puts "価格\t\t#{h.price}\t\t#{n.price}\t\t#{f.price}\t\t#{t.price}"
puts "定員\t\t#{h.capacity}\t\t#{n.capacity}\t\t#{f.capacity}\t\t#{t.capacity}"
puts "車高\t\t#{h.height}\t\t#{n.height}\t\t#{f.height}\t\t#{t.height}"
puts "乗員\t\t#{h.passenger}\t\t#{n.passenger}\t\t#{f.passenger}\t\t#{t.passenger}"
puts "走行時間\t\t#{h.time}\t\t#{n.time}\t\t#{f.time}\t\t#{t.time}"
end
if __FILE__ == $0
main
end
# car.rb
# parent class
class Car
attr_accessor :type, :speed, :price, :capacity, :height, :passenger, :distance, :brake_speed
def initialize(type, speed, price, capacity, height, passenger)
@type = type
@speed = speed
@price = price
@capacity = capacity
@height = height
@passenger = passenger
end
def time
#レースタイムを算出。
@distance = 10000 #走行距離
@brake_speed = @speed/2 #減速時の速度
random = Random.new
@brake_time = random.rand(1..5) #今回はブレーキを踏む回数を1〜5回でランダム設定
@distance / (@brake_speed*@brake_time) + (@distance-@brake_speed*@brake_time) / @speed #タイム算出の計算式・・・ 減速時に進んだ時間 + 通常時に進んだ時間の和
end
end
if を使って解決
car.rb
の def time
で「1度計算されたら2度目は計算されないようにする」というメソッドを作る。
具体的には「if
走行時間が計算されていないならelse
走行時間を計算する」という式を以下のソースコードのように作ります。(再掲になりますが)
# car.rb
# parent class
class Car
attr_accessor :type, :speed, :price, :capacity, :height, :passenger
def initialize(type, speed, price, capacity, height, passenger)
@type = type
@speed = speed
@price = price
@capacity = capacity
@height = height
@passenger = passenger
end
def time #レースタイムを算出
if @time # @time が存在するならその時間を表示(1回目は存在しない)
@time
else # @time が存在しないなら計算する → 1回目だけ計算して2回目以降は計算結果だけでソートできるようにしてる
@distance = 10000 #走行距離
@brake_speed = @speed/2 #減速時の速度
random = Random.new
@brake_time = random.rand(1..5) #今回はブレーキを踏む回数を1〜5回でランダム設定
@time = @distance / (@brake_speed*@brake_time) + (@distance-@brake_speed*@brake_time) / @speed #タイム算出の計算式・・・ 減速時に進んだ時間 + 通常時に進んだ時間の和
end
end
end
あとは array の使い方を工夫
これで解決です!
arr = [h, n, f, t]
puts arr.sort_by{|x| x.time}.map(&:type)
# timeメソッドで走行時間を引っ張ってきて早い順にソートして車体名で表示
end