When passing a Ruby array as an argument, why does `` append while `+=` does not?
The main difference between the #<<
, and #+
s that #<<
is just the Array
's instance method, so you just add a value to the specified instance of Array
arr = []
arr.__id__ # => 68916130
arr << 10
arr.__id__ # => 68916130
but in form of #+
is used assignment operator, which replace reference to a variable with a new instance, and that new instance shall not be passed into uplevel of the #add_to_array
function.
arr = []
arr.__id__ # => 68916130
arr += [10]
arr.__id__ # => 68725310
NOTE: That +=
implies the #+
method plus assignment operator =
, however ruby interpreter treats it as a specific operator not as a sum.
Additionals
The form arr = arr + [10]
isn't work properly also.
def add_to_array(value, arr)
arr = arr + [value]
end
build_results()
# => []
Array assign vs. append behavior
You redefine local variable in the first method.
This is the same as
visited = []
local_visited = visited
local_visited = ['A']
visited
# => []
And in the second method:
visited = []
local_visited = visited
local_visited << 'A'
visited
# => ["A"]
How do I pass multiple arguments to a ruby method as an array?
Ruby handles multiple arguments well.
Here is a pretty good example.
def table_for(collection, *args)
p collection: collection, args: args
end
table_for("one")
#=> {:collection=>"one", :args=>[]}
table_for("one", "two")
#=> {:collection=>"one", :args=>["two"]}
table_for "one", "two", "three"
#=> {:collection=>"one", :args=>["two", "three"]}
table_for("one", "two", "three")
#=> {:collection=>"one", :args=>["two", "three"]}
table_for("one", ["two", "three"])
#=> {:collection=>"one", :args=>[["two", "three"]]}
(Output cut and pasted from irb)
Yield within Set to eliminate in an Array
In Ruby, when you are putting yield
keyword inside any method(say #bar
), you are explicitly telling #bar
that, you will be using a block with the method #bar
. So yield
knows, inside the method block will be converted to a Proc
object, and yield
have to call that Proc
object.
Example :
def bar
yield
end
p bar { "hello" } # "hello"
p bar # bar': no block given (yield) (LocalJumpError)
In the
uniq_by
method, we did not do anything to handle block argument. How is the passed argument handled byuniq_by
method?
You did do, that is you put yield
. Once you will put this yield
, now method is very smart to know, what it supposed to so. In the line Messages.all.uniq_by { |h| h.body }
you are passing a block { |h| h.body }
, and inside the method definition of uniq_by
, that block has been converted to a Proc
object, and yield
does Proc#call
.
Proof:
def bar
p block_given? # true
yield
end
bar { "hello" } # "hello"
Better for understanding :
class Array
def uniq_by
seen = Set.new
select{ |x| seen.add?( yield( x ) ) }
end
end
is same as
class Array
def uniq_by
seen = Set.new
# Below you are telling uniq_by, you will be using a block with it
# by using `yield`.
select{ |x| var = yield(x); seen.add?(var) }
end
end
Read the doc of yield
Called from inside a method body, yields control to the code block (if any) supplied as part of the method call. If no code block has been supplied, calling
yield
raises an exception.yield
can take an argument; any values thus yielded are bound to the block's parameters. The value of a call toyield
is the value of the executed code block.
In Ruby, why is it not enough to alias `method_missing` to catch call to undefined methods?
Generally speaking, you want method_missing
(that is called by Ruby internally) to be an alias for mistrafe
not vice versa. You have an implementation in mistrafe
and you want reassign method_missing
to be calling it.
That said, the following will work:
alias method_missing mistrafe
See the alias documentation.
Ruby required argument assignment logic: how does the ordering works?
Ruby provides a great deal of flexibility in how a method's arguments are defined. She cannot read your mind, however, so she'll raise an exception if the assignment of arguments to variables is incorrect or ambiguous. The basic rule is that Ruby can work out how the values passed to a method should be assigned to arguments if and only if you can work that out yourself, and conclude that there is only one way it can be done (subject to Rule 4 below). We can formalize this with the first three rules below.
If these rules are satisfied, it should be obvious from the discussion how the values passed to the method are assigned to arguments, though Rule 4, which is not obvious, is also needed.
Rule 1: required arguments must be preceded or followed by zero or more required variables, and nothing else (i.e., they must be stacked at the beginning and/or end of the list of values passed to the method).
Rule 2: If all of a method's arguments are required or have default values, the number of values passed to the method must be between
n
andn+m
, wheren
is the number of required arguments andm
is the number of arguments with defaults.Rule 3: A splatted argument cannot precede a splatted argument or an argument with a default value.
Rule 4: If an argument
a
with a default value is followed by a splatted argument or another argument with a default value, and the above rules are satisfied, the first value passed to the method that has not already been assigned to a preceding argument is assigned toa
.
Assignment of arguments to required variables
Ruby first checks that there is exactly one way that required arguments can be assigned to their associated variables.
Here are some examples where Ruby gives a thumbs-up.
def meth(w,x)
[w,x]
end
meth 1,2
#=> [1,2]
I gave the above for completeness.
def meth(w,x,*y,z)
[w,x,y,z]
end
meth 1,2,3,4,5,6
#=> [1, 2, [3, 4, 5], 6]
meth 1,2,3
#=> [1, 2, [], 3]
w
and z
are the first and last arguments, and x
is preceded by one or more required arguments and nothing else.
def meth(w,x=1,y,z)
[w,x,y,z]
end
meth 1,2,3,4
#=> [1, 2, 3, 4]
meth 1,2,3
#=> [1, 1, 2, 3]
w
and z
are the first and last arguments, and y
is followed by one or more required arguments and nothing else.
Now some examples where Ruby finds the assignment of variables to arguments is ambiguous, and therefore raises an exception (at compile-time).
def meth(*x,y,*z)
[x,y,z]
end
#=> syntax error, unexpected keyword_end, expecting end-of-input
The reason is obvious.
def meth(w,x=1,y,*z)
[w,x,y,z]
end
#=> syntax error, unexpected keyword_end, expecting end-of-input
Ruby doesn't know if x
should equal its default or the second value passed to the method, which affects both y
and *z
.
Note that if the first (last) argument is required, the first (last) value passed to the method is assigned to that argument.
This establishes Rule #1.
When all arguments are required or have default values
def meth(w,x,y=3,z)
[w,x,y,z]
end
meth 1,2,3,4
#=> [1, 2, 3, 4]
meth 1,2,3
#=> [1, 2, 3, 3]
meth 1,2
#=> ArgumentError: wrong number of arguments (given 2, expected 3..4)
meth 1,2,3,4,5
#=> ArgumentError: wrong number of arguments (given 5, expected 3..4)
This establishes Rule #2.
Non-required arguments
If the method passes the test for required arguments, we can remove the associated variables from the argument list and then determine if Ruby would be happy with what's left (since the required arguments are stacked at the beginning and/or end of the argument list).
Two examples:
def meth(*w,*x)
end
#=> syntax error, unexpected *
def meth(*w,x=1)
end
#=> syntax error, unexpected '=', expecting ')'
This establishes Rule #3.
Remaining possibilities
Consider an argument with a default value that precedes either a splatted argument or another argument with a default value.
def meth(w=1,x=2,*y)
[w,x,y]
end
meth
#=> [1, 2, []]
meth 3
#=> [3, 2, []]
meth 3,4
#=> [3, 4, []]
meth 3,4,5
#=> [3, 4, [5]]
meth 3,4,5,6
#=> [3, 4, [5, 6]]
def meth(w=1,x=2)
[w,x]
end
meth
#=> [1, 2]
meth 3
#=> [3, 2]
meth 3,4
#=> [3, 4]
We see that the first values passed to the method will be assigned to the arguments with the default values. If no values are passed to the method both variables will be assigned to their default values.
This gives us Rule 4.
Design decision
Note that the Ruby monks could have decided to raise a compile-time exception for def meth(w=1,*x)
and/or a run-time exception for def meth(w=1,x=2)
when only one value is passed, but chose not to do so.
Common practice
As a matter of common practice, one nearly always sees the required arguments (if any) listed first, followed by arguments with default values (if any), followed by zero or one splatted arguments. There is no loss of functionality by ordering the variables in that way.
Related Topics
Does Ruby Provide a Constant_Added Hook Method
Sublime Text 2 Doesn't Save Built SASS File
Net::Ssh with Non Unix/Linux Host
Rails Form_For Never Invokes the Create Controller Action to Use Redirect_To
How to Strip Commas from Float Input
Watir Won't Download PDF, Only Opens in Viewer
Newbie: Text Replacement in My Case
Connecting to Google Analytics API in a Rails App
Determining Type of an Object in Ruby
Utc Time Resets to 2000-01-01 (Ruby). How to Prevent the Time from Resetting
Why Does Shoveling a String into a Hash Cause This Result
How to Display Error Messages in a Multi-Model Form with Transaction
How to Convert a Large Gem to Standalone Rails App
Rexml::Document.New How to Give Encode Parameters on This Line