Using Local Variables in Define_Method

Using local variables in define_method

As mentionned in comments this is down to closures - the block passed to define_method captures the local variables from its scope (and not just their value as you found out).

for doesn't create a new scope, so your method 'sees' the change to i. If you use a block (for example with each) then a new scope is created and this won't happen. You just need to change your code to

class Test
def self.plugin
(1..2).each do |i|
define_method("test#{i}".to_sym) do
p i
end
end
end
plugin
end

which is basically what the question linked by Arup was about

How define_method uses variables initialised outside of it

Because blocks (do...end) are closures and have access to their
surrounding scope.

You used block with class_eval,so it has the access to its surroundings,which is the scope of the method add. Now you use another block with define_method,which as also has the access to the scope of the method add,via the block with the class_eval.match local variable has been created inside the scope of the method add. So the blocks has the access to the variable.

And, at last but not least, can I create class method here using define_method?

No you can't.define_method Defines an instance method in the receiver.self.class is Temp1. Now under Temp1.class_eval do..end,you are defining instance methods of the class Temp1,with the method define_method.define_method is a private class method of all the classes,in which ancestor chain Object class present.

class C;end
C.private_methods.grep(/define_/)
# => [:define_method]

Also, i don't see difference here between using class_eval or instance_eval, seems like it do the same thing.

Okay! Let me explain for you. You can't see the difference here,as Teamp1 is a Class and also an instance of a Class. In both call class_eval and instance_eval,self is being set to Teamp1,by their respective definition as documented.

class C
def self.bar;11;end
def baz;12;end
end

C.is_a? Class # => true
C.instance_of? Class # => true

C.class_eval{ bar } # => 11
C.instance_eval{ bar } # => 11

Hope this helps!

How to dynamically define a class method which will refer to a local variable outside?

Class methods don't really exist in Ruby, they are just singleton methods of the class object. Singleton methods don't really exist, either, they are just ordinary instance methods of the object's singleton class.

Since you already know how to define instance methods (using Module#define_method), you already know everything you need to know. You just need to call class_eval on C's singleton class instead of C itself:

(class << C; self end).class_eval do
define_method(:a_class_method) do
puts var
end
end

Current versions of Ruby have a singleton_class method to make that easier:

C.singleton_class.class_eval do
define_method(:a_class_method) do
puts var
end
end

But actually, current versions of Ruby also have Module#define_singleton_method, so, in this particular case, that is unnecessary:

C.define_singleton_method(:a_class_method) do
puts var
end

Why does storing a local variable and reading it back trigger a TargetInvocationException?

To use a local variable in IL, you first need to declare it, so that the runtime knows its type. To do that, use ILGenerator.DeclareLocal().

You might also consider using the LocalBuilder returned from DeclareLocal() when emitting instructions that use the variable. This way, you don't need to remember the indexes of all your local variables:

var local = il.DeclareLocal(typeof(int));



il.Emit(OpCodes.Stloc, local);
il.Emit(OpCodes.Ldloc, local);

How are variables bound to the body of a define_method?

Blocks in Ruby are closures: the block you pass to define_method captures the variable name itself–not its value—so that it remains in scope whenever that block is called. That's the first piece of the puzzle.

The second piece is that the method defined by define_method is the block itself. Basically, it converts a Proc object (the block passed to it) into a Method object, and binds it to the receiver.

So what you end up with is a method that has captured (is closed over) the variable name, which by the time your loop completes is set to :destroy.

Addition: The for ... in construction actually creates a new local variable, which the corresponding [ ... ].each {|name| ... } construction would not do. That is, your for ... in loop is equivalent to the following (in Ruby 1.8 anyway):

name = nil
[ :new, :create, :destroy ].each do |name|
define_method("test_#{name}") do
puts name
end
end
name # => :destroy

confused between instance variable and local variable

In your first example you created a local variable updated, that is only accessible within the scope of the block it is defined in. Meaning, it is available only within Prime.each(number) do end block.

In your second example you created instance variable @updated.

without creating a class how my instance variable ( @updated) is
working

It is because in Ruby everything occurs in the context of some object. Even though you did not created a class, you are being in the top-level context, in the context of object main.

Thus, any instance variables defined within top-level are instance variables of this object main.

So going back to your issue, to overcome it you'll want to just define the updated local variable outside the Prime.each(number) do end block:

def prime_large(number)
arr_prime = []
updated = nil # initialize local varialbe
Prime.each(number) do |x|
new_arr_prime = arr_prime.push(x.to_s)
updated = new_arr_prime.select { |y| y.reverse == y } # assign new value
end
p updated.max
end
p prime_large(1000)

To test it you can open irb or pry and check it yourself:

self               # the object in the context of which you are currently
#=> main
self.class # main is an instance of class Object, any methods defined
# within main are added to Object class as instance methods
#=> Object
instance_variables # list of it's instance variables, none yet created
#=> []
@variable = 1 # create and instance variable
#=> 1
instance_variables # now created variable occurs in the list of current object's instance variables
#=> [:@variable]
def hello; :hello end # define method
#=> :hello
self.class.public_instance_methods(false) # list instance methods defined in Object
#=> [:hello]

What you now want to read about is lexical scopes in Ruby.

Why does a local variable lose its value when defining a method with define_method?

If you define a local variable inside a block, it will vanish when the end of the block is reached.

To achieve the lifetime you want, you need to define the memory variable before the block:

def memoize(obj, method)
memory = {}
ghost = class << obj; self; end
ghost.class_eval do
define_method(method) do |*args|
memory.has_key?(args) ? memory[args] : memory[args] = super(*args)
end
end
end

How can I access local variable in main code block?

Design-wise, global variables (static fields in Java) are usually not a great idea because it causes a tight coupling between classes, making it harder to make changes to the system later on.

That said, to do what you describe you would do this:

public class YourClass {

// class field
public static boolean checkWord;

// instance (object) field
private TextField textField = new TextField("Your text field");

public void yourMethod() {
textField.setOnAction(event -> {
// ...
checkWord = true;
// ...
});
System.out.println(checkWord);
}

public static void iDoNotKnowAboutInstances() {
// OK
System.out.println(checkWord);
// Compile error - cannot refer to instance field in static context
System.out.println(textField);
}
}

Meanwhile, in another class:

public class YourOtherClass {

public void yourOtherMethod() {
System.out.println(YourClass.checkWord);
}
}

A static field exists at class level. It is initialized when the class is loaded by the class loader for the first time, in this case it will be initialized as false (the default for booleans). Then, when yourMethod is executed and an event is handled, the field checkWord is set to true. It can be referred to directly from within the same class. From another class it can be referred to by prefixing the class name, as shown in YourOtherClass.

EDIT: Not that you can refer to static fields from anywhere (as long as their visibility qualifier allows it) but you only refer to instance field via an actual instance. So for example from the static method iDoNotKnowAboutInstances you cannot refer to instance field textField. You often run into this when you create a simple java application with the entry method public static void main(String[] args). If you then add instance fields to the class you will first need to create an instance of the class using YourClass instance = new YourClass() to be able to read and write those fields.



Related Topics



Leave a reply



Submit