Somehow not assigning a class with Ruby
On runtime, my code often come into an undefined method error for the method mate
. As far as I can figure, a Person
somehow slips through the cracks sometime along the code's exucution, and manages not to have an allele
assigned to it.
Code (disclaimer, not the best formatted):
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
#m开发者_C百科ates 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
What immediately looks wrong?
As egosys says, you ought not to delete from an array over which you are iterating.
Another problem is in your loop that starts "4.times do". Sometimes the female array is empty, so returns size 0; rand(0) is a random float >= 0 and < 1. Using that as an array index on an empty female_array returns nil, which is then passed to mate.
But there's more than that wrong. You're iterating over male_array using each, but then picking a male at random. That allows some males to mate more than once; others not at all. Similarly, some females get to mate and reproduce more than once in each iteration, others not at all. Is that your intent?
Let's first consider keeping all of the males and females in the same array. This will simplify things. However, since you do need to sometimes find all the males and sometimes all the females, we'll make methods for that:
def males(population)
population.find_all do |person|
person.male?
end
end
def females(population)
population.find_all do |person|
person.female?
end
end
It would be more biologically accurate if males and females should be paired at random, but nobody gets to mate more than once. That's pretty simple:
def random_pairs(males, females)
males.shuffle[0...females.size].zip(females.shuffle)
end
Then reproduction of a population becomes, simply:
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
Having such functions, then doing 4 cycles of reproduction would be as simple as this:
people = # ... generate your initial list of people of all sexe.
4.times do
people = reproduce(people)
end
Since no function modifies the arguments passed to it, you will have no troubles with side-effects.
More could be done in an OO style, for example making Population a first-class object and moving the functions "males", "females", "random_pairs" and "reproduce" into it. I'll leave that as an exercise for the reader.
Deleting from an array which you are iterating with each has undefined behavior. Usually the advice is to use Array#delete_if, but I'm not sure how you would use it in this case.
精彩评论