ruby - Setting method local variables from a proc -
if have class 2 instance variables @x
, @y
, can change them proc using self.instance_exec
:
class location attr_accessor :x, :y def initialize @x = 0 @y = 0 end def set &block self.instance_exec(&block) end end location = location.new location.set @x = rand(100) @y = rand(100) end puts location.x puts location.y
if have class method set
2 local variables x
, y
, can use proc return values set them:
class location def set &block x = 0; y = 0; x, y = block.call() # x , y puts x puts y end end location = location.new location.set x = rand(100) y = rand(100) [x, y] end
is there way access set
method local variables x
, y
proc without using return values?
you can it, sort of, isn't pretty
there way block set variable in calling method, isn't pretty. can pass in binding, eval code using binding:
def foo(binding) binding.eval "x = 2" end x = 1 foo(binding) p x # => 2
blocks carry them binding in defined, if block being passed, then:
def foo(&block) block.binding.eval "x = 2" end x = 1 foo {} p x # => 2
what's in block doesn't matter, in case. it's being used carrier binding.
other ways achieve same goal
yield object
a more pedestrian way block interact it's caller pass object block:
class point attr_accessor :x attr_accessor :y end class location def set point = point.new yield point p point.x # => 10 p point.y # => 20 end end location = location.new location.set |point| point.x = 10 point.y = 20 end
this preferred fancier techniques: it's easy understand both implementation , use.
instance_eval object
if want (but shouldn't want to), block's caller can use instance_eval/instance_exec call block. sets self object, block.
class location def set(&block) point = point.new point.instance_eval(&block) p point.x # => 10 p point.y # => 20 end end location = location.new location.set self.x = 10 self.y = 20 end
you see block had use use self.
when calling writers, otherwise new, local variables have been declared, not wanted here.
yield or instance_eval object
even though still shouldn't use instance_eval, it's useful. don't know when it's good, though, let's let method's caller decide technique use. method has check block has parameters:
class location def set(&block) point = point.new if block.arity == 1 block.call point else point.instance_eval(&block) end p point.x p point.y end end
now user can have block executed in scope of point:
location = location.new location.set self.x = 10 self.y = 20 end # => 10 # => 20
or can have point passed it:
location.set |point| point.x = 30 point.y = 40 end # => 30 # => 40
Comments
Post a Comment