I'm implementing a weighted lottery in groovy. It allows some participants to have a better chance at winning than others (basically exactly like the NBA draft). It works by tossing each participant into an array N times where N is the number of chances you have to win. It then picks a random index from that array.
Like a good little coder, I wrote a test. It picks a winner from the group 100 times and outputs how many times each participant was picked. The expectation being that it would fall roughly in line with how many times they should be picked (based on their number of chances). The results were...off.
I narrowed the issue down to a single line that, if split into 2 separate statements, works perfectly. A slimmed down version of the routine is below. The "bad" version is active and the "good version" is commented out
def randomInRange(int min, int max) {
Random rand = new Random()
rand.nextInt((max - min) + 1) + min
}
def bob = [name:'bob', timesPicked:0]
def joe = [name:'joe', timesPicked:0]
def don = [name:'don', timesPicked:0]
def chanceWheel = []
//don should get picked a lot more
2.times{chanceWheel << bob}
2.times{chanceWheel << joe}
6.times{chanceWheel << don}
//pick somebody at random from the chance wheel
100.times{
//this will produce timesPicked counts that do NOT sum to 100 and usually under-represents don
chanceWheel[randomInRange(0,9)].timesPicked++
//splitting the logic into 2 lines will always have the correct sum of timesPicked with roughly the right distribution of winners
//def picked = chanceWheel[randomInRange(0,9)]
//picked.timesPicked++
}
println bob
println joe
println don
My question is what is wrong with the one liner version? My guess is that its an order of execution issue but I cannot for the life of my figure out where its going off the rails.
Yes, the "?:" operator will return the value to the left, if it is not null. Else, return the value to the right. "Yes, the "?:" operator will return the value to the left, if it is not null." - That is incorrect.
It's somewhat acceptable in scripts (Groovy scripts and groovysh allow you to do so), but in production code it's one of the biggest evils you can come across which is why you must define a variable with def in all actual groovy code (anything inside a class).
Logical operators Groovy offers three logical operators for boolean expressions: && : logical "and" || : logical "or" ! : logical "not"
chanceWheel[randomInRange(0,9)].timesPicked++
is
chanceWheel[randomInRange(0,9)].timesPicked =
chanceWheel[randomInRange(0,9)].timesPicked + 1
which calls randomRange()
twice in contrast to the working example where it is called once and assigned to a variable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With