Understand Ruby scopes and Ruby scopes

Source: Internet
Author: User

Understand Ruby scopes and Ruby scopes

 

What is scope?

When talking about the scope, you should immediately thinkVariableAndVisibilityThese two words,VariableAndVisibilityIt is the main content of the scope. Yes, the scope is about where the code variables are visible. After you fully understand the scope, I will give you a piece of code, you can easily know what variables are visible at this time, and most importantly, what variables are invisible during code execution.

So we can define all the variables from the very beginning so that all the variables are visible in all aspects of the program, so we can avoid the scope issue? Isn't this making life easier? Well, this is not the case ......

You may know many opposing programmer camps, such as functional programming camps and object-oriented programming camps, different variable naming styles camps, and different code format camps, but no one has ever heard of the camp that supports removing scopes, especially those programmers with rich programming experience are loyal supporters of retaining scopes. Why? Because the more you program, the more stupid and destructive you will find to keep all variables visible throughout the program, since all variables are defined and visible to the entire program at the beginning, it is difficult for you to track when and when the Code modifies the variable when the program is running, for projects with multi-person collaboration, it is hard for you to know who defined a variable in the face of thousands of lines of code? Where is the value assigned? Using a large number of global variables makes it difficult for your program to detect and track, and the running results are unpredictable. If you use global variables, you may encounter a very tricky problem: how to uniquely name thousands of global variables.

Scope provides developers with a way to implement the Minimum permission principle similar to that in computer security systems. Imagine that you are developing a banking system, and everyone can read and write all the data, A person made a change to the deposit but was not sure he was the owner of the deposit. What a terrible thing!

Ruby variable scope quick browsing

You may have some knowledge about ruby's variable scope, but I found that most of the tutorials only give a brief introduction to the variable type, without a specific explanation of it, the following is a detailed description of various types of variables in ruby:

Class variable (prefix with @): only visible to the class and its subclass that define the class variable.

Instance variable (prefix with @): It is visible to the instance of the class that defines the variable and its instance method, but cannot be directly used by the class.

Global variables (prefixed with $): visible to the entire ruby script program.

Local variables: only visible in local code blocks. This is also the most frequently used and prone variable type in programming, because the scope of local variables depends on a lot of context code blocks.

The following uses an image to describe the relationship between the four variable scopes in a concise and clear manner.


In the next chapter, I will focus on introducing this local variable. From my experience and conversations with others, I found that most scope problems are caused by lack of a good understanding of local variables.

When is a local variable defined?

 In ruby, instance variables (such as @ variable) defined in a class do not need to be explicitly declared in advance, if you try to obtain an undeclared instance variable in the class method, nil is returned. When you try to obtain the value of an undeclared local variable, A NameError error is thrown. (undefined local variable or method).

The Ruby interpreter adds this variable to the local scope when it sees the value assignment statement of the local variable. It should be noted that whether or not the value assignment of this local variable is executed, as long as the ruby interpreter sees that the program has a value assignment statement for this variable, the variable will be added to a local scope, so the following code can be executed normally without reporting an error.

if false # the code below will not run
  a = 'hello' # Ruby interpreter sees this statement adding the a variable to the local scope
end
p a # nil, because the assignment statement to a is not executed
 

  You can try to delete the a = ‘hello’ statement to see what happens.

  Local variable naming conflict

  Suppose you have the following code

def something
  'hello'
end

p something
==> hello
something = 'Ruby'
p something
==> Ruby # 'hello' is not printed
  In Ruby, the method call can be like a variable without explicitly adding a bracket and method receiving object, so you may encounter a naming conflict problem like the above code.

  When there is a variable name and a method name with the same name in your ruby code, the variable with the same name will override the method with the same name with a higher priority, but this does not mean that you can no longer call the method. Add parentheses explicitly or add self as the method receiving object before calling the method.

