Using this example:
arr = [1, 2, 3]
for elem in arr do
puts elem
end
puts elem # => 3
The code outputs:
1
2
3
3
elem contains the value even outside the loop. Why? What is it's scope outside the loop?
Can anyone clarify please?
That is expected. According to the documentation:
The for loop is similar to using each, but does not create a new variable scope.
Example with for:
for i in 1..3
end
i #=> 3
Example with each:
(1..3).each do |i|
end
i #=> NameError: undefined local variable or method `i'
If I'm not mistaken, methods (each, map, loop, upto) create variables scopes, whereas keywords (for, while, until) don't.
The for statement defines the variable elem and initializes it with the value of the current loop.
To avoid this use Array#each:
arr.each do |elem|
puts elem
end
# 1
# 2
# 3
# => [1, 2, 3]
elem
NameError: undefined local variable or method `elem' for main:Object
from (irb):5
from /usr/bin/irb:12:in `<main>'
Now the elem variable exists in the block only.
You can declare your variable elem outside the loop scope. So, if we modify your example:
arr = [1, 2, 3];
elem;
for elem in arr do
puts elem
end
puts elem # => 3
Related
I am new to Ruby, so please excuse this question if it is obvious.
I am working with a Module with a function signature that I don't understand. How would I call this function?
module Facter
...
def self.[](name)
collection.fact(name)
end
...
In my code I want to reference something that should be in collection.fact, in this Facter module. What syntax to I use to call this function?
Cheers
It works like this:
class MyModule
def self.[](arg)
puts arg
end
end
MyModule["Hello world"] # will print Hello world
Please see official docs:
https://ruby-doc.org/core/doc/syntax/methods_rdoc.html
Additionally, methods for element reference and assignment may be defined: [] and []= respectively. Both can take one or more arguments, and element reference can take none.
class C
def [](a, b)
puts a + b
end
def []=(a, b, c)
puts a * b + c
end
end
obj = C.new
obj[2, 3] # prints "5"
obj[2, 3] = 4 # prints "10"
So about example from docs
# From docs
obj[2, 3]
# It's the same as
obj.[](2, 3)
More interesting example
# From docs
obj[2, 3] = 4
# will print 10
# => 4
# It's the almost as
obj.[]=(2, 3, 4)
# will print 10
# => nil
As you see when you call as obj[2, 3] = 4 Ruby takes the value after = as the last argument of the []= method and return it as method result
And regardless of whether there is return in the method body. For example
class C
def []=(a, b, c)
puts "Before return"
return 12
puts "After return"
end
end
obj = C.new
obj[2, 3] = 4
# will print Before return
# => 4
obj.[]=(2, 3, 4)
# will print Before return
# => 12
It is desirable to define such method with more than one parameter. Technically, you can have only one, but the call will be like this obj[] = 1
How can I use a variable's value at the point of Proc definition instead of defining the Proc with a reference to the variable? Or how else would I approach the problem of defining a list of different steps to be executed in sequence based on an input sequence?
Example:
arr = []
results = [1,2,3]
for res in results
arr << Proc.new { |_| res }
end
p arr[0].call(42)
p arr[1].call(3.14)
Expected output:
1
2
Actual output:
3
3
Why Your Code Doesn't Work as Expected: Shared Closure Scope
By definition, a Proc is a closure that retains its original scope but defers execution until called. Your non-idiomatic code obscures several subtle bugs, including the fact that the for-in control expression doesn't create a scope gate that provides the right context for your closures. All three of your Proc objects share the same scope, where the final assignment to the res variable is 3. As a result of their shared scope, you are correctly getting the same return value when calling any of the Procs stored in your array.
Fixing Your Closures
You can make your code work with some minor changes. For example:
arr = []
results = [1,2,3]
results.map do |res|
arr << Proc.new { |_| res }
end
p arr[0].call(42) #=> 1
p arr[1].call(3.14) #=> 2
Potential Refactorings
A More Idiomatic Approach
In addition to creating a proper scope gate, a more idiomatic refactoring might look like this:
results = [1, 2, 3]
arr = []
results.map { |i| arr << proc { i } }
arr.map { |proc_obj| proc_obj.call }
#=> [1, 2, 3]
Additional Refinements
A further refactoring could simplify the example code even further, especially if you don't need to store your inputs in an intermediate or explanatory variable like results. Consider:
array = [1, 2, 3].map { |i| proc { i } }
array.map &:call
#=> [1, 2, 3]
Validating the Refactoring
Because a Proc doesn't care about arity, this general approach also works when Proc#call is passed arbitrary arguments:
[42, 3.14, "a", nil].map { |v| arr[0].call(v) }
#=> [1, 1, 1, 1]
[42, 3.14, "a", nil].map { |v| arr[1].call(v) }
#=> [2, 2, 2, 2]
The problem is that the proc object use the context inside the loop the following should work
def proc_from_collection(collection)
procs = []
collection.each { |item| procs << Proc.new { |_| item } }
procs
end
results = [1,2,3]
arr = proc_from_collection(results)
p arr[0].call # -> 1
p arr[1].call # -> 2
After reading Todd A. Jacobs answer I felt like I was missing something.
Reading some post on stackoverflow about the for loop in ruby made me realize that we do not need a method here.
We can iterate the array using a method that does not pollute the global environment with unnecessary variables like the for loop does.
I suggest using a method whenever you need a proper closure that behaves according to a Lexical Scope (The body of a function is evaluated in the environment where the function is defined, not the environment
where the function is called.).
My first answer is still a good first approach but as pointed by Todd A. Jacobs a 'better' way to iterate the array could be enough in this case
arr = []
results = [1,2,3]
results.each { |item| arr << Proc.new { |_| item } }
p arr[0].call # -> 1
p arr[1].call # -> 2
I need help understanding inheritance.
class MyArray < Array
end
a = MyArray[1, 2, 3] #=> [1, 2, 3]
b = MyArray[4, 5] #=> [4, 5]
c = a + b #=> [1, 2, 3, 4, 5]
a.class #=> MyArray
b.class #=> MyArray
c.class #=> Array
I don't understand why the result of the addition is not an instance of MyArray class.
I don't understand why my array "a" is not "MyArray" class after add.
Why should it (be a MyArray)? Concatenation operation on arrays is defined to return a new Array, so that's what happens here. https://ruby-doc.org/core-2.5.3/Array.html#method-i-2B
If you want, you can override that operation in your class to return an instance of MyArray. Don't forget about all other similar methods.
This is also why it's a bad idea to subclass standard collections. Better to use composition over inheritance here.
Just to add a bit to Sergio's answer in terms of his comment on using composition over inheritance and the exchange in the comments.
Instead of saying MyArray is an array you can say MyArrayLike has and array.
Then you can "forward" methods that make sense to the underlying array but still add your own functionality that makes sense for your class without sub-classing array.
Ruby even has several ways to make this very easy including the Forwardable module.
class MyArrayLike
attr_reader :arr
def initialize( initial_arr )
#arr = initial_arr
end
def +(other)
result = self.class.new(arr + other.arr)
# maybe you want to do more than just concat the underlying array, if so you can do it here
result
end
def first
# for example maybe you want first to just return the first item in the underlying array.
arr.first
end
end
a = MyArrayLike.new([1,2,3])
b = MyArrayLike.new([4,5])
puts "a.class = #{a.class}"
# => a.class = MyArrayLike
puts a
# => #<MyArrayLike:0x00000000dc4b00>
a += b
puts "a.class = #{a.class}"
# => a.class = MyArrayLike
puts a
# => #<MyArrayLike:0x00000000dc4470>
puts a.first
# => 1
puts a.arr
# => 1
# 2
# 3
# 4
# 5
Adding MyArray to MyArray to get Array may be counterintuitive, but a method can be defined to return any class. And in the case of Array#+, which you are calling, it just happens to be defined to return an Array. That's it.
If you want it to return a MyArray, one way to do it is to define MyArray#+ as follows:
class MyArray < Array
def +other
MyArray.new(super)
end
end
(MyArray.new([1, 2, 3]) + MyArray.new([4, 5])).class # => MyArray
By the way, notice that your MyArray#initialize definition is meaningless, and hence redundant.
I just started studying Ruby a little while ago and I was having difficulties with global versus local variable scoping.
Working on a practice problem, I found that an array defined globally was being changed by a function called on it. If I explicitly assign the array to something else, nothing changes. But if I run through and delete items one by one, this deletes them from the global array itself.
Why do delete and pop (which I also tested) methods have this behavior? I understood from reading that this should not be happening, that the "array" inside the functions is a reference to the values of arr, rather than the variable arr.
(I'm using Ruby version 2+)
def change_int x
x += 2
end
def change_arr array
array = [4, 5, 6]
end
def pop_arr array
puts array
new_array = []
while array.length > 0
new_array.push array[0]
array.delete_at 0
end
array
end
x = 5
change_int x
puts x == 5 # true
arr = [1, 2, 3]
change_arr arr
puts arr == [1, 2, 3] # true
old_arr = arr
puts pop_arr arr
puts arr == [1, 2, 3] # false
puts "arr = #{arr}" # arr = []
You can see by printing #object_id before calling pop_arr and inside pop_arr that those arrays are the same objects. This means that arguments are passed into the function by reference in Ruby.
Here is code:
def pop_arr(array)
puts array.object_id
# Rest of the fucntion
end
arr = [1, 2, 3]
puts arr.object_id
pop_arr(arr)
All of this means that when you edit array inside the function it will have effect on the object which was passed. #delete, #delete_at, #pop are operations that change the Array on which they are made.
See also: Ruby - Parameters by reference or by value? and Is Ruby pass by reference or by value?.
The curious thing is that change_arr doesn't affect the global array, but pop_arr does, in your code.
Here's what's happening: ruby passes references to objects as parameters. So like Bartosz said, you can see that at the top of those methods, the object id matches the one you passed in; they're referencing the same object.
So, in pop_arr, when you call delete_at, you're operating on the same object that you passed in, and the changes persist after the method returns.
In change_arr, the difference is that you're assigning the internal var to a new object. When you pass in the parameter array, the internal variable references the same object you passed in. When you instantiate a new Array object and assign the internal array variable to it, the internal variable is now referencing a different object.
def change_arr array
puts "change id: #{array.object_id}"
array = [4, 5, 6]
puts "change id2: #{array.object_id}"
array
end
That's why the changes don't persist after the method ends. If you wanted the changes to persist, you'd have to say
array = change_arr(array)
Hope that helps.
When using array.each you can specify the function in two forms:
Curly Braces:
a = [1,2,3]
a.each { |x| puts x * x }
Output:
1
4
9
=> [1, 2, 3]
'do' Syntax:
a = [1,2,3]
a.each do |x|
puts (x * x)
end
Output:
1
4
9
=> [1, 2, 3]
Question:
How can I replicate the 'do' syntax style with my own custom function? The closest to the curly brace style I can get is:
What I've tried:
def PutWith2Arg(proc)
puts proc.call(2)
end
PutWith2Arg(Proc.new { |x| x + 100 })
Output:
102
=> nil
The do |foo| … end and { |foo| … } syntaxes are equivalent. These are 'blocks' in Ruby, and any method can get them. To call them you need to either:
def my_method # No need to declare that the method will get a block
yield(42) if block_given? # Pass 42 to the block, if supplied
end
my_method do |n|
puts "#{n} times 2 equals #{n*2}"
end
#=> "42 times 2 equals 84"
my_method{ |n| puts "#{n} times 2 equals #{n*2}" }
#=> "42 times 2 equals 84"
my_method # does nothing because no block was passed
or, for more sophisticated uses:
def my_method( &blk ) # convert the passed block to a Proc named blk
blk.call( 42 ) if blk
end
# Same results when you call my_method, with or without a block
The latter style is useful when you need to pass the block on to another method. If you have a Proc or Lambda referenced by a variable, you can pass it to a method as the block for that method using the & syntax:
def my_method( &blk ) # convert the passed block to a Proc named blk
[1,2,3].each( &blk ) # invoke 'each' using the same block passed to me
end
my_method{ |x| p x=>x**2 }
#=> {1=>1}
#=> {2=>4}
#=> {3=>9}
For more details, this webpage is fairly instructive.