数据映射器分配行为

时间:2011-11-16 06:41:15

标签: ruby datamapper

我有一个创建足球装备的脚本。

每个俱乐部只能与其他俱乐部进行一场比赛,而且可以在家里或远方。

这是我的实际代码,不适用于20个俱乐部(它创造了145场比赛,而不是190场比赛,而且俱乐部没有打出相同数量的比赛)

  clubs_to_play = all_clubs = Club.all #problematic line

  all_clubs.each do |club|
    home = true
    clubs_to_play.delete(club)
    clubs_to_play.each do |club_to_play|
      f = Fixture.new
      if (home)
        f.home = club
        f.away = club_to_play
        home = false
      else
        f.home = club_to_play
        f.away = club
        home = true
      end
      f.save
    end
  end
end

但如果我将第一行更改为:

clubs_to_play = Club.all
all_clubs = Club.all

该脚本可以工作并生成190个匹配项。那是为什么?

1 个答案:

答案 0 :(得分:1)

执行此操作时:

clubs_to_play = all_clubs = Club.all

你基本上做的是:

all_clubs = Club.all
clubs_to_play = all_clubs

它们是相同的对象(如果你查看它们的object_id,你会看到它们。)

在调用kicker方法之前,DataMapper不会实现记录集。这基本上意味着在您开始迭代之前不会执行SQL。当然,由于这些变量都指向同一个对象,当您开始迭代all_clubs时,您从 all_clubs和{{1}中的数据库中提取所有记录因为它们是完全相同的对象

相反,当你这样做时:

clubs_to_play

此处all_clubs = Club.all clubs_to_play = Club.all all_clubs引用不同的对象。也就是说,它们具有不同的clubs_to_play,并且当您迭代object_ids时,其所有记录都已实现,但all_clubs保持不受影响,直到您迭代它(并从其他集合中删除值)

由于您的算法假设clubs_to_playall_clubs可以彼此独立地修改,因此当它们是同一个对象时,您会得到不希望的行为。

尝试这样的(稍微更具功能性)方法:

clubs_to_play

技术上有更多功能性的方法来维持主场和客场之间的交替,但它们更复杂。 Club.all.combination(2).each_with_index do |(club1, club2), idx| Fixture.create( :home => idx.even? ? club1 : club2, :away => idx.even? ? club2 : club1 ) end 返回集合中所有可能的(唯一)值组合。