Difference Between Various Variables Scopes in Ruby

Difference between various variables scopes in ruby

  1. Class variables are the same for all instances, because they're class variables–associated with the class. Everything access the same variable, including each instance.

  2. No. Local variables are just that–local. They may be local to a function, or local to the class declaration, which is different than being a class variable. Locals in a class declaration go out of scope when the class declaration ends.

  3. That's because they're exactly the same–they're global. Global state is always evil; this is not a property of the language or environment. That said, some global state may be required–that's just the way it is. It makes sense to use global state when there's global state. The trick is to use global state properly, which is sometimes a non-trivial endeavor.

  4. That's just how Ruby is.

  5. One has already been given by Chris.

  6. I would think this question would be largely self-answering. Global when the entire world needs access. Instance when it's specific to a class instance. Local when it's only required in a local scope (e.g., a method, a block (note differences between 1.8 and 1.9 with regard to block scope), etc.) Constant when the variable isn't supposed to change. A class variable when it's something that either every instance needs, or if exposed via a class method, something tightly associated with a class.

  7. There is no "most use-cases", it totally depends on what you're doing with the variable. And public isn't the de facto choice in Java–it depends on the entity in question. Default Java scope is package-private (methods, properties). Which to use in Ruby depends entirely upon the use-case, noting that as with Java, and even more easily in Ruby, things can be circumvented.

What is the difference in scoping between a global variable and a constant in Ruby?

Global variables are the ones that can be accessed from anywhere. Their scope turns to be the whole of the main object. This means they can be used anywhere in this scope i.e anywhere in the code itself. For instance

module A
module B
class C
$glo = 'this is glo-bal variable'
end
end
end

module D
class E
CON = 'this is con-stant'
def call_glo
puts $glo
end

def call_con
puts CON
end
end

def self.call_con
puts CON
end

E.new.call_glo #=> "this is glo-bal variable"
end

D::E.new.call_glo #=> "this is glo-bal variable"
D::E.new.call_con #=> "this is con-stant"
D.call_con #=> Throws Error Unitialized Constant

While the constants are restricted to the scope they are defined in. They can only be used in the scope they are defined.

Now, as you said Constants starts with capitals, hence all the class names and module names are themselves nothing but Constants.

Now in the above example, you see the call_glo method is called twice. Once from the scope of module D while one from the main object scope, do you see the difference between the instantiation of class E?

In module D it is called without any scope operator :: while outside of module we had to use the scope operator, that is the restriction of scope. Constants are bound to.

Difference between using higher scope variables and using variables explicitly passed in a function

If a function uses a variable from scope that might cause a side effect(function modifying the outer variable) and this is considered bad practice because makes function impure.

Global variables considered bad practice and should only be used if variable is constant. If the variable is constant it is okay, because now function can't modify the scope.

Difference between Ruby/JS instance variable scope

@ is roughly the same as this in javascript, it refers to the current instance. The initialize method in a class is a constructor, not a getter/setter.

function Greeter( name ) { 
/*Functions called with `new` return objects
with their proto link pointing to the object
pointed at function's .prototype property at the time of instanciation. */
this.name = arguments.length < 1 ? "World" : name; //No real way to do optional arguments
}

Greeter.prototype.say_hi = function() {
alert( "Hi " + this.name + "!" ); //Methods are defined outside on the `.prototype` object
};

Greeter.prototype.say_bye = function() {
alert( "Bye " + this.name + ", come back soon." );
};

Ruby Instance Variables or Local Variables?

I try to explain it with a little example:

class MyClass
def meth
conn = 1
end
def result
conn
end
end

x = MyClass.new
p x.result #-> test.rb:6:in `result': undefined local variable or method `conn'

conn is unknown. Let's try to call meth before:

class MyClass
def meth
conn = 1
end
def result
conn
end
end

x = MyClass.new
x.meth # try to create conn
p x.result #-> test.rb:6:in `result': undefined local variable or method `conn'

Same result. So conn is no instance variable. You define a local variable in meth but it is unknown outside.

Let's try the same with instance variables:

class MyClass
def meth
@conn = 1
end
def result
@conn
end
end

x = MyClass.new
p x.result #-> nil (no value assigned (*), but is is existing)
x.meth # initialze @conn with a value
p x.result #-> 1

With the usage of accessor-methods you define implicit an instance variable:

class MyClass
attr_reader :conn
def meth
conn = 1
end
def result
conn
end
end

x = MyClass.new
p x.result #-> nil (no value assigned (*), but is is existing)
x.meth # define conn with a value
p x.result #-> nil - the instance variable is not changed, a locale variable was used

