Reputation: 644
So I started learning ruby and I found out that we can have default values in a method, this looked similar to just having instance variables, Is there any importance or benefit this gives apart from reducing the lines of code?
//This
def order_food(price, size="large", quantity = 8)
if quantity == 1
extra = "salad"
else
extra = "Burgers"
end
puts "#{quantity} #{size} #{extra}, coming right up!"
end
//To this
def order_food(price)
size = "large"
quantity = 8
if quantity == 1
extra = "salad"
else
extra = "Burgers"
end
puts "#{quantity} #{size} #{extra}, coming right up!"
end
Upvotes: 1
Views: 287
Reputation: 42192
There are in fact 4 common ways to pass parameters to a function. Your first example is the most common one but you made a bad example I'm afraid. Your quantity is always 8, so the if is superfluous, also the parametere price isn't used so also superfluous This would be the same as the following
def order_food price
"8 large Burgers, coming right up!"
end
But that is not your purpose I presume.
So, it would be something like this First method
def order_food1(size, quantity, price)
extra = quantity == 1 ? :salad : :Burgers
cost = quantity * price
"#{quantity} #{size} #{extra}, coming right up! Thats is #{cost} dollar please"
end
+ fast (see benchmarks)
+ everybody uses it and understands it in one glace
- you have to know the parameters and the order in which they are defined, you need to read the API for less common uses methods
- you must provide all the parameters
Next: using optional parameters with default values
def order_food2(size = "large", quantity = 1, price = 4)
extra = quantity == 1 ? :salad : :Burgers
cost = quantity * price
"#{quantity} #{size} #{extra}, coming right up! Thats is #{cost} dollar please"
end
+ also widely used
+ no need to use parameters if they are the default ones
- you still need to know the order and meaning of the parameters and if the last is used you need them all
Next: using a hash as parameter
def order_food3(opts = {})
opts = {size: :large, quantity: 1, price: 4}.merge!(opts)
extra = opts[:quantity] == 1 ? :salad : :Burgers
cost = opts[:quantity] * opts[:price]
"#{opts[:quantity]} #{opts[:size]} #{extra}, coming right up! Thats is #{cost} dollar please"
end
- less used, your method self is a little harder to read
- slower
+ no need to know parameters you don't need, nor the order
+ the use of the method is more readable
Next: a simplified version of the previous method
def order_food4(size: :large, quantity: 1, price: 4)
extra = :quantity == 1 ? :salad : :Burgers
cost = quantity * price
"#{quantity} #{size} #{extra}, coming right up! Thats is #{cost} dollar please"
end
- slower
+ method itself and use of it are more readable
Which one is better ? Depends on personal taste and the situation. I use all of them and there is nothing in the design guides as far as I know that prefares one or the other. In practice you will even combine some of them. Parameters that are seldom changed are best given a default value and vice versa. Methods that are called many times (eg recursive ones) could benefit from the faster and less memory consuming method 1. In my opinion, readability is most important for a Ruby script, so if having a lot of parameters and feasable use method 3 or 4.
Some examples of use and benchmarks..
puts order_food1("large", 3, 4)
puts order_food2("large", 3, 4)
puts order_food3(size: "large", quantity: 3, price: 4)
puts order_food3
puts order_food4(size: "large", quantity: 3, price: 4)
puts order_food4
# 3 large Burgers, coming right up! Thats is 12 dollar please
# 3 large Burgers, coming right up! Thats is 12 dollar please
# 3 large Burgers, coming right up! Thats is 12 dollar please
# 1 large salad, coming right up! Thats is 4 dollar please
# 3 large Burgers, coming right up! Thats is 12 dollar please
# 1 large Burgers, coming right up! Thats is 4 dollar please
require 'benchmark'
Benchmark.bmbm do |x|
x.report("order_food1 ") { 10000.times { order_food1("large", 3, 12) }}
x.report("order_food2 ") { 10000.times { order_food2("large", 3, 12) }} # all parameters given
x.report("order_food2_def") { 10000.times { order_food2 }} # using default parameters
x.report("order_food3 ") { 10000.times { order_food3(size: "large", quantity: 3, price: 12) }} # all parameters given
x.report("order_food3 def") { 10000.times { order_food3 }} # using default parameters
x.report("order_food4 ") { 10000.times { order_food3(size: "large", quantity: 3, price: 12) }} # all parameters given
x.report("order_food4 def") { 10000.times { order_food3 }} # using default parameters
end
# user system total real
# order_food1 0.015000 0.000000 0.015000 ( 0.010420)
# order_food2 0.000000 0.000000 0.000000 ( 0.010675)
# order_food2_def 0.016000 0.000000 0.016000 ( 0.011007)
# order_food3 0.015000 0.000000 0.015000 ( 0.020182)
# order_food3 def 0.016000 0.000000 0.016000 ( 0.016954)
# order_food4 0.015000 0.000000 0.015000 ( 0.020256)
# order_food4 def 0.000000 0.000000 0.000000 ( 0.016968)
Upvotes: 0
Reputation: 211590
Here's a re-reworked version of your code that's more Ruby-like:
def order_food(price, size: :large, quantity: 1)
extras =
case (quantity)
when 1
"salad"
else
"Burgers"
end
"#{quantity} #{size} #{extra}, coming right up!"
end
puts order_food(2, :small, 8)
Doing display (puts
) inside a method is often giving the method too much responsibility. Split out display concerns from the compositional ones. Maybe you want to write that to a file, or embed it in HTML. puts
inside the method limits your options.
Also take advantage of keyword arguments if you want to have a number of them that are somewhat arbitrary in nature. This allows you to skip one and use the other without having code that has to re-specify defaults.
Upvotes: 1
Reputation: 3610
It's not the same as an instance variable. An instance variable has scope for an instance of a class and is declared using the @
symbol.
For example:
class MyClass
def initialise
@my_instance_variable = 'hello world'
end
def method_one
puts "I have access to the instance variable and it is #{@my_instance_variable}"
end
end
What you have shown are both declarations of local variables for the scope of the method only, however one is defining a parameter to your method and the other isn't.
def order_food(price, size="large", quantity = 8)
is not the equivalent of:
def order_food(price)
size = "large"
quantity = 8
Although size and quantity are both variables and both have scope only for the order_food
method, the first is declaring them as parameters the method can accept, so it can be called like so:
order_food(5, 'small', 2)
Whereas in the second example these cannot be set by the callee - they are fixed at 'large' and 8.
It is not necessary to declare method parameters with defaults but by doing so the callee doesn't need to provide them and the default values would be used instead. So for the method declaration of:
def order_food(price, size="large", quantity = 8)
you could make the following calls:
order_food price: 10, quantity: 2 #will use default size with value 'large'
order_food price: 5, size: 'small' #will use default quantity of 8
Upvotes: 3
Reputation: 1984
First of all, the variables are local variables not instance variables. Instance variables belong to the instance of a class and are notated with the @var_name
notation, while local variables belong to a scope (very simplistically that is anything surrounded by a do ... end
. More detail here) and are notated with just the variable name (my_var = "some_value"
).
It depends on what you are using the method for. If you want to be able to pass the arguments quantity
and size
than you should use the first one. The second one will give you and ArgumentError
if you try to pass more than 1 argument. The first one will set the values of quantity = 8
and size = "large"
if they are not passed, but if they are passed, it will use the passed values.
If you want to be able to call the method and set the size and quantity as arguments and if they are not passed use size = "large"
and quantity = 8
as defaults, use the first method:
order_food "9.00" #=> "8 large burgers, coming right up!"
order_food "9.00", "small", 1 #=> "1 small salad, coming right up!"
The second method will not allow you to pass either of the other two arguments, and they will always be set so quantity = 8
and size = "large"
. This has it's benefits, because sometimes you don't want the variables to be change-able with arguments. So with the second method:
order_food "9.00" #=> "8 large burgers, coming right up!"
order_food "9.00", "small", 1 #=> ArgumentError: wrong number of arguments (given 3, expected 1)
Upvotes: 2
Reputation: 644
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
Upvotes: 4