Given the method:
def foo(a,b=5,c=1)
return a+(b*c)
end
Running foo(1) should return 6. However, how would you go about doing something like this: foo(1,DEFAULT,2). I need to change the third value, but use the default second value.
How would you do this? (Note: I can't just change the order of the variables because they are arguments for a method from a gem)
You can't do it, in the terms posed. This sort of situation is exactly why named (keyword) parameters were introduced in Ruby 2. But your parameters with default values, according to the terms of the question, are not named.
Therefore, they are positional — that is why the optional parameters must come last — and the rule is, accordingly, that this method must be called with at least one argument (because a is not optional), and any further arguments will be used in the order supplied to fill in the corresponding parameters.
Thus, you can supply a, or a and b, or a and b and c. You cannot supply a and c alone, as you could easily do if these parameters were named.
Two obvious solutions spring to mind.
Call the method, supplying the default value of the second parameter as the second argument. Presumably you know what it is, so it's not much of a hardship:
foo(1,5,2)
Write a trampoline method that does the same thing, but where the parameters are named:
def foo(a,b=5,c=1)
return a+(b*c)
end
def bar(a,b:5,c:1)
return foo(a,b,c)
end
bar(1,c:2) # => 11
One solution is to default to nil and then default in the body of the code:
def foo(a,b=nil,c=nil)
b ||= 5
c ||= 1
a + (b * c)
end
Or you can do that as part of the computation:
def foo(a,b=nil,c=nil)
a + (b || 5) * (c || 1)
end
Related
I'm practicing a Ruby program for 2D coordinate operations. Among them, the writing of def +(other) and def -(other) confuses me. I have the following three questions:
Why is the method name equal to the operator name?
Where are the parameters received? Passed from where?
Why is the parameter other called other, and what is its value and
transmission process?
This is code
class Point
attr_accessor :x, :y
def initialize(x=0, y=0)
#x, #y = x, y
end
def inspect # p印出(x, y)
"(#{x}, #{y})"
end
def +(other)
self.class.new(x + other.x, y + other.y)
end
def -(other)
self.class.new(x - other.x, y - other.y)
end
end
point0 = Point.new(3, 6)
point1 = Point.new(1, 8)
p point0
p point1
p point0 + point1
p point0 - point1
it will eventually print
(3, 6)
(1, 8)
(4, 14)
(2, -2)
Why is the method name equal to the operator name?
Why not? Why should the symbol used to define the operator not be the symbol used to call it? That's how we do it for every other method, after all: if I want to call foo, I define def foo.
The Language designers could have chosen any arbitrary name. But it just makes sense to have the name of the method be the same symbol that you use to call it. Can you imagine the confusion if the language designers had chosen, for example, the symbol - for the name of the method which corresponds to the + operator?
Other programming languages make different choices. For example, in C++, the name of the function corresponding to the + operator is operator+. In Python, the name of the method corresponding to the + operator is __add__. (More precisely, when encountering the expression a + b, Python will first try calling a.__add__(b) and if that is not implemented by a, then it will try the "reverse add" b.__radd__(a).)
In C#, there is no method name which corresponds to the operator, rather, there is an operator keyword followed by the symbol corresponding to the operator.
If you want to know all the nitty-gritty details about how operator expressions are evaluated in Ruby, I recommend checking out Section 11.4.3 Unary operator expressions and Section 11.4.4 Binary operator expressions of the ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification.
Where are the parameters received? Passed from where?
It's not quite clear what you mean by this. A parameter is kind of like a "hole" or a placeholder in the method definition. This "hole" then gets filled in with an argument when you actually call the method. For example, here:
def foo(a, b) a + b end
a and b are parameters (more precisely, mandatory positional parameters, they are mandatory because you have to pass an argument for them and they are positional because which argument gets bound to which parameter in the parameter list depends on the position of the argument in the argument list). You don't actually know what the result of this method will be because you don't know what a and b are. They are placeholders for values that will be filled in when you call the method:
foo(2, 3)
Here, 2 and 3 are arguments (more precisely, positional arguments). The argument 2 gets bound to the parameter a because 2 is the first positional argument in the argument list and a is the first positional parameter in the parameter list. The argument 3 gets bound to the parameter b because 3 is the second positional argument in the argument list and b is the second positional parameter in the parameter list.
So, the code that gets ultimately executed for this particular method call is (replacing a with 2 and b with 3):
2 + 3
Please note: this is a simplified explanation. The mental model of replacing every occurrence of the parameter in the method definition body with the argument expression is a good first approximation, but it is not actually what Ruby does. In particular, that mental model I just described corresponds to the call-by-name evaluation strategy, whereas Ruby actually uses a special case of the call-by-value evaluation strategy called call-by-object-sharing.
You can observe the difference in this code:
def bar(a) a + a end
bar((puts "Hello"; 23))
# Hello
#=> 46
In the "replace every occurrence of the parameter with the argument expression" mental model which corresponds to call-by-name, the code would look like this:
(puts "Hello"; 23) + (puts "Hello"; 23)
# Hello
# Hello
#=> 46
and Hello would be printed twice.
However, with call-by-value and call-by-object-sharing, the argument expression gets evaluated before calling the method and the result of that evaluation gets passed in, so the actual code execution looks more like this:
__fresh_variable_with_unspeakable_name__ = (puts "Hello"; 23)
# Hello
__fresh_variable_with_unspeakable_name__ + __fresh_variable_with_unspeakable_name__
#=> 46
If you want to know all the nitty-gritty details about how method arguments are evaluated in Ruby, I recommend checking out Section 11.3 Method invocation expressions, in particular Section 11.3.1 General description and Section 11.3.2 Method arguments of the ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification.
There is also some (unfortunately incomplete) information to be found in The Ruby Spec Suite aka ruby/spec, in particular in language/def_spec.rb and language/method_spec.rb.
Why is the parameter other called other, and what is its value and
transmission process?
The parameter other is called other because that is what the author of that piece of code chose to call it. They could have called it a or b or x or y or foo or bar or i_dont_have_the_slightest_idea_what_to_call_this_parameter_so_i_am_just_choosing_something_totally_random. If you want to know why the author of that piece of code chose to call it other, you would have to ask the author of that piece of code.
other is a somewhat popular name for the "other" operand of a binary operator definition, not just in Ruby, as you can see for example in the Python documentation as well. It does make sense if you read it out loud: in an Object-Oriented Programming Language like Ruby or Python, where operators are interpreted as being sent to one of the operands, one of the two operands of a binary operator will always be self or this and the "other" operand will be … well … the "other" operand. So, naming it other is just natural. In Ruby, this is codified in some Style Guides, for example the Rubocop Ruby Style Guide.
Consider the following:
(1..10).inject{|memo, n| memo + n}
Question:
How does n know that it is supposed to store all the values from 1..10? I'm confused how Ruby is able to understand that n can automatically be associated with (1..10) right away, and memo is just memo.
I know Ruby code blocks aren't the same as the C or Java code blocks--Ruby code blocks work a bit differently. I'm confused as to how variables that are in between the upright pipes '|' will automatically be assigned to parts of an object. For example:
hash1 = {"a" => 111, "b" => 222}
hash2 = {"b" => 333, "c" => 444}
hash1.merge(hash2) {|key, old, new| old}
How do '|key, old, new|' automatically assign themselves in such a way such that when I type 'old' in the code block, it is automatically aware that 'old' refers to the older hash value? I never assigned 'old' to anything, just declared it. Can someone explain how this works?
The parameters for the block are determined by the method definition. The definition for reduce/inject is overloaded (docs) and defined in C, but if you wanted to define it, you could do it like so (note, this doesn't cover all the overloaded cases for the actual reduce definition):
module Enumerable
def my_reduce(memo=nil, &blk)
# if a starting memo is not given, it defaults to the first element
# in the list and that element is skipped for iteration
elements = memo ? self : self[1..-1]
memo ||= self[0]
elements.each { |element| memo = blk.call(memo, element) }
memo
end
end
This method definition determines what values to use for memo and element and calls the blk variable (a block passed to the method) with them in a specific order.
Note, however, that blocks are not like regular methods, because they don't check the number of arguments. For example: (note, this example shows the usage of yield which is another way to pass a block parameter)
def foo
yield 1
end
# The b and c variables here will be nil
foo { |a, b, c| [a,b,c].compact.sum } # => 1
You can also use deconstruction to define variables at the time you run the block, for example if you wanted to reduce over a hash you could do something like this:
# this just copies the hash
{a: 1}.reduce({}) { |memo, (key, val)| memo[key] = val; memo }
How this works is, calling reduce on a hash implicitly calls to_a, which converts it to a list of tuples (e.g. {a: 1}.to_a = [[:a, 1]]). reduce passes each tuple as the second argument to the block. In the place where the block is called, the tuple is deconstructed into separate key and value variables.
A code block is just a function with no name. Like any other function, it can be called multiple times with different arguments. If you have a method
def add(a, b)
a + b
end
How does add know that sometimes a is 5 and sometimes a is 7?
Enumerable#inject simply calls the function once for each element, passing the element as an argument.
It looks a bit like this:
module Enumerable
def inject(memo)
each do |el|
memo = yield memo, el
end
memo
end
end
And memo is just memo
what do you mean, "just memo"? memo and n take whatever values inject passes. And it is implemented to pass accumulator/memo as first argument and current collection element as second argument.
How do '|key, old, new|' automatically assign themselves
They don't "assign themselves". merge assigns them. Or rather, passes those values (key, old value, new value) in that order as block parameters.
If you instead write
hash1.merge(hash2) {|foo, bar, baz| bar}
It'll still work exactly as before. Parameter names mean nothing [here]. It's actual values that matter.
Just to simplify some of the other good answers here:
If you are struggling understanding blocks, an easy way to think of them is as a primitive and temporary method that you are creating and executing in place, and the values between the pipe characters |memo| is simply the argument signature.
There is no special special concept behind the arguments, they are simply there for the method you are invoking to pass a variable to, like calling any other method with an argument. Similar to a method, the arguments are "local" variables within the scope of the block (there are some nuances to this depending on the syntax you use to call the block, but I digress, that is another matter).
The method you pass the block to simply invokes this "temporary method" and passes the arguments to it that it is designed to do. Just like calling a method normally, with some slight differences, such as there are no "required" arguments. If you do not define any arguments to receive, it will happily just not pass them instead of raising an ArgumentError. Likewise, if you define too many arguments for the block to receive, they will simply be nil within the block, no errors for not being defined.
I have code as follows:
def sum(a, b)
a + b
end
puts sum.call 2, 3
I get an error like:
wrong number of arguments (given 0, expected 2) (ArgumentError)
How can I call a function?
EDIT
I want to have a function able to call other one with certain arguments. I've written the code like below but the same error is still displayed.
def sum(a, b)
a + b
end
def kall(func, *args)
send(func, *args)
end
puts kall(sum, 2, 3)
In order to invoke the function sum, just delete the .call call:
def sum(a, b)
a + b
end
sum(1, 2)
# => 3
Other way to call the method is doing:
send(:sum, 1, 2)
Which invokes the method sum on the current context/object with the list of arguments (1, 2).
One more way to call a method is:
method(:sum).call(2, 3)
#=> 5
sum is not a function, it is a method. Methods belong to objects, they aren't objects. Ruby is an object-oriented language, which means you can only store objects in variables, only pass objects as arguments (with the slightly odd exception of blocks), only return objects from methods and only send messages to objects.
You cannot send a message to the sum method, because you can only send messages to objects, and methods aren't objects.
And even if it were possible to send messages to methods, there would still be an ambiguity in your code: Ruby allows you to leave out the argument list to a message send if you don't pass any arguments, therefore
sum
is a valid message send and is (somewhat) equivalent (modulo privacy) to
self.sum()
So, even if it were possible to send messages to methods, Ruby would still think that you try to send the sum message without an argument list.
So, since the problem is that we need an object, there are two things we can do. Firstly, we can use an object to begin with.
You used the term "function" in your question. Well, Ruby doesn't have functions, but it has something close to it: Procs. One solution would be to use a Proc instead of a method:
sum = -> (a, b) { a + b }
sum.(2, 3)
#=> 5
The other solution would be to obtain an object for the method. Ruby's Reflection System has a class called Method which responds to call, instances of which are reflective proxies for methods. You can obtain a Method object by sending the Object#method message to an object, e.g.:
sum = method(:sum)
sum.(2, 3)
#=> 5
You don't need to do .call or anything like that. If the function has no parameters, simply just type the name of the function. And if the function does have parameters, just do myFunction(param1, param2).
def hello
puts 'Hello, World!'
end
def output(string)
puts string
end
# Both of these do the same thing:
hello
hello()
# Doing that with stdout won't work though. It expects one argument, string
output('You can do it with parenthesis')
output 'You can also do it without'
[1,2,3,4,5,6,7].delete_if{|i| i < 4 }
For example, in the above, why do you need to put |i| before i < 4?
I'm new to Ruby programming and the purpose of this element escapes me.
This is very basic Ruby syntax for a block. A block can sometimes take parameters which are given between the bars |. In your case:
[1,2,3,4,5,6,7].delete_if { |i| i < 4 }
The delete_if method for the type Array accepts a block as a parameter. When the bock is given, the block accepts the array element as a parameter. So it iterates i over each value within the array in this case. More specifically, an element will be deleted from the array if that element is < 4. The result will be:
[4,5,6,7]
You'll often see documentation for methods for Ruby types which say, for example:
delete_if { |item| block } -> Array
Which means that the method accepts a block with a parameter, the block being some code that uses the parameter, and the output being another array. The method's description explains more detail in the documentation (e.g., Ruby Array).
I recommend reading some Ruby getting started information online or a good introductory book which will explain this in more detail.
You have to put i there for the same reason you would put i in the first line here:
def plus_one(i)
return i + 1
end
You have to name your method argument, which you later use as a local variable in the method.
Ruby blocks are similar to methods, they can also receive arguments, and syntax for declaring them is slightly different: enclosing them in | |.
I've redone my answer, even though the OP's question has already been answered, because I thought of a new way to explain this that may help future SO users with the same question.
From high school algebra, you should remember functions like this: f(x) = x + 1.
Imagine putting curly braces around the x + 1: f(x) = { x + 1 }
Then move the (x) to inside the curly braces: f = {(x) x + 1 }
And then get rid of the name f: {(x) x + 1 }. This makes it an "anonymous function," i.e. a "lambda."
Here's the problem: The braces could contain arbitrary statements, which may themselves use parentheses: (x + 1) * 4. So how would Ruby know that the (x) is supposed to be an argument to the function, and not an expression to execute? Some other syntax had to be used. Hence the vertical bars: |x|. (At least I assume that was the thought process).
So {|i| i > 4 } is just like f(i) = i > 4, except that it has no name and is not defined in advance, so the parameter has to be defined "inside" the function itself, rather than being outside attached to the name.
Array#delete_if expects such a function (called a "block" when it's used like this) and knows what to do with it. It passes each member of the array [1,2,3,4,5,6,7] into the block as the argument i to see whether i > 4 is true for that member. It's equivalent to doing something like this:
def greater_than_four(x)
x > 4
end
arr = [1,2,3,4,5,6,7]
arr.each do |el|
arr.delete(el) if greater_than_four(el)
end
You could avoid defining the greater_than_four method in advance by defining a lambda on the fly like this:
arr = [1,2,3,4,5,6,7]
arr.each do |el|
arr.delete(el) if lambda{|i| i > 4}.call(el)
end
But since Array#delete_if already expects a block, and already knows to call it on each element, you can save yourself a whole lot of code:
[1,2,3,4,5,6,7].delete_if{|i| i < 4 }
The parameter which you are passing to the delete_if method is a block and the thing inside the parameter you pass to the block.
Think of the block as a method of sorts. The delete_if method iterates over the block and passes the current item as the parameter i to the block. If the condition evaluates to true then that element gets deleted.
It is said that when we have a class Point and knows how to perform point * 3 like the following:
class Point
def initialize(x,y)
#x, #y = x, y
end
def *(c)
Point.new(#x * c, #y * c)
end
end
point = Point.new(1,2)
p point
p point * 3
Output:
#<Point:0x336094 #x=1, #y=2>
#<Point:0x335fa4 #x=3, #y=6>
but then,
3 * point
is not understood:
Point can't be coerced into Fixnum (TypeError)
So we need to further define an instance method coerce:
class Point
def coerce(something)
[self, something]
end
end
p 3 * point
Output:
#<Point:0x3c45a88 #x=3, #y=6>
So it is said that 3 * point is the same as 3.*(point). That is, the instance method * takes an argument point and invoke on the object 3.
Now, since this method * doesn't know how to multiply a point, so
point.coerce(3)
will be called, and get back an array:
[point, 3]
and then * is once again applied to it, is that true?
Now, this is understood and we now have a new Point object, as performed by the instance method * of the Point class.
The question is:
Who invokes point.coerce(3)? Is it Ruby automatically, or is it some code inside of * method of Fixnum by catching an exception? Or is it by case statement that when it doesn't know one of the known types, then call coerce?
Does coerce always need to return an array of 2 elements? Can it be no array? Or can it be an array of 3 elements?
And is the rule that, the original operator (or method) * will then be invoked on element 0, with the argument of element 1? (Element 0 and element 1 are the two elements in that array returned by coerce.) Who does it? Is it done by Ruby or is it done by code in Fixnum? If it is done by code in Fixnum, then it is a "convention" that everybody follows when doing a coercion?
So could it be the code in * of Fixnum doing something like this:
class Fixnum
def *(something)
if (something.is_a? ...)
else if ... # other type / class
else if ... # other type / class
else
# it is not a type / class I know
array = something.coerce(self)
return array[0].*(array[1]) # or just return array[0] * array[1]
end
end
end
So it is really hard to add something to Fixnum's instance method coerce? It already has a lot of code in it and we can't just add a few lines to enhance it (but will we ever want to?)
The coerce in the Point class is quite generic and it works with * or + because they are transitive. What if it is not transitive, such as if we define Point minus Fixnum to be:
point = Point.new(100,100)
point - 20 #=> (80,80)
20 - point #=> (-80,-80)
Short answer: check out how Matrix is doing it.
The idea is that coerce returns [equivalent_something, equivalent_self], where equivalent_something is an object basically equivalent to something but that knows how to do operations on your Point class. In the Matrix lib, we construct a Matrix::Scalar from any Numeric object, and that class knows how to perform operations on Matrix and Vector.
To address your points:
Yes, it is Ruby directly (check calls to rb_num_coerce_bin in the source), although your own types should do too if you want your code to be extensible by others. For example if your Point#* is passed an argument it doesn't recognize, you would ask that argument to coerce itself to a Point by calling arg.coerce(self).
Yes, it has to be an Array of 2 elements, such that b_equiv, a_equiv = a.coerce(b)
Yes. Ruby does it for builtin types, and you should too on your own custom types if you want to be extensible:
def *(arg)
if (arg is not recognized)
self_equiv, arg_equiv = arg.coerce(self)
self_equiv * arg_equiv
end
end
The idea is that you shouldn't modify Fixnum#*. If it doesn't know what to do, for example because the argument is a Point, then it will ask you by calling Point#coerce.
Transitivity (or actually commutativity) is not necessary, because the operator is always called in the right order. It's only the call to coerce which temporarily reverts the received and the argument. There is no builtin mechanism that insures commutativity of operators like +, ==, etc...
If someone can come up with a terse, precise and clear description to improve the official documentation, leave a comment!
I find myself often writing code along this pattern when dealing with commutativity:
class Foo
def initiate(some_state)
#...
end
def /(n)
# code that handles Foo/n
end
def *(n)
# code that handles Foo * n
end
def coerce(n)
[ReverseFoo.new(some_state),n]
end
end
class ReverseFoo < Foo
def /(n)
# code that handles n/Foo
end
# * commutes, and can be inherited from Foo
end