bad value in range argument error - regarding range construction in ruby - ruby

I wrote the following snippet.
def add_me(num)
result = 0
(1..num).each { |i| result += i}
result
end
puts add_me(STDIN.gets)
I received an argument error list_sum.rb:6:in 'AddMe': bad value for range (ArgumentError) the line # corresponds to line # in my editor.
I also experimented with things like foo = (1..num).to_a. But still receive the same error. What is going on? Ruby version 2.3.3. What am I missing? I should be able to use variables in ranges, no?

gets returns a string. You need to do gets.to_i, in order to turn the input into a number for your numeric range. Right now you’re trying to make a range where the start is the number 1 and the end is some string, and that is raising an ArgumentError.
Also as an aside, ruby convention would tell you that your function should be named add_me. Ruby uses snake case, and anything that starts with a capital letter is typically assumed to be a class or constant (constant being all caps).

Related

How to test for stdout that is inside a .each iteration block, using rspec?

I have a sample code:
def print_stuff(first_num, second_num)
puts 'hello'
(first_num..second_num).to_a.each do |num|
puts 'The current number is: '
puts "#{num}"
end
end
I and using rspec, I would like to test to see if the output is correct or not. My attempt are as follows:
expect(initialize_program(1, 3)).to output(
"The current number is:
1
The current number is:
2
The current number is:
3").to_stdout
But instead, I get a expected block to output to stdout but not a block error since the initialize_program(1,3) is outputting the texts, but it is done inside a .each block thus the method itself returns the array of range of numbers.
How can I test for the texts inside the block, to see if the outputted texts are correct?
Thanks!
Todd's answer is fine and I'd strongly recommend you read it carefully: refactor your app in a way that UI (CLI in your case) is minimal and easy to test. But when you want full coverage you'd need to test that std output eventually.
The way you're using it now:
expect(initialize_program(1, 3)).to output("whatever").to_stdout
Means that initialize_program(1, 3) is evaluated immediately when the matcher is called, and it's too soon - it has to do it's magic(*) first, and then run your code. Try like this:
expect { initialize_program(1, 3) }.to output("whatever").to_stdout
Now, instead passing results of calling initialize_program(1, 3) into the matcher, you pass a block that "knows how" to call initialize_program(1, 3). So what the matcher does:
saves the block for later
does it magic to capture whatever goes to the STDOUT (see below)
calls the block, calling the initialize_program(1, 3), capturing whatever was supposed to go to STDOUT
compares it with what you've set up in your expectation (the output("whatever") part)
https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher
The mentioned magic is not that magical anyway:
https://github.com/rspec/rspec-expectations/blob/44d90f46a2654ffeab3ba65eff243039232802ce/lib/rspec/matchers/built_in/output.rb#L49
and
https://github.com/rspec/rspec-expectations/blob/44d90f46a2654ffeab3ba65eff243039232802ce/lib/rspec/matchers/built_in/output.rb#L141
It just creates StringIO, and replaces global var $stdout with it.
Refactor to Inspect a String, Not Standard Output
This type of code is why you should write your tests first. You're essentially testing Kernel#puts, which always returns nil, rather than validating that you've constructed the String you expect. Don't do that. Instead, refactor like so:
def print_stuff(num1, num2)
str =
(num1..num2).map { |num|"The current number is: #{num}" }
.join "\n"
puts str
str
end
print_stuff 1, 3
#=> "The current number is: 1\nThe current number is: 2\nThe current number is: 3"
This will not only print what you expect on standard output:
The current number is: 1
The current number is: 2
The current number is: 3
but will also use the implicit return of the last line of your method to return a value that you can use to compare to the expectations in your spec.
You might also refactor the method to return an Array of String objects, or whatever else you might explicitly want to test. The more your real method reflects what you plan to test, the better.
RSpec Examples
RSpec.describe '#print_stuff' do
it 'prints the expected message' do
expected_string = <<~EOF
The current number is: 1
The current number is: 2
The current number is: 3
EOF
expect(print_stuff 1, 3).to eql(expected_string.chomp)
end
# Even without the collection matchers removed in RSpec 3,
# you can still validate the number of items returned.
it 'returns the expected number of lines' do
lines = print_stuff(1, 3).split("\n").count
expect(lines).to eql(3)
end
end
Testing RSpec Examples in IRB
In irb, you can validate your specs like so:
require 'rspec'
include RSpec::Matchers
expected_string = <<~EOF
The current number is: 1
The current number is: 2
The current number is: 3
EOF
# String#chomp is needed to strip the newline from the
# here-document
expect(print_stuff 1, 3).to eql(expected_string.chomp)
# test the returned object in other ways, if you want
lines = print_stuff(1, 3).split("\n").count
expect(lines).to eql(3)

Ruby to_s conversion to binary argument error

I was trying to solve a HackerRank problem which requires binary manipulation. The test cases are extremely huge numbers so I thought better to manipulate them as strings.
t=gets
def winner(pturn)
if a%2==0
puts "Richard\n"
else
puts "Louise\n"
end
end
while t != 0
turn=1
n=gets
(n2=n).to_s(2)
while n!=1
one="1"
zero="0"
if n2.count(1)>1
zero*=(n2.length - 2)
one.concat(zero)
n-=one.to_i(base=2)
else
n/=2
end
turn+=1
end
winner(turn)
t-=1
end
It caused an argument error (wrong number of argument) in line as seen below.
(n2=n).to_s(2)
I think I'm using to_s wrongly. I can't see the mistake and need someone to point it out.
I think you are trying to convert a number in Ruby to a string with it's binary representation. This is possible with (if the number is actually a number in Ruby, e.g. Fixnum) :
4.to_s 2
# => "100"
But in your case, what you are getting after calling gets is a string, and the to_s method of the String class simply returns itself, and doesn't take any arguments, hence the error.
You can fix it by calling gets.to_i instead of just gets, so the read string of contents will be converted to an integer in Ruby (you should be sure that you will only be reading numbers at there).
And I believe you are trying to assign the binary representation (as a string) of n into the variable n2, for that you should be doing
n2 = n.to_s(2)
If you just do :
(n2=n).to_s
Because of the parenthesis, first n2 will be assigned the value of n, then to_s will be called on n2, the string version of n2 will be returned, and nothing else happens. First thing you should be doing is the conversion, then the assignment.
And also you should pass in a string while calling String#count, i.e. you should call n2.count('2') instead of n2.count(2).
Argument error is coming because you are trying to pass parameter to to_s method because to_s method never accept any arguments.
So you should change (n2=n).to_s(2) to
(n2=n).to_s

Ruby Anagram Comparison Module

I am new to Ruby and trying to figure out a problem for an online test. The problem is as follows:
An anagram is a word formed from another by rearranging its letters,
using all the original letters exactly once; for example, orchestra
can be rearranged into carthorse.
Write a function that checks if two words are each other's anagrams.
For example, AreAnagrams.are_anagrams?('momdad', 'dadmom') should
return true as arguments are anagrams.
The code I have come up with is this:
module AreAnagrams
def self.are_anagrams?(string_a, string_b)
## Create #word1 variable to store string_a
#word1 = string_a
## Create #word1_compare variable to store string_a in lower case
#word1_compare = #word1.downcase
## Create #word2_compare variable to store string_b
#word2 = string_b
## Create #word2_compare variable to store string_b in lower case
#word2_compare = #word2.downcase
## Check length of #word1 and #word2 to make sure they are the same length
if #word1.length == #word2.length
=begin
Sort the letters of the #word1 and #word2 variables and compare
them to see if they are anagrams
=end
if #word1_compare.chars.sort.join == #word2_compare.chars.sort.join
puts "#{#word1} and #{#word2} are anagrams!"
else
puts "#{#word1} and #{#word2} are not anagrams!"
end
else
puts "#{#word1} and #{#word2} are not anagrams!"
end
end
end
When I submit the code, I get the following back from the test:
Compilation OK, but 4 out of 4 test cases fail
Sample case: Wrong answer
Words with unique letters: Wrong answer
Words with duplicate letters: Wrong answer
One word is the prefix of another word: Wrong answer
I have tested my code with multiple strings and it seems to work just fine. It looks like it wants me to check for more specific instances (special characters, words with duplicate letters, etc.) but is that really required? Sorry if it's a dumb question, I'm new to Ruby and lost.
Any help would be greatly appreciated!
I think the problem here is you're displaying a message but not returning a true or false value which is what is expected.
After each puts, include the appropriate answer. That way your method will return something useful. Right now I'm presuming it's nil for all cases, since that's what puts returns.

repl.it Ruby interpreter : error when defining multiple functions containing loops

I'm relatively new to programming and even newer to Ruby, and I've been using the repl.it Ruby interpreter to test code. However, I've run into the same problem multiple times now whenever I try to enter in multiple function definitions that contain loops -- I inevitably get an error message that looks like this:
(eval):350: (eval):350: compile error (SyntaxError)
(eval):344: syntax error, unexpected kDO_COND, expecting kEND
(eval):350: syntax error, unexpected kEND, expecting $end
Does anyone know what the problem is and how to avoid this? It doesn't look like a code error per se, since my code seems to run fine with codepad. But I've been told to use this specific interpreter to test code for a program I'm applying to.
Here's my code (I'm testing two different methods I wrote to reverse a string, one in place and the other using a new output list):
def reverse(s)
#start by breaking the string into words
words = s.split
#initialize an output list
reversed = []
# make a loop that executes until there are no more words to reverse
until words.empty?
reversed << words.pop.reverse
end
# return a string of the reversed words joined by spaces
return reversed = reversed.join(' ')
end
def reverse_string(s)
# create an array of words and get the length of that array
words = s.split
count = words.count #note - must be .length for codepad's older Ruby version
#For each word, pop it from the end and insert the reversed version at the beginning
count.times do
reverse_word = words.pop.reverse
words.unshift(reverse_word)
end
#flip the resulting word list and convert it to a string
return words.reverse.join(' ')
end
a = "This is an example string"
puts reverse(a)
puts reverse_string(a)
Your code is fine; their interpreter is old. If you change the block syntax that you use with times, e.g.
count.times {
reverse_word = words.pop.reverse
words.unshift(reverse_word)
}
...suddenly it works.

