I was looking at Facebook's 'this day in your timeline' thingy when I came across this tweet that I'd retweeted way back in prehistory: https://twitter.com/dbrady/status/12546255974 . It contains a one-liner that prints an ASCII mandelbrot set:
60.times{|a|puts((0..240).map{|b|x=y=i=0;until(x*x+y*y>4||i==99);x,y,i=x*x-y*y+b/120.0-1.5,2*x*y+a/30.0-1,i+1;end;i==99?'#':'.'}*'');}
I wanted to understand it, so the first thing I did was unfold all the blocks, and turn the multiple assignment into three separate lines:
60.times do |a|
puts((0..240).map do |b|
x=y=i=0
until(x*x+y*y>4||i==99)
x = x*x-y*y+b/120.0-1.5
y = 2*x*y+a/30.0-1
i = i+1
end
i==99?'#':'.'
end*'')
end
This didn't work; it produced something like the mandelbrot set, but a bit blobbier. I scratched my head, tried to reason about it, knew it was something to do with multiple assignment, and went to bed. This morning, I came up with a solution:
60.times do |a|
puts((0..240).map do |b|
x=y=i=0
until(x*x+y*y>4||i==99)
x_ = x*x-y*y+b/120.0-1.5
y = 2*x*y+a/30.0-1
x = x_
i=i+1
end
i==99?'#':'.'
end*'')
end
The reason this works is because in the first version, with the multiple assignment, all of the expressions on the right are evaluated first. So, if you try assigning them one at a time, like the first version, x changes and makes the next line, for y, give the wrong result for this iteration. Thus, storing the new x in x_ until after the new y is calculated.