Ruby default value for def - ruby

i have ruby method
def get(name1="john", age=31)
puts "#{name1} + #{age}"
end
how to allocate only a variable "age"
For example:
get(:age => 3)
=> john + 3

When Ruby encounters
def m(a=1, b=2, c=3)
puts "a=#{a}, b=#{b}, c=#{3}"
end
This is what happens:
m(4,5)
# a=4, b=5, c=3
Ruby has no way of knowing which variable you want assigned to its default so she employs a simple rule: assign arguments to variables left to right until they are used up, then any remaining arguments are assigned to their default values.
If you want a different priority you could change the order of the arguments, say,
def m(b=2, c=3, a=1)
puts "a=#{a}, b=#{b}, c=#{3}"
end
m(4,5)
# a=1, b=4, c=3
This may suffice in some situations but a more flexible solution, which also reads better, is to use named arguments (aka named paramters), as #steenstag has done in his solution. Here that might be as follows.
def m(a: 1, b: 2, c: 3)
d = a + c
puts "a=#{a}, b=#{b}, c=#{c}, d=#{d}"
end
m(:b=>4, :c=>5)
# a=1, b=4, c=5, d=6
m(c: 4)
# a=1, b=2, c=4, d=5
Notice that I've written c=>5 in the first instance and c: 4 in the second. Either form can be used. We cannot, however, write def m(:a=>1, :b=>2, :c=>3).
Since Ruby v2.1 we can also have required named arguments:
def m(a:, b: 2, c: 3)
puts "a=#{a}, b=#{b}, c=#{c}"
end
m(c:5, a: 4)
# a=4, b=2, c=5
m(c: 5)
# ArgumentError (missing keyword: a)
We can also have some named and some unnamed arguments, provided the named arguments are at the end, but its less confusing to simply name all arguments if any are named.
def m(d, a:, b: 2, c: 3)
puts "d=#{d}, a=#{a}, b=#{b}, c=#{c}"
end
m(6, c:5, a: 4)
# d=6, a=4, b=2, c=5
def m(a:, b: 2, c: 3, d)
end
#=> SyntaxError ((irb):165: syntax error, unexpected tIDENTIFIER)
# ... def m(a:, b: 2, c: 3, d)
# ^

You're very close:
def get(name1: "john", age: 31)
puts "#{name1} + #{age}"
end
get(:age => 3) # => john + 3

Related

How do I call a ruby function named []?

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

Ruby programmatically calling method, with variable number of args