def some_var; 'I am a method'; end
some_var = 'I am a variable'
p some_var # I am a variable
p some_var () # I am a method
p self.some_var # I am a method. Explicitly call some_var method using self object
  A very effective method to judge whether the variable is out of scope

  First find the variable you want to view in your code segment, and then look up the change amount until you find the variable, then there will be two cases:

  If you encounter the condition of 1 before encountering 2, it is very likely that your code will throw a NameError error. If you encounter the condition of 2 before encountering 1, then congratulations, the local variable is here The scope of the segment code.

  Instance variables vs local variables

  An instance variable belongs to an object and is available in all methods of the object. When a local variable belongs to a specific scope, it is only available under that scope. Instance variables are available for modification in each new instance, and local variables will be changed or overwritten when entering a new scope. How do you know when the scope will change? The answer is: Scope Gate.

  Scope Gate: An important concept to understand scope

  When using the following statements, what effect do you guess have on the scope?

  When you use these keywords, you open up a new scope, which is equivalent to Ruby opening a door to let your code enter a new context. All class / def / module definitions are called scope gates because they open a new scope. In this scope, all the old scopes are no longer available, and the old local variables are replaced by the new local variables. Substitute.

  If you are confused about the above statement, it does not matter, the following example will allow you to better grasp the concept.

v0 = 0
class SomeClass # open new scope
  v1 = 1
  p local_variables # print out all local variables

  def some_method # open a new scope
    v2 = 2
    p local_variables
  end # scope closed
end # scope closed

some_class = SomeClass.new
some_class.some_method
  When you run the above code, you will see that [: v1] and [: v2] are printed separately, why is v0 not in the local variable in SomeClass? v1 is no longer in some_method method? It is precisely because the class and def keywords respectively open up new scopes and replace the old scopes. The local variables of the old scopes no longer exist in the new scopes. Why is the scope replaced by " "Are you? Because the old scope is only temporarily replaced, when the new scope is closed, it will return to the old scope again. After running the p_local_variables statement after some_class.som_method, you will see v0 reappear in the local variable list.

  Break the scope gate

  As you can see, class / def / module will limit the scope of local variables, and will shield the previous scope, making the originally defined variables invisible in the new scope, then if we want to deal with the role of the definition How should local variables outside methods, classes, and modules break the isolation between scopes?

  The answer is simple, just replace the scope gate method with the method call, that is:

Use Class.new instead of class
Replace module with Module.new
Use define_method instead of def
  Below is an example:

v0 = 0
SomeClass = Class.new do
  v1 = 1
  p local_variables

  define_method (: some_method) do
    v2 = 2
    p local_variables
  end
end

some_class = SomeClass.new
some_class.some_method
  After running the above code, it will print out [: v1,: v0,: some_class] and [: v2,: v1,: v0,: some_class] two lines of local variable names, you can see that we successfully broke the scope The limitation is due to the function of blocks in ruby that we need to learn next.

  Are Blocks also a type of scope gate?

  You might think that blocks are also a type of scope gate. After all, it also creates a scope that contains local variables, and the local variables defined in blocks are not accessible externally, as in the following example:

sample_list = [1,2,3]
hi = '123'
sample_list.each do | item | # start of block code block
  puts hi # Does it print 123 or throws an error?
  hello = 'hello' # declare and assign to the variable hello
end

p hello # Print ‘hello’ or throw undefined local variable exception
  As you can see, the variable ‘hello’ defined in the blocks code block is a local variable that exists only in the scope of the block and cannot be accessed or visible from the outside.

  If the block code block is a scope gate, then an exception should be triggered when the statement puts hi is executed, but the value of hi can be successfully printed in the block, and you can not only access the value of hi in blocks, You can modify it, try to modify the value of hi in the do / end code block to '456', you will find that the value of the external variable hi is successfully modified.

  What if you don't want the code in the block to modify the value of external local variables? This is that we can use block-local variable (a formal parameter in a similar method), just use the parameter in the block; fill in the name of the local variable with the same external name after splitting (these variables will successfully block-local variables in the block), The modification of these variables in the block will not affect the original value of the external, the following is an example:

hi = 'hi'
hello = 'hello'
3.times do | i; hi, hello |
  p i
  hi = 'hi again'
  hello = 'hello again'
end
p hi # "hi"
p hello # "hello"
  If you remove; hi, hello from the block's parameter list, you will find that the values of variables hi and hello become 'hi again' and 'hello again' outside the code block.

  Remember to use do and end to create a block code block while creating a new scope.

[1,2,3] .select do | item | # do is here, new scope is being introduced
  # some code
end
Using each, map, detect, or other methods, when you use do / end to create a block of code as a parameter to these methods, just create a new scope.

Some quirks of Blocks and Scope

Imagine what the following code will output:

y to guess what will this Ruby code print:

