How to add and write to a file in Ruby - ruby

I am new to programming and would like some advice on how to proceed with this question.
I have a file with 10 math questions, but can't seem to figure out how to replace the underscores with the answer of each question.
1. 1 + 3 = __
2. 2 + 2 = __
3. 0 + 9 = __
4. 3 + 4 = __
5. 5 + 2 = __
6. 2 + 5 = __
7. 6 + 4 = __
8. 7 + 1 = __
9. 9 + 9 = __
10. 10 + -1 = __
I am able to open the text file and reading from it but can't seem to figure the way to add them and delete the underscores with the correct answer.
This is the code to read from the file.
File.open("math1.txt", 'r+') do |file|
file.each_line do |line|
puts line
end
end

Extracting operands and operator from a string
You have to somehow extract the operands and operators from the line. I'd use a regular expression:
re = /([-]?\d+) ([-+*\/]) ([-]?\d+)/
([-]?\d+) is the 1st capturing group
[-]? match the literal character - zero or one time
\d+ match a digit (0-9) one ore more times
([-+*\/]) is the 2nd capturing group
[-+*\/] match the characters -, +, * or / (must be escaped) once
([-]?\d+) is the 3rd, capturing group, same as the 1st
Examples using match:
re.match("1. 1 + 3 = __") #=> #<MatchData "1 + 3" 1:"1" 2:"+" 3:"3">
re.match("7. 6 + 4 = __") #=> #<MatchData "6 + 4" 1:"6" 2:"+" 3:"4">
re.match("10. 10 + -1") #=> #<MatchData "10 + -1" 1:"10" 2:"+" 3:"-1">
The capturing groups are can be retrieved from the MatchData:
m = re.match("7. 6 + 4 = __") #=> #<MatchData "6 + 4" 1:"6" 2:"+" 3:"4">
m[1] #=> "6"
m[2] #=> "+"
m[3] #=> "4"
And they are also stored (from left to right) in special global variables $1, $2 and $3:
re.match("7. 6 + 4 = __") #=> #<MatchData "6 + 4" 1:"6" 2:"+" 3:"4">
$1 #=> "6"
$2 #=> "+"
$3 #=> "4"
Performing calculations with dynamic operands and operator
Note that these are still strings. We have to convert them to integers via to_i in order to perform operations on them:
$1.to_i #=> 6
$3.to_i #=> 4
$1.to_i + $3.to_i #=> 10
But I've hard-coded the +. To make the method call dynamic, we have to use send:
6.send("+", 4) #=> 10
$1.to_i.send($2, $3.to_i) #=> 10
Replacing a placeholder with a value
Finally, we have to replace __ with our result. This can be done with sub:
"7. 6 + 4 = __".sub("__", "10")
#=> "7. 6 + 4 = 10"
That's it. Now you have to incorporate these steps into your loop.

This would be a simple solution
out = File.open("out.txt",'w+')
File.open("math1.txt", 'r+') do |file|
file.each_line do |line|
calc = eval(line.split(".")[1].split("=")[0])
out.write(line.sub('__',calc.to_s))
end
end

Related

Ruby: evaluation order depends on whitespace and seemingly redundant parens, why?