I am trying to do something similar to this:
def foo(mode= :serial)
if (mode == :serial) then
self.send(:bar, "one_type")
else
self.send(:bar,"second_type",:T5)
end
end
I can obviously type this out like this.
But I've recently tried expanding it to include a second function like this:
def foo(mode= :serial)
if (mode == :serial) then
self.send(:bar, "one_type")
self.send(:foobar, "one_type",20)
else
self.send(:bar,"second_type",:T5)
self.send(:foobar, "one_type",20,:T5)
end
end
I can still continue as it is, but I thought to myself, there's a pattern here, I should abstract the arguments away into another layer and make this simpler.
So what I wanted to do was something like this:
arg_arr = [:bar, "one_type"]
arg_arr_2 = [:foobar, "one_type", 20]
if (mode == :serial) then
self.send(arg_arr)
self.send(arg_arr_2)
else
arg_arr << :T5
arg_arr2 << :T5
self.send(arg_arr)
self.send(arg_arr2 )
end
I tried some other ideas involving .each, .inspect, but nothing that would work (the usual error was can't convert array into string, which I'm guessing refers to the fact that it treats the array as the entire function name). I can do it if I explicitly say "use array elements[0] , [1] etc, but that just seems wasteful.
Is there a way to achieve this without writing code that is hardcoded to the number of arguments?
Try this
def foo(a, b)
puts a
puts b
end
array = ['bar', 'qux']
send(:foo, *array) # using send
foo(*array) # using the method name
Both print
bar
qux
The splat operator * packs or unpacks an array.
Some years ago I did what you are trying now. With an asterisk in front of a method parameter you can receive as many parameters as you want in a function. So You don't need to know the number of the given parameters. It's called a splat.
Send your values as an array with an asterisk in front too and it will work.
I tested the folling with an irb console:
def test(*args)
puts args.inspect
end
my_args = [1, 2, 3]
self.send(:test, *my_args)
# [1, 2, 3]
# => nil
Or send as many single parameters as you want:
self.send(:test, 'a', 'b', 'c', 'd')
# ["a", "b", "c", "d"]
# => nil
If you have a fixed number of parameters this will work:
def test(arg1, arg2, arg3)
puts arg1.inspect
puts arg2.inspect
puts arg3.inspect
end
my_args = [1, 2, 3]
self.send(:test, *my_args)
# 1
# 2
# 3
# => nil
First, you shouldn't use send. You could use public_send, Method#call or just bar(...) if you know the method name.
Homogenous parameters
If the parameters are homogenous (e.g. are instances of the same Class), you can just put them in an Array, and use this Array as parameter :
def analyze_array(array)
puts "Elements : #{array}"
puts "Length : #{array.size}"
puts "Sum : #{array.inject(:+)}"
puts
end
analyze_array([1,2,3])
analyze_array([1,2,3,4,5])
It outputs :
Elements : [1, 2, 3]
Length : 3
Sum : 6
Elements : [1, 2, 3, 4, 5]
Length : 5
Sum : 15
Example
Refactoring your code a bit, it could become :
arg_arr = [:bar, 1]
arg_arr_2 = [:foobar, 1, 2]
def bar(array)
puts " bar with one parameter : #{array}"
end
def foobar(array)
puts " foobar with one parameter : #{array}"
end
[:serial, :parallel].each do |mode|
puts "Mode : #{mode}"
[arg_arr, arg_arr_2].each do |method_and_args|
method_name, *args = method_and_args
args << 3 if mode != :serial
method(method_name).call(args)
end
end
It outputs :
Mode : serial
bar with one parameter : [1]
foobar with one parameter : [1, 2]
Mode : parallel
bar with one parameter : [1, 3]
foobar with one parameter : [1, 2, 3]
Heterogenous parameters
For an unknown number of parameters that might belong to different classes, you can use the splat operator (documentation) :
def analyze_parameters(*params)
puts "Parameters : #{params}"
puts "Number : #{params.size}"
puts "Classes : #{params.map(&:class)}"
end
analyze_parameters('Test')
analyze_parameters(1, 'a', :b, [:c, :d])
It outputs :
Parameters : ["Test"]
Number : 1
Classes : [String]
Parameters : [1, "a", :b, [:c, :d]]
Number : 4
Classes : [Fixnum, String, Symbol, Array]
Your example becomes :
arg_arr = [:bar, 1 ]
arg_arr_2 = [:foobar, 1, 'a']
def bar(*params)
puts " bar with multiple parameters : #{params}"
end
def foobar(*params)
puts " foobar with multiple parameters : #{params}"
end
[:serial, :parallel].each do |mode|
puts "Mode : #{mode}"
[arg_arr, arg_arr_2].each do |method_and_args|
method_name, *args = method_and_args
args << :b if mode != :serial
method(method_name).call(*args)
end
end
It outputs :
Mode : serial
bar with multiple parameters : [1]
foobar with multiple parameters : [1, "a"]
Mode : parallel
bar with multiple parameters : [1, :b]
foobar with multiple parameters : [1, "a", :b]

How do I make a function that accepts an anonymous function using 'do' keyword syntax?

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.

Ruby : Rubeque - difficult with *n or n

I'm doing http://www.rubeque.com/problems/queue-continuum/solutions/51a26923ba804b00020000df and I spent a while there. I can't realize why this code doesn't pass
def initialize(queue)
#q = queue
end
def pop(n=1)
#q.shift(n)
end
def push(arr)
arr.each { |x|
#q.push(x)
}
return true
end
def to_a
#q
end
but this works perfectly.
def initialize(queue)
#q = queue
end
def pop(*n)
#q.shift(*n)
end
def push(arr)
#q.push(*arr)
return true
end
def to_a
#q
end
i'm totally confused about
def pop(*n)
#q.shift(*n)
end
and
def push(arr)
#q.push(*arr)
end
why should I take (arr) as array and than change it into... *arr which is Array of array? I'm confused, please help!
The splat works in two ways.
When receiving arguments, it combines arguments into an array.
def foo *args; args end
foo(1) # => [1]
foo(1, 2, 3) # => [1, 2, 3]
When giving arguments, it decomposes an array into arguments.
def bar x, y, z; y end
bar(*[1, 2, 3]) # => 2
def baz x; x end
baz(1) # => [1]
baz(1, 2, 3) # => Error
The *arr you are wondering is the latter case. It is not an object like [1, 2, 3] (hence, not an array of arrays). It is a part of arguments (like 1, 2, 3) passed to a method.
There are other uses of splats (as in array literals, case statements, etc.), but their function is either of the two uses above.

ruby variable as same object (pointers?)