2.times do
  i || = 1
  print "# {i}"
  i + = 1
  print "# {i}"
end
  Do you think it will output 1 2 2 2? But the answer is 1 2 1 2 because each iteration will create a new scope and reset local variables. Therefore, in this code we created two iterations, each resetting the variable i to 1 at the beginning of each iteration.

  So what do you think the following code will output?

def foo
  x = 1
  lambda {x}
end

x = 2

p foo.call
  The answer is 1, because the blocks and blocks objects see the scope defined in them rather than the scope when the block is called. This is related to how they are regarded as closures in Ruby. Closures are a kind of inclusion of code, so that the code has the following characteristics:

This part of the code can be called like an object (can be called by call after definition)
When the closure is defined, record the variables under the scope
  These features provide us with convenience when writing infinite number generators, etc .:

def increase_by (i)
  start = 0
  lambda {start + = i}
end

increase = increase_by (3)
start = 453534534 # won't affect anything
p increase.call # 3
p increase.call # 6
  You can use lambda expressions to conveniently defer variable assignments:

i = 0
a_lambda = lambda do
  i = 3
end

p i # 0
a_lambda.call
p i # 3
  What do you think the last line of code in the following code segment will output?

a = 1
ld = lambda {a}
a = 2
p ld.call
  If your answer is 1, then you are wrong and the last line of statement will output 2. Hey, wait a minute, don't lambda expressions see their scope? If you think about it again, you will find that this is actually correct. A = 2 is also in the scope of the lambda expression. As you can see, the lambda expression only starts calculating the variable when it is called value. If you do not consider this, it will be easy to trigger bugs that are difficult to track down.

  How to share variables in two methods

  Once we know how to break the scope gate, we can use these to achieve amazing results. I learned this knowledge from the book Ruby Metaprogramming. This book helps me understand the working principle of the scope behind ruby. Below is an example:

def let_us_define_methods
  shared_variable = 0

  Kernel.send (: define_method,: increase_var) do
    shared_variable + = 1
  end

  Kernel.send (: define_method,: decrease_var) do
    shared_variable-= 1
  end
end

let_us_define_methods # Initialize the definition of increase_var and decrease_var methods after running this statement
p increase_var # 1
p increase_var # 2
p decrease_var # 1
  Is it very concise?

  Top-level scope

  What's the top-level scope? How to determine whether the current code is in the top-level scope? Being in the top-level scope means that you have not called any methods or all of your methods have been called and returned.

  In ruby, everything is an object. Even if you are in the top-level scope, you are also in an object (the object inherits from the Object class, called the main object), try to run the following code to verify.

p self # main
p self.class # Object
  Where am I?

  In debugging, if you know the current self value will solve many of your headaches, the current self value affects the actual Example variables and method calls that do not explicitly indicate the receiving object. If you have determined that your variable or method has been defined (you see the definition of the variable / method in the program) but still trigger the undefined method / instance variable error, then very It is possible that the self value is problematic.

  Quiz: What variables are available?

  To confirm whether you have mastered the knowledge mentioned in this article, please assume that you are a small ruby debugger to run the following code:

class SomeClass
  b = 'hello'
  @@ m = 'hi'
  def initialize
    @some_var = 1
    c = 'hi'
  end

  def some_method
    sleep 1000
    a = 'hello'
  end
end

some_object = SomeClass.new
some_object.some_method
  With the statement sleep 1000 as a breakpoint, what can you see? What variables are available at this breakpoint now? Before you run the code, think about it and give your own answer. Your answer should not only contain the available variable, but also explain why the variable is available at this time.

  As I mentioned before, local variables are bound in scope. When some_method function is defined, it will break a new scope and replace its previous scope with a new scope. In the new scope you , Variable a is the only variable available.

  The instance variable is bound to self. In the above code, some_object is the current object and @some_var is the strength variable available for the entire some_object. Class variables are similar, @mm instance variables are also available in the current object. The local variables b and c become invisible in the method because of the scope gate. If you want to make them visible, see the section on breaking scope gates.

 I hope my article is helpful to you, if you have any doubts, please comment in the comments.

 

----------------------------------------Dividing line-------- --------------------------------------------

  This article is translated from Darko Gjorgjievski's Understanding Scope in Ruby. I think this article explains the scope better, and it is helpful to understand the scope of ruby, so I translated it.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.