I'm trying to understand the behaviour of arithmetic methods in Ruby. 1 .+ 2 .* 3 and 1 .+ (2) .* 3 and 1. + 2. * 3 and 1. + (2). * 3 and 1.+2.*3 all evaluate to 7, which means :+ is called before :*, but 1.+(2).*3 evaluates to 9, which means :* is called before :+. Redefining both methods confirms that that's what's happening:
class Integer
alias_method :add, :+
alias_method :mul, :*
def + other
print :+
self.add other
end
def * other
print :*
self.mul other
end
end
puts 1 .+ 2 .* 3
puts 1 .+ (2) .* 3
puts 1. + 2. * 3
puts 1. + (2). * 3
puts 1.+2.*3
puts 1.+(2).*3
Output:
*+7
*+7
*+7
*+7
*+7
+*9
Why does it happen? Please point me where to find the relevant documentation. Thanks in advance.
Let's give an example that might be easier to understand
puts (2) + 3
vs
puts(2) + 3
In the first case, puts is followed by a space. Ruby takes it as omitted parentheses and evaluates the rest as the argument. It's equvalent to puts((2) + 3). 5 is printed. In the 2nd case, puts is immediately followed by a left parenthesis and ruby takes that as the beginning of method arguments. It's equivalent to (puts(2)) + 3. It will print a 2 and then fail when trying to evaluate nil + 3.
Now lets look at your examples
puts 1 .+ 2 .* 3
puts 1 .+ (2) .* 3
puts 1. + 2. * 3
puts 1. + (2). * 3
puts 1.+2.*3
In the first 6 cases, no method call is immediately followed by (, all the parentheses are redundant. They are evaluated right to left. Note that not * before +, but right to left.
puts 1 .* 2 .+ 3 #=> 5
In the last example puts 1.+(2).*3, the ( immediately follows .+, so 2 is an argument to .+. The result is then multiplied by 3.

Ruby -- Why does += increase the number for my string?

In the following code the value for "seven" changes from 1 to 2:
word_counts = Hash.new(0)
sample = "If seven maids with seven mops"
sample.split.each do |word|
word_counts[word.downcase] += 1
puts word_counts
end
Output:
{}
{"if"=>1}
{"if"=>1, "seven"=>1}
{"if"=>1, "seven"=>1, "maids"=>1}
{"if"=>1, "seven"=>1, "maids"=>1, "with"=>1}
{"if"=>1, "seven"=>2, "maids"=>1, "with"=>1}
{"if"=>1, "seven"=>2, "maids"=>1, "with"=>1, "mops"=>1}
Can someone explain why it went from 1 to 2?
OK, I'll try..
word_counts[word.downcase] += 1 means word_counts[word.downcase] = word_counts[word.downcase] + 1. Now, on fifth iteration word equals 'seven', so it does word_counts['seven'] = word_counts['seven'] + 1. But word_counts['seven'] was 1, so it becomes 2.
When you split the string you get the array with two strings "seven", because the sentence has two occurrences of that word.
"If seven maids with seven mops".split #=> ["If", "seven", "maids", "with", "seven", "mops"]

Ruby while and if loop issue

x = 16
while x != 1 do
if x % 2 == 0
x = x / 2
print "#{x} "
end
break if x < 0
end
Hi, the result I get from above is 8 4 2 1 . Is there any way to remove the space at the end?
One of Rubys main features is its beauty - you can shorten that loop to a nice one liner when using an array:
x = 16
arr = []
arr.push(x /= 2) while x.even?
puts arr.join(' ')
# => "8 4 2 1"
* As sagarpandya82 suggested x.even? is the same as using x % 2 == 0, leading to even more readable code
Don't print the values into the loop. Put them into a list (array) then, after the loop, join the array items using space as glue.
x = 16
a = []
while x != 1 do
if x % 2 == 0
x = x / 2
a << x
end
break if x < 0
end
puts '<' + a.join(' ') + '>'
The output is:
<8 4 2 1>
As #Bathsheba notes in a comment, this solution uses extra memory (the array) to store the values and also the call to Array#join generates a string that doubles the memory requirements. This is not an issue for small lists as the one in the question but needs to be considered the list becomes very large.
loop.reduce([[], 16]) do |(acc, val), _|
break acc if val <= 1
acc << val / 2 if val.even?
[acc, val / 2]
end.join ' '
if x != 0
print " "
end
is one way, having dropped the suffixed space from the other print. I/O will always be the performance bottleneck; an extra if will have a negligible effect on performance, and the extra print will merely contribute to the output stream which is normally buffered.

Inserting a space in between characters using gsub - Ruby

Let's say I had a string "I have 36 dogs in 54 of my houses in 24 countries".
Is it possible by using only gsub to add a " " between each digit so that the string becomes "I have 3 6 dogs in 5 4 of my houses in 2 4 countries"?
gsub(/(\d)(\d)/, "#{$1} #{$2}") does not work as it replaces each digit with a space and neither does gsub(/\d\d/, "\d \d"), which replaces the each digit with d.
s = "I have 3651 dogs in 24 countries"
Four ways to use String#gsub:
Use a positive lookahead and capture group
r = /
(\d) # match a digit in capture group 1
(?=\d) # match a digit in a positive lookahead
/x # extended mode
s.gsub(r, '\1 ')
#=> "I have 3 6 5 1 dogs in 2 4 countries"
A positive lookbehind could be used as well:
s.gsub(/(?<=\d)(\d)/, ' \1')
Use a block
s.gsub(/\d+/) { |s| s.chars.join(' ') }
#=> "I have 3 6 5 1 dogs in 2 4 countries"
Use a positive lookahead and a block
s.gsub(/\d(?=\d)/) { |s| s + ' ' }
#=> "I have 3 6 5 1 dogs in 2 4 countries"
Use a hash
h = '0'.upto('9').each_with_object({}) { |s,h| h[s] = s + ' ' }
#=> {"0"=>"0 ", "1"=>"1 ", "2"=>"2 ", "3"=>"3 ", "4"=>"4 ",
# "5"=>"5 ", "6"=>"6 ", "7"=>"7 ", "8"=>"8 ", "9"=>"9 "}
s.gsub(/\d(?=\d)/, h)
#=> "I have 3 6 5 1 dogs in 2 4 countries"
An alternative way is to look for the place between the numbers using lookahead and lookbehind and then just replace that with a space.
[1] pry(main)> s = "I have 36 dogs in 54 of my houses in 24 countries"
=> "I have 36 dogs in 54 of my houses in 24 countries"
[2] pry(main)> s.gsub(/(?<=\d)(?=\d)/, ' ')
=> "I have 3 6 dogs in 5 4 of my houses in 2 4 countries"
In order to reference a match you should use \n where n is the match, not $1.
s = "I have 36 dogs in 54 of my houses in 24 countries"
s.gsub(/(\d)(\d)/, '\1 \2')
# => "I have 3 6 dogs in 5 4 of my houses in 2 4 countries"

Ruby: increment all integers in a string by +1

I am looking for a succinct way to increment all the integers found in a string by +1 and return the full string.
For example:
"1 plus 2 and 10 and 100"
needs to become
"2 plus 3 and 11 and 101"
I can find all the integers very easily with
"1 plus 2 and 10 and 100".scan(/\d+/)
but I'm stuck at this point trying to increment and put the parts back together.
Thanks in advance.
You could use the block form of String#gsub:
str = "1 plus 2 and 10 and 100".gsub(/\d+/) do |match|
match.to_i + 1
end
puts str
Output:
2 plus 3 and 11 and 101
The gsub method can take in a block, so you can do this
>> "1 plus 2 and 10 and 100".gsub(/\d+/){|x|x.to_i+1}
=> "2 plus 3 and 11 and 101"
The thing with your regex is that it doesn't preserve your original string in the chain in order to put it back. What I did was to split it using spaces, detect which are words or integers using w.to_i != 0 (not counting 0 as an integer, you might want to improve this), add one, and join it back:
s = "1 plus 2 and 10 and 100"
s.split(" ").map{ |e| if (e.to_i != 0) then e.to_i+1 else e end }.join(" ")
=> "2 plus 3 and 11 and 101"

Resources