In method result the conn is the reader method conn. In the method meth it is a locale variable (this can be confusing, because now you have a variable with the same name as a variable.

If you want to change the conn-value in the meth-method you must define a setter and use self.conn:

class MyClass
attr_reader :conn
attr_writer :conn
def meth
self.conn = 1
end
def result
conn
end
end

x = MyClass.new
p x.result #-> nil (not defined yet, but is is existing)
x.meth # define conn with a value
p x.result #-> 1

You can replace attr_reader and attr_writer with attr_accessor.

(*) Remark: I wrote no value assigned - this is not really correct, nil is also a value.

Performance in different variable scopes?

I have converted this to use the STDLIB benchmark and got this result

require 'benchmark'

iterations = 10 ** 7
$global = 0
local = 0
@instance = 0
Benchmark.bmbm(8) do |x|
x.report('global') {iterations.times { $global } }
x.report('local') {iterations.times { local } }
x.report('instance') {iterations.times { @instance } }
end

The results:

Rehearsal --------------------------------------------
global 1.580000 0.010000 1.590000 ( 1.600952)
local 1.540000 0.000000 1.540000 ( 1.555683)
instance 1.600000 0.000000 1.600000 ( 1.642781)
----------------------------------- total: 4.730000sec

user system total real
global 1.560000 0.000000 1.560000 ( 1.575711)
local 1.540000 0.000000 1.540000 ( 1.547040)
instance 1.600000 0.010000 1.610000 ( 1.618772)

Using benchmark-ips gem:

require 'benchmark/ips'

$global = 0
local = 0
@instance = 0

Benchmark.ips do |x|
x.report('global') { $global }
x.report('local') { local }
x.report('instance') { @instance }
x.compare!
end

Gives the following report on my machine at this time, and perhaps gives a more easily read comparison:

Calculating -------------------------------------
global 34.310k i/100ms
local 34.461k i/100ms
instance 34.383k i/100ms
-------------------------------------------------
global 3.154M (± 2.9%) i/s - 15.748M
local 3.205M (± 4.5%) i/s - 15.990M
instance 3.153M (± 3.1%) i/s - 15.747M

Comparison:
local: 3205049.9 i/s
global: 3153595.5 i/s - 1.02x slower
instance: 3152813.3 i/s - 1.02x slower

You had compared only local variable and global variables but neglected instance variables.

There is very negligible difference in any, which is actually a good thing.

I am not sure this answers your question, but hope it helps.

What is the specific difference between block variable and block-local variable in Ruby?

Unfortunately there are no suitable keywords in ruby which might explain it beyond the doubt. So, let me translate it to javascript! (Javascript local variables are practically identical to ruby variables - with an exception for explicit creation keywords)

But before, few notes about scopes in javascript. Even though I'll be writing using JS, all the notes are also correct for ruby - except for a lack of the explicit variable keyword.

So: let keyword - let creates a new variable on the current scope. Once created, given variable can be read only when we are inside the same lexical scope or its child scopes:

let x = 0;

x #=> 0;

function() { # function creates a new child scope
return x; # access variable of the parent scope
}() #=> 0

It is important to understand that the scope is lexical, not dynamic - this means that variables resolves in a static context, which depends on the structure of the code rather than how the code is being called. This creates so-called closures (again - closures also exists in Ruby, however, unlike in JS, it's best to avoid them).

When searching for a variable, we always look at the current scope first and, if we have nothing defined in current scope, move to the parent scope. If no parent scope is found, exception is thrown.

So, let's translate your code into javascript:

let x = 0, y = 0, z = 0        
let ary = [1, 2, 3]

ary.forEach(function(x) { # x is now function argument
let y; # This is because of `|...; y|` in your block

# In this scope we have 3 variables:
# x - is an argument of a function
# y - is locally scoped variable
# z - is undefined in this scope, so it'll reference z from the parent scope
y = x
z = x
console.log(x, y, z)
})

console.log(x, y, z)

Let's analyze. In the above code, there are two scopes - top scope and child scope. There are 4 variables defined in your top scope (x, y, z and ary) and 2 variables defined in the child scope (x, y).

When you do y = x you first read the value of x - since such a variable exists in the current scope (and it is passed as an argument), we take that. In first iteration the value of x is 1, so expression evaluates to:

y = 1

Now we need to find variable y we want to assign to - there is variable y in the current scope so we assign to that one. The y variable in the main scope is not affected by this assignment

Next expression: z = x - x resolves the same way as before, so in first iteration we have:

z = 1

But now, there is no z variable in the current scope, so we look for a variable in the parent scope. In result, this assignment modifies the variable in the main scope

Next, we're printing x, y and z resolved in the current scope - there's no surprise here, they are all 1s.

Second iteration, everything repeats, but this time x resolves to 2. Again, we set the value of a y in a local, child scope and z of the parent scope. And then, third iteration.

In last expression we print the values of the variables, but in the parent scope. x is 0 as we have never done a single assignment to it, similarly y is 0 - because all the assignments were done against the variable defined in the child scope so, in fact, we didn't make a single assignment to it. z on the other hand was assigned to on each iteration. On the last iteration it received value 3 which is what it currently holds.

Ruby: Parameter vs variables

While thinking deep into this I realized one of the very great benefit this provides is flexibility and readability.
So for instance, I could pass in parameters like

order_food(2, "small", 90)

This allows me to override the default values which is better than having to change a variables content while

order_food(9, "extraLarge")

gets the default quantity that I have set

@ variables in Ruby on Rails

title is a local variable. They only exists within its scope (current block)

@title is an instance variable - and is available to all methods within the class.

You can read more here:
http://strugglingwithruby.blogspot.dk/2010/03/variables.html

In Ruby on Rails - declaring your variables in your controller as instance variables (@title) makes them available to your view.



Related Topics



Leave a reply



Submit