Multiple name parameters with default values in Ruby 2.5 - ruby

I am trying to create a method where I need to pass multiple name parameters with default values but I am getting syntax error, unexpected keyword_next error. How can I rectify it?
Eg method
def action(prev = nil, next = nil)
if prev.present?
# do something
elsif next.present?
# do something
else
# do something else
end
end
How can I make the above code work?

next is a reserved word in Ruby, as it is used to skip one iteration in enumerables. For example in the following code:
my_array = [1, 2, 3, 4]
my_array.each do |number|
next if number == 2
puts number
end
which will oputput:
1
3
4
This means that you can not use it as a variable/parameter name. To fix your code, you just need to rename the variable. For example:
def action(prev = nil, following = nil)
if prev.present?
# do something
elsif following.present?
# do something
else
# do something else
end
end

Related

Codility error: Invalid result type, Integer expected, NilClass found

EDIT: SOLVED BY STEFAN
BUT: Now, the only questions left are: Why has the shorter solution such a poor performance (results: 100%, performance: 32%, result: 66%) , while the longer version performs a bit better but seems to produce worse results (60%, 50%, 55%)?
Start of original question:
I'm currently trying the Codility demo test and the problem to solve is to find the lowest integer above 0 that's not included in a given array.
This is my code in two different versions with the same result. The output is right but the compiler throws the abovementioned error, resulting in the test failing. This seems to be a common error on Codility, when looking up this error here on SO.
# you can write to stdout for debugging purposes, e.g.
# puts "this is a debug message"
def solution(a)
# write your code in Ruby 2.2
num = 1
a=a.sort
a.uniq!
a.each do |x|
if x == num then
num += 1
next
else
break
end
end
puts num
end
or
def solution(a)
# write your code in Ruby 2.2
num = 1
while a.include?(num) do
num += 1
end
puts num
end
results in:
Compilation successful.
Example test: [1, 3, 6, 4, 1, 2]
Output (stderr):
Invalid result type, Integer expected, NilClass found
Output:
5
RUNTIME ERROR (tested program terminated with exit code 1)
Example test: [1, 2, 3]
Output (stderr):
Invalid result type, Integer expected, NilClass found
Output:
4
RUNTIME ERROR (tested program terminated with exit code 1)
Example test: [-1, -3]
Output (stderr):
Invalid result type, Integer expected, NilClass found
Output:
1
RUNTIME ERROR (tested program terminated with exit code 1)
Producing output might cause your solution to fail performance tests.
You should remove code that produces output before you submit your solution.
Detected some errors.
I really don't understand what's wrong. The array only contains integers, num is an integer, everything is an integer but the compiler says it's NIL. What can I do?
EDIT: The same code runs without errors in the SoloLearn app and on my local machine.
The output is right but the compiler throws the abovementioned error, resulting in the test failing
Although puts generates output it has a return value of nil:
puts 123
# 123 # <- output
#=> nil # <- return value
I assume that your method is supposed to return the value instead just printing it to standard out.
You can fix this by removing puts in your method's last line:
def solution(a)
num = 1
while a.include?(num)
num += 1
end
num # <- without "puts"
end
To generate debug output you could add puts num on a separate line before the return value, e.g.:
def solution(a)
# ...
puts num # <- prints num
num # <- returns num
end
or you could use p which outputs the object's inspect value and returns the object:
def solution(a)
# ...
p num # <- prints num.inspect and returns num
end
Regarding the performance: try to understand what the code has to do in order to get the result. The "short" solution increments num and checks whether it is included in the array. But an inclusion check has to traverse the array (at least up to the matching element). So for each increment of num, you are traversing the array from the beginning.
You can speed this up significantly by utilizing Set for the lookup:
require 'set'
def solution(a)
set = Set.new(a)
num = 1
num += 1 while set.include?(num)
num
end
def solution(a)
range = (1..a.max).to_a
(range - a).first
end
If you are printing within the code just remove that lines
for Ex in Javascript remove used console.log

Case conditional in ruby doesn't enter in when

