1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Ruby】1度だけ計算して2度目以降(ソートする時など)に1度目のデータを基に並び替えする方法

Last updated at Posted at 2019-08-05

テラテイルで僕の苦戦した歴史が載っています。
https://teratail.com/questions/204044

備忘録として整理した内容で掲載します。同じく苦戦した方のお役に立てれば幸いです。

タイトルの内容を分かりやすく

レースの走行時間の計算をします。次にその走行時間を元に早い順に並べ替えたいときの方法を今回は述べています。
ソートした時に2度目の計算が行われ、1度目の計算を基準にしたいのにできないというドツボにハマってしまいました。

ゴールはタイム順に車体名が表示されること

下のスクショのような感じがゴールです。走行時間順に順位が表示されてますね。
ちなみにこの出力ができるまでに8時間くらい要したのですが端折って書きたいと思います。
同じ悩みを抱えてる方が15分くらいで実装できるように書くつもりですが、分からない場合はコメントください。
スクリーンショット 2019-08-05 13.58.56.png

最終ソースコード

main.rb が実行ファイルで、他のファイルをrequireしています。
car.rbdef time で距離の計算をしています。
車種ごとのファイルferrari.rb honda.rb nissan.rb toyota.rb はスルーでも大丈夫です。
「ブレーキしたら速度が半分になる」という条件入れてるので少々式は複雑ですがコメントアウトでコードの意味書いてるので、分からない場合はご連絡ください。

main.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
# 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
ferrari.rb
class Ferrari < Car
end
honda.rb
class Honda < Car
end
nissan.rb
class Nissan < Car
end
toyota.rb
class Toyota < Car
end

if を使わないと走行時間と順位表示が合わない

この場合、2位はNissanのはずなのにフェラーリが2位に表示されています。

スクリーンショット 2019-08-02 18.43.15.png

上手く行かない時のソースコードと原因

以下のコードだとmain.rb
output_information で1度走行時間が計算されます。
arr = [h, n, f, t] puts arr.sort_by{|x| x.time}.map(&:type) で2度目の計算がされます。
出力画面の走行時間に表示されている数字が1回目、arr で順位表示させた時は2回目の計算がされているので順位が異なるのです。

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) #ここで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
# 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.rbdef time で「1度計算されたら2度目は計算されないようにする」というメソッドを作る。
具体的には「if 走行時間が計算されていないならelse 走行時間を計算する」という式を以下のソースコードのように作ります。(再掲になりますが)

car.rb
# 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 の使い方を工夫

これで解決です!

main.rb
arr = [h, n, f, t]
puts arr.sort_by{|x| x.time}.map(&:type) 
# timeメソッドで走行時間を引っ張ってきて早い順にソートして車体名で表示
end
1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?