Ruby forgets local variables during a while loop?

I'm processing a record-based text file: so I'm looking for a starting string which constitutes the start of a record: there is no end-of-record marker, so I use the start of the next record to delimit the last record.
So I have built a simple program to do this, but I see something which suprises me: it looks like Ruby is forgetting local variables exist - or have I found a programming error ? [although I don't think I have : if I define the variable 'message' before my loop I don't see the error].
Here's a simplified example with example input data and error message in comments:
flag=false
# message=nil # this is will prevent the issue.
while line=gets do
if line =~/hello/ then
if flag==true then
puts "#{message}"
end
message=StringIO.new(line);
puts message
flag=true
else
message << line
end
end
# Input File example:
# hello this is a record
# this is also part of the same record
# hello this is a new record
# this is still record 2
# hello this is record 3 etc etc
#
# Error when running: [nb, first iteration is fine]
# <StringIO:0x2e845ac>
# hello
# test.rb:5: undefined local variable or method `message' for main:Object (NameError)
#
From the Ruby Programming Language:
(source: google.com)
Blocks and Variable Scope
Blocks define a new variable scope: variables created within a block exist only within that block and are undefined outside of the block. Be cautious, however; the local variables in a method are available to any blocks within that method. So if a block assigns a value to a variable that is already defined outside of the block, this does not create a new block-local variable but instead assigns a new value to the already-existing variable. Sometimes, this is exactly the behavior we want:
total = 0
data.each {|x| total += x } # Sum the elements of the data array
puts total # Print out that sum
Sometimes, however, we do not want to alter variables in the enclosing scope, but we do so inadvertently. This problem is a particular concern for block parameters in Ruby 1.8. In Ruby 1.8, if a block parameter shares the name of an existing variable, then invocations of the block simply assign a value to that existing variable rather than creating a new block-local variable. The following code, for example, is problematic because it uses the same identifier i as the block parameter for two nested blocks:
1.upto(10) do |i| # 10 rows
1.upto(10) do |i| # Each has 10 columns
print "#{i} " # Print column number
end
print " ==> Row #{i}\n" # Try to print row number, but get column number
end
Ruby 1.9 is different: block parameters are always local to their block, and invocations of the block never assign values to existing variables. If Ruby 1.9 is invoked with the -w flag, it will warn you if a block parameter has the same name as an existing variable. This helps you avoid writing code that runs differently in 1.8 and 1.9.
Ruby 1.9 is different in another important way, too. Block syntax has been extended to allow you to declare block-local variables that are guaranteed to be local, even if a variable by the same name already exists in the enclosing scope. To do this, follow the list of block parameters with a semicolon and a comma-separated list of block local variables. Here is an example:
x = y = 0 # local variables
1.upto(4) do |x;y| # x and y are local to block
# x and y "shadow" the outer variables
y = x + 1 # Use y as a scratch variable
puts y*y # Prints 4, 9, 16, 25
end
[x,y] # => [0,0]: block does not alter these
In this code, x is a block parameter: it gets a value when the block is invoked with yield. y is a block-local variable. It does not receive any value from a yield invocation, but it has the value nil until the block actually assigns some other value to it. The point of declaring these block-local variables is to guarantee that you will not inadvertently clobber the value of some existing variable. (This might happen if a block is cut-and-pasted from one method to another, for example.) If you invoke Ruby 1.9 with the -w option, it will warn you if a block-local variable shadows an existing variable.
Blocks can have more than one parameter and more than one local variable, of course. Here is a block with two parameters and three local variables:
hash.each {|key,value; i,j,k| ... }
Contrary to some of the other answers, while loops don't actually create a new scope. The problem you're seeing is more subtle.
Prerequisite knowledge: a brief scoping demo
To help show the contrast, blocks passed to a method call DO create a new scope, such that a newly assigned local variable inside the block disappears after the block exits:
### block example - provided for contrast only ###
[0].each {|e| blockvar = e }
p blockvar # NameError: undefined local variable or method
But while loops (like your case) are different, because a variable defined in the loop will persist:
arr = [0]
while arr.any?
whilevar = arr.shift
end
p whilevar # prints 0
Summary of the "problem"
The reason you get the error in your case is because the line that uses message:
puts "#{message}"
appears before any code that assigns message.
It's the same reason this code raises an error if a wasn't defined beforehand:
# Note the single (not double) equal sign.
# At first glance it looks like this should print '1',
# because the 'a' is assigned before (time-wise) the puts.
puts a if a = 1
Not Scoping, but Parsing-visibility
The so-called "problem" - i.e. local-variable visibility within a single scope - is due to ruby's parser. Since we're only considering a single scope, scoping rules have nothing to do with the problem. At the parsing stage, the parser decides at which source locations a local variable is visible, and this visibility does not change during execution.
When determining if a local variable is defined (i.e. defined? returns true) at any point in the code, the parser checks the current scope to see if any code has assigned it before, even if that code has never run (the parser can't know anything about what has or hasn't run at the parsing stage). "Before" meaning: on a line above, or on the same line and to the left-hand side.
An exercise to determine if a local is defined (i.e. visible)
Note that the following only applies to local variables, not methods. (Determining whether a method is defined in a scope is more complex, because it involves searching included modules and ancestor classes.)
A concrete way to see the local variable behavior is to open your file in a text editor. Suppose also that by repeatedly pressing the left-arrow key, you can move your cursor backward through the entire file. Now suppose you're wondering whether a certain usage of message will raise the NameError. To do this, position your cursor at the place you're using message, then keep pressing left-arrow until you either:
reach the beginning of the current scope (you must understand ruby's scoping rules in order to know when this happens)
reach code that assigns message
If you've reached an assignment before reaching the scope boundary, that means your usage of message won't raise NameError. If you don't reach any assignment, the usage will raise NameError.
Other considerations
In the case a variable assignment appears in the code but isn't run, the variable is initialized to nil:
# a is not defined before this
if false
# never executed, but makes the binding defined/visible to the else case
a = 1
else
p a # prints nil
end
While loop test case
Here's a small test case to demonstrate the oddness of the above behavior when it happens in a while loop. The affected variable here is dest_arr.
arr = [0,1]
while n = arr.shift
p( n: n, dest_arr_defined: (defined? dest_arr) )
if n == 0
dest_arr = [n]
else
dest_arr << n
p( dest_arr: dest_arr )
end
end
which outputs:
{:n=>0, :dest_arr_defined=>nil}
{:n=>1, :dest_arr_defined=>nil}
{:dest_arr=>[0, 1]}
The salient points:
The first iteration is intuitive, dest_arr is initialized to [0].
But we need to pay close attention in the second iteration (when n is 1):
At the beginning, dest_arr is undefined!
But when the code reaches the else case, dest_arr is visible again, because the interpreter sees that it was assigned 2 lines above.
Notice also, that dest_arr is only hidden at the start of the loop; its value is never lost (as evidenced by the final contents being [0, 1]).
This also explains why assigning your local before the while loop fixes the problem. The assignment doesn't need to be executed; it only needs to appear in the source code.
Lambda example
f1 = ->{ f2 }
f2 = ->{ f1 }
p f2.call()
# The following fails because the body of f1 tries to access f2 before an assignment for f2 was seen by the parser.
p f1.call() # undefined local variable or method `f2'.
Fix this by putting an f2 assignment before f1's body. Remember, the assignment doesn't actually need to be executed!
f2 = nil # Could be replaced by: if false; f2 = nil; end
f1 = ->{ f2 }
f2 = ->{ f1 }
p f2.call()
p f1.call() # ok
Method-masking gotcha
Things get really hairy if you have a local variable with the same name as a method:
def dest_arr
:whoops
end
arr = [0,1]
while n = arr.shift
p( n: n, dest_arr: dest_arr )
if n == 0
dest_arr = [n]
else
dest_arr << n
p( dest_arr: dest_arr )
end
end
Outputs:
{:n=>0, :dest_arr=>:whoops}
{:n=>1, :dest_arr=>:whoops}
{:dest_arr=>[0, 1]}
A local variable assignment in a scope will "mask"/"shadow" a method call of the same name. (You can still call the method by using explicit parentheses or an explicit receiver.) So this is similar to the previous while loop test, except that instead of becoming undefined above the assignment code, the dest_arr method becomes "unmasked"/"unshadowed" so that the method is callable w/o parentheses. But any code after the assignment will see the local variable.
Some best-practices we can derive from all this
Don't name local variables the same as method names in the same scope
Don't put the initial assignment of a local variable inside the body of a while or for loop, or anything that causes execution to jump around within a scope (calling lambdas or Continuation#call can do this too). Put the assignment before the loop.
I think this is because message is defined inside the loop. At the end of the loop iteration "message" goes out of scope. Defining "message" outside of the loop stops the variable from going out of scope at the end of each loop iteration. So I think you have the right answer.
You could output the value of message at the beginning of each loop iteration to test whether my suggestion is correct.
Why do you think this is a bug? The interpreter is telling you that message may be undefined when that particular piece of code executes.
I'm not sure why you're surprised: on line 5 (assuming that the message = nil line isn't there), you potentially use a variable that the interpreter has never heard of before. The interpreter says "What's message? It's not a method I know, it's not a variable I know, it's not a keyword..." and then you get an error message.
Here's a simpler example to show you what I mean:
while line = gets do
if line =~ /./ then
puts message # How could this work?
message = line
end
end
Which gives:
telemachus ~ $ ruby test.rb < huh
test.rb:3:in `<main>': undefined local variable or method `message' for main:Object (NameError)
Also, if you want to prepare the way for message, I would initialize it as message = '', so that it's a string (rather than nil). Otherwise, if your first line doesn't match hello, you end up tring to add line to nil - which will get you this error:
telemachus ~ $ ruby test.rb < huh
test.rb:4:in `<main>': undefined method `<<' for nil:NilClass (NoMethodError)
you can simply do this :
message=''
while line=gets do
if line =~/hello/ then
# begin a new record
p message unless message == ''
message = String.new(line)
else
message << line
end
end
# hello this is a record
# this is also part of the same record
# hello this is a new record
# this is still record 2
# hello this is record 3 etc etc

Resources