Skip to content

Instantly share code, notes, and snippets.

@photomattmills
Last active August 29, 2015 14:19
Show Gist options
  • Save photomattmills/0bfdaddce739152058ee to your computer and use it in GitHub Desktop.
Save photomattmills/0bfdaddce739152058ee to your computer and use it in GitHub Desktop.
A note about multiple assignment in ruby (with mandelbrot!)

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment