Поведение назначения Datamapper - PullRequest
0 голосов
/ 16 ноября 2011

У меня есть сценарий, который создает футбольный матч.

Каждый клуб может играть только в одну игру с другими клубами, и это может быть дома или в гостях.

Это мой действительный кодкоторый не работает с 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 Ответ

1 голос
/ 16 ноября 2011

Когда вы делаете это:

clubs_to_play = all_clubs = Club.all

То, что вы в основном делаете, это:

all_clubs = Club.all
clubs_to_play = all_clubs

Это один и тот же объект (который вы увидите, если посмотрите на их object_id s).

DataMapper не материализует наборы записей, пока не будет вызван метод кикера. Это в основном означает, что SQL не выполняется, пока вы не начнете выполнять итерации. Конечно, поскольку обе эти переменные указывают на один и тот же объект, когда вы начинаете итерацию all_clubs, вы извлекаете все записи из базы данных в и all_clubs и clubs_to_play, поскольку они являются точно такой же объект .

И наоборот, когда вы делаете это:

all_clubs = Club.all
clubs_to_play = Club.all

Здесь all_clubs и clubs_to_play ссылка различные объекты . То есть они имеют различное object_ids, и когда вы повторяете all_clubs, все его записи материализуются, но clubs_to_play остается неизменным, пока вы не выполните итерацию (и не удалите значения из другой коллекции).

Поскольку ваш алгоритм предполагает, что all_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

Существует технически более функциональных способов поддержания чередования между домом и выездом, но они более запутанны. Enumerable#combination возвращает все возможные (уникальные) комбинации значений в коллекции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...