>> a = 5
=> 5
>> b = a
=> 5
>> b = 4
=> 4
>> a
=> 5
how can I set 'b' to actually be 'a' so that in the example, the variable a will become four as well. thanks.
class Ref
def initialize val
#val = val
end
attr_accessor :val
def to_s
#val.to_s
end
end
a = Ref.new(4)
b = a
puts a #=> 4
puts b #=> 4
a.val = 5
puts a #=> 5
puts b #=> 5
When you do b = a, b points to the same object as a (they have the same object_id).
When you do a = some_other_thing, a will point to another object, while b remains unchanged.
For Fixnum, nil, true and false, you cannot change the value without changing the object_id. However, you can change other objects (strings, arrays, hashes, etc.) without changing object_id, since you don't use the assignment (=).
Example with strings:
a = 'abcd'
b = a
puts a #=> abcd
puts b #=> abcd
a.upcase! # changing a
puts a #=> ABCD
puts b #=> ABCD
a = a.downcase # assigning a
puts a #=> abcd
puts b #=> ABCD
Example with arrays:
a = [1]
b = a
p a #=> [1]
p b #=> [1]
a << 2 # changing a
p a #=> [1, 2]
p b #=> [1, 2]
a += [3] # assigning a
p a #=> [1, 2, 3]
p b #=> [1, 2]
You can't. Variables hold references to values, not references to other variables.
Here's what your example code is doing:
a = 5 # Assign the value 5 to the variable named "a".
b = a # Assign the value in the variable "a" (5) to the variable "b".
b = 4 # Assign the value 4 to the variable named "b".
a # Retrieve the value stored in the variable named "a" (5).
See this article for a more in-depth discussion of the topic: pass by reference or pass by value.
As has been noted the syntax you are using can not be done. Just throwing this out there though you could make a wrapper class it depends what you actually want to do
ruby-1.8.7-p334 :007 > class Wrapper
ruby-1.8.7-p334 :008?> attr_accessor :number
ruby-1.8.7-p334 :009?> def initialize(number)
ruby-1.8.7-p334 :010?> #number = number
ruby-1.8.7-p334 :011?> end
ruby-1.8.7-p334 :012?> end
=> nil
ruby-1.8.7-p334 :013 > a = Wrapper.new(4)
=> #<Wrapper:0x100336db8 #number=4>
ruby-1.8.7-p334 :014 > b = a
=> #<Wrapper:0x100336db8 #number=4>
ruby-1.8.7-p334 :015 > a.number = 6
=> 6
ruby-1.8.7-p334 :016 > a
=> #<Wrapper:0x100336db8 #number=6>
ruby-1.8.7-p334 :017 > b
=> #<Wrapper:0x100336db8 #number=6>
You can use arrays:
a = [5]
b = a
b[0] = 4
puts a[0] #=> 4
This idea is based on this answer.
Just for the sake of reference.
>> a = 5
=> 5
>> a.object_id
=> 11
>> b = a
=> 5
>> b.object_id
=> 11
>> b = 4
=> 4
>> b.object_id
=> 9
>> a.object_id
=> 11
# We did change the Fixnum b Object.
>> Fixnum.superclass
=> Integer
>> Integer.superclass
=> Numeric
>> Numeric.superclass
=> Object
>> Object.superclass
=> BasicObject
>> BasicObject.superclass
=> nil
I hope this gives us all a little better understanding about objects in Ruby.
One option in cases where you feel you would like to have direct pointer operations is to use the replace method of Hashes, Arrays & Strings.
this is useful for when you would like to have a method return a variable that a proc the method sets up will change at a later date, and don't want the annoyance of using a wrapper object.
example:
def hash_that_will_change_later
params = {}
some_resource.on_change do
params.replace {i: 'got changed'}
end
params
end
a = hash_that_will_change_later
=> {}
some_resource.trigger_change!
a
{i: 'got changed'}
It's probably better generally to use explicit object wrappers for such cases, but this pattern is useful for building specs/tests of asynchronous stuff.
I'm no Ruby expert. But for a technically crazy kluge...that would only work if you felt like going through eval every time you worked with a variable:
>> a = 5
=> 5
>> b = :a
=> :a
>> eval "#{b} = 4"
=> 4
>> eval "#{a}"
=> 4
>> eval "#{b}"
=> 4
Note that a direct usage of b will still give you :a and you can't use it in expressions that aren't in eval:
>> b
=> :a
>> b + 1
NoMethodError: undefined method `+' for :a:Symbol
...and there are certainly a ton of caveats. Such as that you'd have to capture the binding and pass it around in more complex scenarios...
'pass parameter by reference' in Ruby?
#Paul.s has an answer for if you can change the point of declaration to be a wrapper object, but if you can only control the point of reference then here's a BasicReference class I tried:
class BasicReference
def initialize(r,b)
#r = r
#b = b
#val = eval "#{#r}", #b
end
def val=(rhs)
#val = eval "#{#r} = #{rhs}", #b
end
def val
#val
end
end
a = 5
puts "Before basic reference"
puts " the value of a is #{a}"
b = BasicReference.new(:a, binding)
b.val = 4
puts "After b.val = 4"
puts " the value of a is #{a}"
puts " the value of b.val is #{b.val}"
This outputs:
Before basic reference
the value of a is 5
After b.val = 4
the value of a is 4
the value of b.val is 4

Resources