Как-то не назначая класс с Ruby - PullRequest
0 голосов
/ 08 февраля 2010

Во время выполнения мой код часто сталкивается с неопределенной ошибкой метода для метода mate. Насколько я могу судить, Person каким-то образом проскальзывает через трещины когда-то в исполняемом коде и удается не назначать ему allele.

Код (заявление об отказе от ответственности, не самый лучший формат):

class Allele
  attr_accessor :c1, :c2

  def initialize(c1, c2)
    @c1 = c1
    @c2 = c2
  end

 #formats it to be readable
 def to_s
  c1.to_s + c2.to_s
 end

 #given Allele a
 def combine(a)
  pick = rand(4)
  case pick
   when 0
    Allele.new(c1,a.c1)
   when 1
    Allele.new(c1,a.c2)
   when 2
    Allele.new(c2,a.c1)
   when 3
    Allele.new(c2,a.c2)
  end
 end
end

class Person
 attr_accessor :allele, :male

 def initialize(allele,male)
    @allele = allele
  @male= male
  end

 #def self.num_people
  #@@num_people
 #end

 def to_s
  "Allele:" + allele.to_s + " | Male:" + male.to_s
 end

 def male
  @male
 end

 def allele
  @allele
 end

 def mate(p)
  if rand(2) == 0
   Person.new(allele.combine(p.allele),true)
  else
   Person.new(allele.combine(p.allele),false)
  end
 end
end

male_array = Array.new
female_array = Array.new
male_child_array = Array.new
female_child_array = Array.new

# EVENLY POPULATE THE ARRAY WITH 5 THAT PHENOTYPICALLY MANIFEST TRAIT, 5 THAT DON'T
# AND 5 GIRLS, 5 GUYS
pheno_dist = rand(5)
#make guys with phenotype
pheno_dist.times { male_array << Person.new(Allele.new(1,rand(2)),true) }
#guys w/o
(5-pheno_dist).times { male_array << Person.new(Allele.new(0,0),true) }
#girls w/ pheno
(5-pheno_dist).times { female_array << Person.new(Allele.new(1,rand(2)),false) }
#girls w/o
pheno_dist.times { female_array << Person.new(Allele.new(0,0),false) }

puts male_array
puts female_array
puts "----------------------"

4.times do
 #mates male with females, adding children to children arrays. deletes partners as it iterates
 male_array.each do
  male_id = rand(male_array.length) #random selection function. adjust as needed
  female_id = rand(female_array.length)
  rand(8).times do
   child = male_array[male_id].mate(female_array[female_id])
   if child.male
    male_child_array << child
   else
    female_child_array << child
   end
  end
  male_array.delete_at(male_id)
  female_array.delete_at(female_id)
 end

 #makes males male children, females female children, resets child arrays
 male_array = male_child_array
 female_array = female_child_array
 male_child_array = []
 female_child_array = []

 puts male_array
 puts female_array
 puts "----------------------"
end

Что сразу выглядит не так?

Ответы [ 2 ]

3 голосов
/ 08 февраля 2010

Как говорит egosys, вам не следует удалять из массива, для которого вы выполняете итерацию.

Другая проблема в вашем цикле, который запускает "4.times do". Иногда женский массив пуст, поэтому возвращает размер 0; rand (0) - это случайное число с плавающей запятой> = 0 и <1. Использование этого в качестве индекса массива для пустого female_array возвращает nil, который затем передается mate. </p>

Но это не так. Вы перебираете male_array, используя каждый, но затем выбираете мужчину наугад. Это позволяет некоторым мужчинам спариваться более одного раза; других нет вообще. Точно так же, некоторые женщины достигают спаривания и размножаются более одного раза в каждой итерации, другие - вовсе нет. Это твое намерение?

Давайте сначала рассмотрим вопрос о сохранении всех мужчин и женщин в одном массиве. Это упростит вещи. Однако, поскольку вам иногда нужно найти всех самцов, а иногда и всех самок, мы сделаем для этого следующие методы:

def males(population)
  population.find_all do |person|
    person.male?
  end
end

def females(population)
  population.find_all do |person|
    person.female?
  end
end

Было бы более биологически точным, если бы мужчины и женщины были в паре случайным образом, но никто не может спариться более одного раза. Это довольно просто:

def random_pairs(males, females)
  males.shuffle[0...females.size].zip(females.shuffle)
end

Тогда воспроизводство населения становится, просто:

def make_children(male, female)
  # return an array of children
end

def reproduce(population)
  children = []
  random_pairs(males(population), females(population)).each do |male, female|
    children << make_children(male, female)
  end
  children
end

Имея такие функции, выполнить 4 цикла воспроизведения будет так просто:

people = # ... generate your initial list of people of all sexe.
4.times do
  people = reproduce(people)
end

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

Больше можно было бы сделать в ОО-стиле, например, сделав Population первоклассным объектом и переместив в него функции "мужчины", "женщины", "random_pairs" и "размножение". Я оставлю это как упражнение для читателя.

1 голос
/ 08 февраля 2010

Удаление из массива, с которым вы итерируете, имеет неопределенное поведение. Обычно совет - использовать Array # delete_if, но я не уверен, как бы вы использовали его в этом случае.

...