My code when executed it's not entering in the when loop inside the case conditional.
What I wanted is to sen two different GET requests based on the *args of the function.
So I can validate the errors when I don't send one of the parameters in the request.
If someone have a better logic to do it one method, I appreaciate as well.
Here is my code:
def get_function(access_token,order1,order2,*args)
case args
when args = "order1"
self.class.get("/v1/apiendpoint?order2=#{order2}",
headers: {'accesstoken': "#{access_token}"})
when args = "order2"
self.class.get("/v1/apiendpoint?order1=#{order1}",
headers: {'accesstoken': "#{access_token}"})
end
end
When I execute with binding.pry (debugging) it shows this part, and doesn't execute the rest of the code.
From: C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/cucumber-core-8.0.1/lib/cucumber/core/test/action.rb # line 25 Cucumber::Core::Test::Action#execute:
22: def execute(*args)
23: #timer.start
24: #block.call(*args)
==> 25: passed
26: rescue Result::Raisable => exception
27: exception.with_duration(#timer.duration)
28: rescue Exception => exception
29: failed(exception)
30: end
There are multiple problems here:
case args
when args = "order1"
Firstly, args is an Array - so it cannot possibly be equal to a String. I'm not sure what you intended to happen here, so can't say exactly how to fix it.
Secondly, = is an assignment operator, whereas == performs an equality check.
And lastly, this is a case statement, not an if statement, so you shouldn't actually be performing an equality check here... Either of these would have made sense syntactically:
case args
when "order1"
# ...
end
# OR:
case
when args == "order1"
# ...
end
Also, note that your question description is a bit confusing. You said:
the when loop
but this is not a loop. You could call it a "clause", or a "statement", but it's certainly not a "loop".
Following Tom help, decided to go with IF statement.
Here is what worked:
def get_function(access_token,order1,order2,*args)
if args == ["order1"]
self.class.get("/v1/apiendpoint?order2=#{order2}",
headers: {'accesstoken': "#{access_token}"})
else
self.class.get("/v1/apiendpoint?order1=#{order1}",
headers: {'accesstoken': "#{access_token}"})
end
args is an Array of arguments, so comparing it to a String will always evaluate to false, no matter the String.
I don't know exactly what you need in terms of the behaviour of the function, but what I can say is that if you want to look inside the args Array to compare every argument to a String, it might be a better idea to iterate over the array.
Example with if:
def args_example(*args)
# Working directly with *args doesn't work, so we assign it to arguments
arguments = *args
# We need a way to save the output after the if clause
output = []
# Let's iterate!
arguments.each do |argument|
# This is where the if would come in
if argument == "One"
output << 1
elsif argument == "Two"
output << 2
else
output << 0
end
end
output
end
args_example("One", "Two", "Three")
=> [1, 2, 0]
Example with case:
def args_example(*args)
# Working directly with *args doesn't work, so we assign it to arguments
arguments = *args
# We need a way to save the output after the case clause
output = []
# Let's iterate!
arguments.each do |argument|
# This is where the case would come in
case argument
when "One"
output << 1
when "Two"
output << 2
else
output << 0
end
end
output
end
args_example("One", "Two", "Three")
=> [1, 2, 0]
This is one way to check all the arguments provided to the function (there's surely a shorter way) and send a GET request accordingly.
Cheers! 🇧🇷
NOTE: I saved the output to be able to display it, but I realised that since you're only performing a GET request, you don't need to do that. Simply execute the request instead of saving the output.

Is there a nicer way to call the current method recursively, without using its name?

For example:
def recurse(value)
if value < 5
self.send(__method__, value + 1)
else
value
end
end
This works, but it's a bit ugly.
Basically I'm looking for a prettier way to call the currently executing method, without referring to it explicitly by name.
If there is a less-cryptic syntax for this, I would probably use it (to avoid the name duplication, reduce effort required for renaming a function, etc). If there isn't a nicer syntax for this, I'll just hard-code the name like normal.
It's a comment rather, as #sagarpandya82 mentioned, you can omit some redundant parts and use both variants. I would refactor it a bit:
def recurse(value)
return value unless value < 5 # return value if value >= 5
send(__method__, value + 1) # or just recurse(value + 1)
end
Non-recursion version with a block:
def non_recurse(value)
if value >= 5
yield value
else
(value..5).each do |i|
yield i
end
end
end
non_recurse(3) {|i| puts i}
#=> 3, 4, 5
non_recurse(6) {|i| puts i}
#=> 6
If you really want to use __method__, your method is correct and reasonably readable. To comply with usual Ruby guidelines, you could just remove returns and use 2 spaces as indent (as mentioned by #sagarpandya82 in the comments):
def recurse(value)
if value < 5
self.send(__method__, value + 1)
else
value
end
end
I don't see any reason to use self.send(__method__) here, so you could write :
def recurse(value)
if value < 5
recurse(value + 1)
else
value
end
end
Actually, I'd say that you don't need recursion at all. All your method does is to keep adding 1 to the value until it reaches 5. If the value is bigger than 5, it returns the value :
For integers:
def no_recurse(value)
[value, 5].max
end
no_recurse(4)
# 5
no_recurse(-3)
# 5
no_recurse(7)
# 7
no_recurse(-2**1000)
# 5
no_recurse(4.5)
# 5 # <- That's wrong
For floats, you'd just need to add the decimal part to 5. This will work for any number:
def no_recurse(value)
[value, 5 + value % 1].max
end
no_recurse(4.5)
# 5.5
no_recurse(5.5)
# 5.5
no_recurse(6)
# 6
no_recurse(-7)
# 5

Get line number of beginning and end of Ruby method given a ruby file

How can I find the line of the beginning and end of a Ruby method given a ruby file?
Say for example:
1 class Home
2 def initialize(color)
3 #color = color
4 end
5 end
Given the file home.rb and the method name initialize I would like to receive (2,4) which are the beginning and end lines.
Finding the end is tricky. The best way I can think of is to use the parser gem. Basically you'll parse the Ruby code into an AST, then recursively traverse its nodes until you find a node with type :def whose first child is :initialize:
require "parser/current"
def recursive_find(node, &block)
return node if block.call(node)
return nil unless node.respond_to?(:children) && !node.children.empty?
node.children.each do |child_node|
found = recursive_find(child_node, &block)
return found if found
end
nil
end
src = <<END
class Home
def initialize(color)
#color = color
end
end
END
ast = Parser::CurrentRuby.parse(src)
found = recursive_find(ast) do |node|
node.respond_to?(:type) && node.type == :def && node.children[0] == :initialize
end
puts "Start: #{found.loc.first_line}"
puts "End: #{found.loc.last_line}"
# => Start: 2
# End: 4
P.S. I would have recommended the Ripper module from the standard library, but as far as I can tell there's no way to get the end line out of it.
Ruby has a source_location method which gives you the file and the beginning line:
class Home
def initialize(color)
#color = color
end
end
p Home.new(1).method(:initialize).source_location
# => ["test2.rb", 2]
To find the end, perhaps look for the next def or EOF.
Ruby source is nothing but a text file. You can use linux commands to find the method line number
grep -nrw 'def initialize' home.rb | grep -oE '[0-9]+'
I have assumed that the file contains the definition of at most one initialize method (though generalizing the method to search for others would not be difficult) and that the definition of that method contains no syntax errors. The latter assumption is probably required for any method to extract the correct line range.
The only tricky part is finding the line containing end that is the last line of the definition of the initialize method. I've used Kernel#eval to locate that line. Naturally caution must be exercised whenever that method is to be executed, though here eval is merely attempting to compile (not execute) a method.
Code
def get_start_end_offsets(fname)
start = nil
str = ''
File.foreach(fname).with_index do |line, i|
if start.nil?
next unless line.lstrip.start_with?('def initialize')
start = i
str << line.lstrip.insert(4,'_')
else
str << line
if line.strip == "end"
begin
rv = eval(str)
rescue SyntaxError
nil
end
return [start, i] unless rv.nil?
end
end
end
nil
end
Example
Suppose we are searching a file created as follows1.
str = <<-_
class C
def self.feline
"cat"
end
def initialize(arr)
#row_sums = arr.map do |row|
row.reduce do |t,x|
t+x
end
end
end
def speak(sound)
puts sound
end
end
_
FName = 'temp'
File.write(FName, str)
#=> 203
We first search for the line that begins (after stripping leading spaces) "def initialize". That is the line at index 4. The end that completes the definition of that method is at index 10. We therefore expect the method to return [4, 10].
Let's see if that's what we get.
p get_start_end_offsets(FName)
#=> [4, 10]
Explanation
The variable start equals the index of the line beginning def initialize (after removing leading whitespace). start is initially nil and remains nil until the "def initialize" line is found. start is then set to the index of that line.
We now look for a line line such that line.strip #=> "end". This may or may not be the end that terminates the method. To determine if it is we eval a string that contains all lines from the one that begins def initialize to the line equal to end just found. If eval raises a SyntaxError exception that end does not terminate the method. That exception is rescued and nil is returned. eval will return :_initialize (which is truthy) if that end terminates the method. In that case the method returns [start, i], where i is the index of that line. nil is returned if no initialize method is found in the file.
I've converted "initialize" to "_initialize" to suppress the warning (eval):1: warning: redefining Object#initialize may cause infinite loop)
See both answers to this SO question to understand why SyntaxError is being rescued.
Compare indentation
If it is known that "def initialize..." is always indented the same amount as the line "end" that terminates the method definition (and no other lines "end" between the two are indented the same), we can use that fact to obtain the beginning and ending lines. There are many ways to do that; I will use Ruby's somewhat obscure flip-flop operator. This approach will tolerate syntax errors.
def get_start_end_offsets(fname)
indent = -1
lines = File.foreach(fname).with_index.select do |line, i|
cond1 = line.lstrip.start_with?('def initialize')
indent = line.size - line.lstrip.size if cond1
cond2 = line.strip == "end" && line.size - line.lstrip.size == indent
cond1 .. cond2 ? true : false
end
return nil if lines.nil?
lines.map(&:last).minmax
end
get_start_end_offsets(FName)
#=> [4, 10]
1 The file need not contain only code.

Use of yield and return in Ruby

Can anyone help me to figure out the the use of yield and return in Ruby. I'm a Ruby beginner, so simple examples are highly appreciated.
Thank you in advance!
The return statement works the same way that it works on other similar programming languages, it just returns from the method it is used on.
You can skip the call to return, since all methods in ruby always return the last statement. So you might find method like this:
def method
"hey there"
end
That's actually the same as doing something like:
def method
return "hey there"
end
The yield on the other hand, excecutes the block given as a parameter to the method. So you can have a method like this:
def method
puts "do somthing..."
yield
end
And then use it like this:
method do
puts "doing something"
end
The result of that, would be printing on screen the following 2 lines:
"do somthing..."
"doing something"
Hope that clears it up a bit. For more info on blocks, you can check out this link.
yield is used to call the block associated with the method. You do this by placing the block (basically just code in curly braces) after the method and its parameters, like so:
[1, 2, 3].each {|elem| puts elem}
return exits from the current method, and uses its "argument" as the return value, like so:
def hello
return :hello if some_test
puts "If it some_test returns false, then this message will be printed."
end
But note that you don't have to use the return keyword in any methods; Ruby will return the last statement evaluated if it encounters no returns. Thus these two are equivelent:
def explicit_return
# ...
return true
end
def implicit_return
# ...
true
end
Here's an example for yield:
# A simple iterator that operates on an array
def each_in(ary)
i = 0
until i >= ary.size
# Calls the block associated with this method and sends the arguments as block parameters.
# Automatically raises LocalJumpError if there is no block, so to make it safe, you can use block_given?
yield(ary[i])
i += 1
end
end
# Reverses an array
result = [] # This block is "tied" to the method
# | | |
# v v v
each_in([:duck, :duck, :duck, :GOOSE]) {|elem| result.insert(0, elem)}
result # => [:GOOSE, :duck, :duck, :duck]
And an example for return, which I will use to implement a method to see if a number is happy:
class Numeric
# Not the real meat of the program
def sum_of_squares
(to_s.split("").collect {|s| s.to_i ** 2}).inject(0) {|sum, i| sum + i}
end
def happy?(cache=[])
# If the number reaches 1, then it is happy.
return true if self == 1
# Can't be happy because we're starting to loop
return false if cache.include?(self)
# Ask the next number if it's happy, with self added to the list of seen numbers
# You don't actually need the return (it works without it); I just add it for symmetry
return sum_of_squares.happy?(cache << self)
end
end
24.happy? # => false
19.happy? # => true
2.happy? # => false
1.happy? # => true
# ... and so on ...
Hope this helps! :)
def cool
return yield
end
p cool {"yes!"}
The yield keyword instructs Ruby to execute the code in the block. In this example, the block returns the string "yes!". An explicit return statement was used in the cool() method, but this could have been implicit as well.

Resources