我有一些我想优化的代码。 首先,一点都不差,但也许它可能会更短或更快,主要是update_result方法:
class Round < ActiveRecord::Base
belongs_to :match
has_and_belongs_to_many :banned_champions, :class_name => "Champion", :join_table => "banned_champions_rounds"
belongs_to :clan_blue, :class_name => "Clan", :foreign_key => "clan_blue_id"
belongs_to :clan_purple, :class_name => "Clan", :foreign_key => "clan_purple_id"
belongs_to :winner, :class_name => "Clan", :foreign_key => "winner_id"
after_save {self.update_result}
def update_result
match = self.match
if match.rounds.count > 0
clan1 = match.rounds.first.clan_blue
clan2 = match.rounds.first.clan_purple
results = {clan1=>0, clan2=>0}
for round in match.rounds
round.winner == clan1 ? results[clan1] += 1 : results[clan2] += 1
end
if results[clan1] > results[clan2] then
match.winner = clan1; match.looser = clan2
match.draw_1 = nil; match.draw_2 = nil
elsif results[clan1] < results[clan2] then
match.winner = clan2; match.looser = clan1
match.draw_1 = nil; match.draw_2 = nil
else
match.draw_1 = clan1; match.draw_2 = clan2
match.winner = nil; match.looser = nil
end
match.save
end
end
end
第二,种子完全糟糕和缓慢.rb:
require 'faker'
champions = [{:name=>"Akali"},
{:name=>"Alistar"},
{:name=>"Amumu"},
{:name=>"Anivia"},
{:name=>"Annie"},
{:name=>"Galio"},
{:name=>"Tryndamere"},
{:name=>"Twisted Fate"},
{:name=>"Twitch"},
{:name=>"Udyr"},
{:name=>"Urgot"},
{:name=>"Veigar"}
]
Champion.create(champions)
10.times do |n|
name = Faker::Company.name
clan = Clan.create(:name=>name)
6.times do |n|
name = Faker::Internet.user_name
clan.players.create(:name=>name)
end
end
for clan in Clan.all do
2.times do
match = Match.create()
c = [clan,Clan.first(:offset => rand(Clan.count))]
3.times do
round = match.rounds.create
round.clan_blue = c[0]
round.clan_purple = c[1]
round.winner = c[0]
round.save!
end
for item in c
for p in item.players.limit(5)
rand_champion = Champion.first(:offset => rand(Champion.count))
match.participations.create!(:player => p, :champion => rand_champion)
end
end
match.save!
end
2.times do
match = Match.create()
c = [clan,Clan.first(:offset => rand(Clan.count))]
3.times do
round = match.rounds.create
round.clan_blue = c[0]
round.clan_purple = c[1]
round.winner = c[1]
round.save!
end
for item in c
for p in item.players.limit(5)
rand_champion = Champion.first(:offset => rand(Champion.count))
match.participations.create!(:player => p, :champion => rand_champion)
end
end
match.save!
end
2.times do
match = Match.create()
c = [clan,Clan.first(:offset => rand(Clan.count))]
2.times do |n|
round = match.rounds.create
round.clan_blue = c[0]
round.clan_purple = c[1]
round.winner = c[n]
round.save!
end
for item in c
for p in item.players.limit(5)
rand_champion = Champion.first(:offset => rand(Champion.count))
match.participations.create!(:player => p, :champion => rand_champion)
end
end
match.save!
end
end
有机会优化它们吗?
答案 0 :(得分:2)
不要低估空格在清理代码可读性方面的价值!
class Round < ActiveRecord::Base
belongs_to :match
belongs_to :clan_blue, :class_name => "Clan", :foreign_key => "clan_blue_id"
belongs_to :clan_purple, :class_name => "Clan", :foreign_key => "clan_purple_id"
belongs_to :winner, :class_name => "Clan", :foreign_key => "winner_id"
has_and_belongs_to_many :banned_champions, :class_name => "Champion", :join_table => "banned_champions_rounds"
after_save { match.update_result }
end
class Match < ActiveRecord::Base
def update_result
return unless rounds.count > 0
clan1, clan2 = rounds.first.clan_blue, rounds.first.clan_purple
clan1_wins = rounds.inject(0) {|total, round| total += round.winner == clan1 ? 1 : 0 }
clan2_wins = rounds.length - clan1_wins
self.winner = self.loser = self.draw_1 = self.draw_2 = nil
if clan1_wins == clan2_wins
self.draw1, self.draw2 = clan1, clan2
else
self.winner = clan1_wins > clan2_wins ? clan1 : clan2
self.loser = clan1_wins < clan2_wins ? clan1 : clan2
end
save
end
end
对于你的种子,我会用工厂模式替换你的灯具,如果用于测试的话。但是,如果你要坚持你所拥有的东西,那么将整个块包装在交易中,它应该会变得更快。
答案 1 :(得分:1)
嗯,在你的第一个例子中,你似乎正在强迫Match行为进入Round类,这与抽象OOP不一致。您的update_result方法实际上属于您的Match类。一旦你这样做,我认为代码会清理一下。
在你的第二个例子中,很难看出你想要做什么,但它的速度太慢并不奇怪。每个创建和保存都会生成一个单独的数据库调用。乍一看,您的代码会生成超过一百个单独的数据库保存。你真的需要所有这些记录吗?你能结合一些保存吗?
除此之外,您可以使用build而不是create将数据库调用减半,如下所示:
round = match.rounds.build
round.clan_blue = c[0]
round.clan_purple = c[1]
round.winner = c[0]
round.save!
如果您想保存一些代码行,可以用以下语法替换上面的内容:
match.rounds.create(:clan_blue_id => c[0].id, :clan_purple_id => c[1].id, :winner_id => c[0].id)
答案 2 :(得分:1)
在种子文件中: c = [clan,Clan.first(:offset =&gt; rand(Clan.count))] 这有效,但看起来你在Ruby中选择一个随机数。根据我的理解,如果你可以用SQL而不是Ruby做一些事情,它通常会更快。试试这个:
c = [clan,Clan.find(:all, :limit => 1, :order => 'random()')
你不会获得太多的收益,因为它只为每个战队运行两次(总共20倍),但有类似这两行的
# (runs 60x total)
rand_champion = Champion.first(:offset => rand(Champion.count))
# (runs up to 200x, I think)
c = [clan,Clan.first(:offset => rand(Clan.count))]
一般情况下,您几乎可以始终在程序中找到更多要优化的内容。因此,从最重复的区域开始 - 最深层嵌套的循环,可以最有效地利用您的时间。我将把上述2行(以及其他可能类似的行)优化为练习。如果您遇到问题,请在评论中告诉我。
另外,我相信你会在很多回复中得到很多好的建议,所以我强烈建议您设置一个基准测试程序,以便衡量差异。对于您测试的每个版本,请确保多次运行它,这样您就可以获得良好的平均值(在后台运行的程序可能会使您的结果失效)。
就简单性而言,我认为可读性非常重要。它不会让你的代码运行得更快,但它可以让你的调试更快(而且你的时间很重要!)。给我带来麻烦的一些事情是非描述变量,如c
和p
。我有时也这样做,但是当你在同一范围内有多个这些变量时,我很快就会达到一个点,我认为“那个变量又是什么?”。像temp_clan
而不是c
之类的东西还有很长的路要走。
为了便于阅读,我还希望.each
代替for
。不过,这完全是个人偏好。
btw我喜欢英雄联盟:)
编辑:(评论不会让我缩进代码)再看看,我意识到这个片段可以进一步优化:
for p in item.players.limit(5)
rand_champion = Champion.first(:offset => rand(Champion.count))
match.participations.create!(:player => p, :champion => rand_champion)
end
更改Champion.first(:offset => rand(Champion.count))
rand_champs = Champion.find(:all, :limit => 5, :order => 'random()')
for p ...
i = 0
match.participations.create!(:player => p, :champion => rand_champs(i))
i++
end
这会将5个SQL查询减少为1.因为它被称为60x,这会将你的SQL查询从60减少到12.作为额外的加分,你不会在同一个团队中获得重复的冠军,(或者我猜如果这是你的意图可能是一个缺点)