When there is more than one default value, how could I change only the second initialization variable without also calling the first?
For example, a Ruby class is created to return the value akin to the roll of a single die with default values for a six sided die ranging from 1 to 6:
class Die
def initialize(min=1, max=6)
@min = min
@max = max
end
def roll
rand(@min..@max)
end
end
If I wanted instead to use this code to simulate the return from rolling a 20 sided die, I could write the following:
p Die.new(min=1, max=20).roll
...but is there a way to argue only the second (max) value?
Of note - and this is where I am confused (I don't fully understand Ruby class attributes and variable scopes) - if I invoke:
p Die.new(max=20).roll
... I get nil
printed. ?. (I understand that this is because rand(20..6)
returns nil
, but I thought that max=
would retain the default min value for the first argument - instead max=20
gets ingested as the integer 20
binding to the min=
... This seems weird to me.)
I suppose I could re-work the Die class to take a default value of the number of sides and also set the min (or max) value relative to the number of sides, but this is beside the point of my main question: How to override only the second default value without explicitly writing the first as well...
Presuming that most dice would normally have a minimum value of 1, I realize that I could reverse the order of min and max like so:
class Die2
def initialize(max=6, min=1)
@max = max
@min = min
end
def roll
rand(@min..@max)
end
end
...and then invoke whatever maximum number of sides like so:
p Die2.new(20).roll
...but given the syntax of class Die
(and my inclination to write the minimum before the maximum) is there a way to only enter an argument for the second value? Or, perhaps I am approaching Ruby classes poorly? Any help or guidance is appreciated - thanks!
If you write
class Die
def initialize(min=1, max=6)
@min, @max = min, max
end
end
and create a new instance by passing a single argument, such as:
die = Die.new(3)
#=> #<Die:0x007fcc6902a700 @min=3, @max=6>
we can see from the return value that the argument 3
has been assigned to @min
and @max
gets its default value. In short, to pass a value to @max
you must also pass one to @min
(unless, of course, you reverse the order of the arguments).
You can do what you want by using named arguments (or named parameters), introduced in Ruby v2.0.
class Die
def initialize(min: 1, max: 6)
@min, @max = min, max
end
end
die = Die.new(max: 3)
#=> #<Die:0x007fcc698ccc00 @min=1, @max=3>
(or die = Die.new(:max=>3
). As you see, @min
equals its default value and @max
equals the argument that is passed, 3
.
Default values were required for keyword arguments in Ruby v2.0, but v2.1 extended their functionality to permit required named arguments as well. See, for example, this article.
Lastly, consider the following two cases (the second being the more interesting).
class Die
def initialize(min=1, max: 6)
@min, @max = min, max
end
end
die = Die.new(max: 3)
#=> #<Die:0x007fcc69954448 @min=1, @max=3>
class Die
def initialize(min, max: 6)
@min, @max = min, max
end
end
die = Die.new(max: 3)
#=> #<Die:0x007fa01b900930 @min={:max=>3}, @max=6>