Ruby: calling a defined function [closed] - ruby

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I have a ruby code that supposes to read a paragraph and find the total number of characters, words, and sentences then find the ARI (Automated Readability Index) and decide which grade level. It's my first time using ruby and i'm not sure how to call the function
is this correct to write it after the last end
calcARI("paragraph.txt")
paragraph.txt is the name of the file
this is the code
def calcARI(paragraph)
file = File.open("paragraph.txt","r")
if file
file_data = file.read
file.close
charCount=0
wordCount=0
sentenceCount=0
ARIvalue==0
gradeLevel=""
file.each_line { |line|
charCount+=line.size
for i in 1..line.length
if(line[i]=='.')
sentenceCount+=1
end
if(line[i]==' ')
wordCount+=1
end
end
}
ARIvalue==4.71*(charCount/wordCount)+0.5*(wordCount/sentenceCount)-21.43
case ARIvalue
when 1
gradeLevel="5-6 Kindergarten"
when 2
gradeLevel="6-7 First/Second Grade"
when 3
gradeLevel="7-9 Third Grade"
when 4
gradeLevel="9-10 Fourth Grade"
when 5
gradeLevel="10-11Fifth Grade"
when 6
gradeLevel="11-12 Sixth Grade"
when 7
gradeLevel="12-13 Seventh Grade"
when 8
gradeLevel= "13-14 Eighth Grade"
when 9
gradeLevel= "14-15 Ninth Grade"
when 10
gradeLevel= "15-16 Tenth Grade"
when 11
gradeLevel= "16-17 Eleventh Grade"
when 12
gradeLevel= "17-18 Twelth Grade"
when 13
gradeLevel= "18-24 College student"
when 14
gradeLevel="24+ Professor"
end
puts "Total # of Charecter: #{charCount}"
puts "Total # of words: #{wordCount}"
puts "Total # of sentences: #{sentenceCount}"
puts "Total # of Automated Readability Index: #{ARIvalue}"
puts "Grade Level: '#{gradeLevel}''"
else
puts "Unable to open file!"
end
end

There are a lot of (basic?) errors in your code. Here's a correct way to write it:
def calcARI filepath
charCount = 0
wordCount = 0
sentenceCount = 0
IO.foreach(filepath) do |line|
charCount += line.size
wordCount += line.scan(/ /).count
sentenceCount += line.scan(/\./).count
end
ariValue = 4.71 * ( charCount.to_f / wordCount ) + 0.5 * ( wordCount.to_f / sentenceCount ) - 21.43
gradeLevel = case ariValue.ceil
when 1
"5-6 Kindergarten"
when 2
"6-7 First/Second Grade"
when 3
"7-9 Third Grade"
when 4
"9-10 Fourth Grade"
when 5
"10-11Fifth Grade"
when 6
"11-12 Sixth Grade"
when 7
"12-13 Seventh Grade"
when 8
"13-14 Eighth Grade"
when 9
"14-15 Ninth Grade"
when 10
"15-16 Tenth Grade"
when 11
"16-17 Eleventh Grade"
when 12
"17-18 Twelth Grade"
when 13
"18-24 College student"
when 14
"24+ Professor"
else
""
end
puts "Total # of Charecter: #{charCount}"
puts "Total # of words: #{wordCount}"
puts "Total # of sentences: #{sentenceCount}"
puts "Total # of Automated Readability Index: #{ariValue}"
puts "Grade Level: '#{gradeLevel}'"
rescue StandardError => e
STDERR.puts e.message
end
BTW, your way of counting characters, words and sentences is far from accurate, but if your ARI takes that into account then they may be correct?
To reply to your question, yes you can call calcARI after you defined it.

Related

Not getting expected answer [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Code not returning the correct answer.
I've tired assigning a value to the animal choices. I've put it in the def and outside of it.
puts "Choose your favorite: cats or dogs"
choose = gets
cats = 1
dogs = 2
def favorite_animal (number)
remainder_when_divided_by_2 = number % 2
if remainder_when_divided_by_2 == 0
return "Ken does too."
end
if remainder_when_divided_by_2 == 1
return "Dogs are better!"
end
end
If the user enters Cats the answer "Ken does too!" should show. If the user enters Dogs the answer "Dogs are better!" should show. All I've gotten is 1 or 2 as an answer.
Try this.
loop do
puts "Choose your favorite: cats or dogs"
case gets.chomp
when "cats"
break "Ken does too."
when "dogs"
break "Dogs are better!"
else
puts "That answer is invalid. Try again"
end
end
Here is an example of a session using this code, with my answers being "pigs" and "dogs".
Choose your favorite: cats or dogs
pigs
That answer is invalid. Try again
Choose your favorite: cats or dogs
dogs
#=> "Dogs are better!"
See Kernel#loop. Many Rubyists use loop with the keyword break for most loops, rather than while or until. (for loops are never used).
For what you are doing you don't need a method, but if you want one add the line
def favorite_animal
at the beginning and the line
end
at the end. Then
favorite_animal
#=> "Dogs are better!"
provided I were to give the same answers as I did earlier.
There's a couple things going on:
You have to call the method favorite_animal somewhere; you've only defined it
Your cats/dogs isn't "mapped" to anything, so you need some logic to convert your input into a number, before you call the favorite_animal method
You still have to do something with the value you return inside your method (puts or something else to get it to show)
Here's a minimum example that works that might be useful for you to see the 3 issues above
def favorite_animal (number)
remainder_when_divided_by_2 = number % 2
if remainder_when_divided_by_2 == 0
return "Ken does too."
end
if remainder_when_divided_by_2 == 1
return "Dogs are better!"
end
end
puts "Choose your favorite: cats or dogs"
choose = gets.chomp
answer = if choose == 'cats'
1
else
2
end
puts favorite_animal(answer)

Nested Loop Ruby Answer [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Need assistance saving and totaling the "rainfall input" after the loop has ended. Any advice would be appreciated.
puts "How many years"
years_input = gets.to_i
for years_input in (1..years_input)
puts "Years Passed: Years = " + years_input.to_s
for m in (1..12)
puts "Month: Month # = " + m.to_s
puts "Inches of rainfall"
rainfall_input = gets.to_i
end
end
puts "Total Months"
puts (years_input * 12).to_s
puts "Total Rainfall"
You need to have a place to store your "counter" in this case the total:
puts "How many years"
years_input = gets.to_i
total_rainfall = 0
for years_input in (1..years_input)
puts "Years Passed: Years = " + years_input.to_s
for m in (1..12)
puts "Month: Month # = " + m.to_s
puts "Inches of rainfall"
total_rainfall += gets.to_i
end
end
puts "Total Months"
puts (years_input * 12).to_s
puts "Total Rainfall"
puts total_rainfall
#Danilo has answered your question, so let me suggest how you might write your code in a more Ruby-like way. Let me first mention that for loops are never used by Ruby coders. Instead we use enumerators (like each and, here, times and sum) and blocks. That's in part to hide information within blocks to avoid it being seen by prying eyes outside the block.
require 'date'
puts "How many years?"
nbr_years = gets.to_i
puts "Number of years: #{nbr_years}"
nbr_years.times do
puts "Which year?"
y = gets.to_i
tot = (1..12).sum do |m|
puts "How many inches of rainfall in #{Date::MONTHNAMES[m]}, #{y}?"
gets.to_f
end
puts "Total rainfall in #{y} was #{tot} inches"
end
#{nbr_years} in "Number of years: #{nbr_years}" converts nbr_years to a string by computing nbr_years.to_s. (See Integer#to_s). It's called string interpolation. If nbr_years were an array, Array#to_s would be applied, and so on.
Search for "MONTHNAMES" in Date and you will find that Date::MONTHNAMES (among several date-related constants) has been defined for your convenience:
Date::MONTHNAMES
#=> [nil, "January", "February", "March", "April", "May", "June",
# "July", "August", "September", "October", "November", "December"]
In pure Ruby we need include 'date' to access that constant (no need for that with Rails).
nil is just a placekeeper. Date::MONTHNAMES[0] is never referenced by coders.
Note that for inches of rainfall per month you want gets.to_f rather than gets.to_i.
We can simulate this code by replacing the gets statements with generated values.
y = 2015
puts "How many years?"
nbr_years = 2
puts "Number of years: #{nbr_years}"
nbr_years.times do
puts
puts "Which year?"
y += 1
tot = (1..12).sum do |m|
puts "How many inches of rainfall in #{Date::MONTHNAMES[m]}, #{y}?"
f = (10 * rand).round(2)
puts "#{f} inches of rainfall in #{Date::MONTHNAMES[m]}, #{y}?"
f
end
puts "Total rainfall in #{y} was #{tot} inches"
end
The following (after minor editing) is displayed.
Which year?
How many inches of rainfall in January, 2016?
3.12 inches of rainfall in January, 2016?
How many inches of rainfall in February, 2016?
2.64 inches of rainfall in February, 2016?
...
How many inches of rainfall in December, 2016?
4.48 inches of rainfall in December, 2016?
Total rainfall in 2016 was 60.71 inches
Which year?
How many inches of rainfall in January, 2017?
7.15 inches of rainfall in January, 2017?
...
How many inches of rainfall in December, 2017?
7.87 inches of rainfall in December, 2017?
Total rainfall in 2017 was 36.31 inches

Multiple conditions in a "While" loop Ruby

I'm creating an app that tells what month of the year each number from 1 to 12 represents, like 1 = January, 2 = February, etc. This is the initial code
print "Please, tell me the month's number!"
number = gets.chomp.to_i
while number > 12 do
print "Please, re-type it again!"
number = gets.chomp.to_i
end
case number
when 1 then print "This is January!"
when 2 then print "This is February!"
when 3 then print "This is March!"
when 4 then print "This is April!"
when 5 then print "This is May!"
when 6 then print "This is June!"
when 7 then print "This is July!"
when 8 then print "This is August!"
when 9 then print "This is September!"
when 10 then print "This is October!"
when 11 then print "This is November!"
when 12 then print "This is December!"
else print "I can't undestand you, i'm sorry!"
end
Now, the basic logic is set. The only thing that I think is missing is the second condition in the while loop that defines that, if the input isn't an integer, I need to retype it. I'm trying to define the second condition in that while loop correctly, but no results so far. How can I do it?
How can I make the code better? And is the while loop in this context the right method for the job?
Here is another way to make your code better:
require 'date'
print "Please, tell me the month's number! "
number = gets.to_i
until number.between?(1,12) do
print "Please, re-type it again! "
number = gets.to_i
end
month_name = Hash.new
(1..12).each do |month_num|
month_name[month_num] = Date::MONTHNAMES[month_num]
end
print "This is #{month_name[number]}!"
As noted in other answers chomp is not necessary if using to_i because converting to an integer will take care of the new line.
I am using number.between?(1,12) as suggested by #steenslag to check if the input is valid.
I tried a Hash approach instead of the case statement. A while loop is one way to do this but this is just one other way to get it done.
print "Please, tell me the month's number!"
number = gets.to_i
You never need chomp if you are going to use to_i because the very nature of a number states that it will never have a line ending.
until number > 1 && number < 12 do
You do want the number to be inclusive of 1 to 12, right?
You could alternative do this,
until (1..12).include?(number) do
Or as #teenslag states,
until number.between?(1, 12) do
I think either reads easier, and between? method may be the better choice.
print "Please, re-type it again!"
number = gets.to_i
end
This can be very DRY, or using the built in Date object, as mentioned. But let's see what we can do with the code as you have it, in the same spirit:
case number
when 1 then print "This is January!"
when 2 then print "This is February!"
when 3 then print "This is March!"
when 4 then print "This is April!"
when 5 then print "This is May!"
when 6 then print "This is June!"
when 7 then print "This is July!"
when 8 then print "This is August!"
when 9 then print "This is September!"
when 10 then print "This is October!"
when 11 then print "This is November!"
when 12 then print "This is December!"
end
turns to this:
answer = case number
when 1
"January"
when 2
"February"
when 3
"March"
when 4
"April"
when 5
"May"
when 6
"June"
when 7
"July"
when 8
"August"
when 9
"September"
when 10
"October"
when 11
"November"
when 12
"December"
end
print "This is #{answer}!"
Though it would be nicer just using the Date class.
If that wasn't available for you, then perhaps I would consider using a Hash here instead. I will let you explore those options.
Or even an Array. Remembering that Array elements start at position 0:
print "Please, tell me the month's number!"
until (number = gets.to_i).between?(1,12) do
print "Please, re-type it again!"
end
months = %w[January February March April May June July August September October November December]
puts "This is #{months[number - 1]}!"
number will always be an integer because you converted it into an integer.
"foo".to_i
=> 0
You probably want to use a range instead.
until (1..12).include?(gets.chomp.to_i)
print "Please, re-type it again!"
end
You can also DRY your code by using the built-in number to month conversion:
number = 4
Date::MONTHNAMES[number]
=> "April"
As the above posters have mentioned, this particular instance does not require a second condition. However, in the event you need a multiple condition while loop you would use the logical AND or the logical OR operator.
The logical AND operator is &&. The logical OR operator is ||. You would use && if both conditions need to be true to continue the loop. You'd use the || if one or the other would need to be true to continue the loop.
For example:
while number > 12 || number == 0 do
stuff...
end
while number > 0 && number < 13 do
stuff...
end
In the first snippet, you will enter the loop if the number entered is either above 12 OR equal to 0. In the second you will enter the loop if the number entered is greater than 0 AND less than 13. (Obviously, the second one would be exactly the opposite of what you were using the while loop for here, but is included for demonstration).
A simple loop that you need:
until month_num > 0 && month_num < 13 do
# prompt again
end
A more idiomatic way would be to use between? or include?
until month_num.between? (1, 12) do
# prompt again
end
And instead of multiple when then, you could use a hash map from the Date module:- Date::MONTHNAMES[month_num]

Multithreading calculations in ruby

I want to create a script to calculate numbers in multiple threads. Each thread will calculate the powers of 2 but the first thread must start calculating from 2, the second from 4, and the third from 8, printing some text in-between.
Example:
Im a thread and these are my results
2
4
8
Im a thread and these are my results
4
8
16
Im a thread and these are my results
8
16
32
My fail code:
def loopa(s)
3.times do
puts s
s=s**2
end
end
threads=[]
num=2
until num == 8 do
threads << Thread.new{ loopa(num) }
num=num**2
end
threads.each { |x| puts "Im a thread and these are my results" ; x.join }
My fail results:
Im a thread and these are my results
8
64
4096
8
64
4096
8
64
4096
Im a thread and these are my results
Im a thread and these are my results
I suggest you read the "Threads and Processes" chapter Pragmatic Programmer's ruby book. Here's an old version online. The section called "Creating Ruby Threads" is especially relevant to your question.
To fix the problem, you need to change your Thread.new line to this:
threads << Thread.new(num){|n| loopa(n) }
Your version doesn't work because num is shared between threads, and may be changed by another thread. By passing the variable via a block, the block variable is no longer shared.
More Info
Also, there's an error in your math.
Output values will be:
Thread 1: 2 4 16
Thread 2: 4 16 256
Thread 3: 6 36 1296
"8" is never reached because the until condition quits as soon as it sees "8".
If you want clearer output, use this as the body of loopa:
3.times do
print "#{Thread.current}: #{s}\n"
s=s**2
end
This lets you distinguish the 3 threads. Note that it's better to use a print command with a newline-terminated string versus using puts without a newline, because the latter prints the newline as a separate instruction, which may be interrupted by another thread.
It's normal. Read what you write. Firstly you run 3 threads that are async so output will be in various of combinations of threads output. Then you write 'Im a thread and these are my results' and join each thread. Also remember that Ruby has only references. So if you pass num to thread and then change it it will change in all threads. To avoid it write:
threads = (1..3).map do |i|
puts "I'm starting thread no #{i}"
Thread.new { loopa(2**i) }
end
I feel the need to post a mathematically correct version:
def loopa(s)
3.times do
print "#{Thread.current}: #{s}\n"
s *= 2
end
end
threads=[]
num=2
while num <= 8 do
threads << Thread.new(num){|n| loopa(n) }
num *= 2
end
threads.each { |x| print "Im a thread and these are my results\n" ; x.join }
Bonus 1: threadless solution (naive)
power = 1
workers = 3
iterations = 3
(power ... power + workers).each do |pow|
worker_pow = 2 ** pow
puts "I'm a worker and these are my results"
iterations.times do |inum|
puts worker_pow
worker_pow *= 2
end
end
Bonus 2: threadless solution (cached)
power = 1
workers = 3
iterations = 3
cache_size = workers + iterations - 1
# generate all the values upfront
cache = []
(power ... power+cache_size).each do |i|
cache << 2**i
end
workers.times do |wnum|
puts "I'm a worker and these are my results"
# use a sliding-window to grab the part of the cache we want
puts cache[wnum,3]
end

Using Loops for prompts with If/Else/Esif

I started with:
puts "Hello there, and what's your favorite number?"
favnum = gets.to_i
puts "Your favorite number is #{favnum}?" " A better favorite number is #{favnum + 1}!"
puts "Now, what's your favorite number greater than 10?"
favnumOverTen = gets.to_i
if favnumOverTen < 10
puts "Hey! I said GREATER than 10! Try again buddy."
else
puts "Your favorite number great than 10 is #{favnumOverTen}?"
puts "A bigger and better number over 10 is #{favnumOverTen * 10}!"
puts "It's literally 10 times better!"
end
That worked fine, but if the user entered a number less than 10 the program ended.
I want the user to be prompted to try again until they enter a number greater than 10.
Am I supposed to do that with a loop?
Here's what I took a swing at, but clearly it's wrong:
puts "Hello there, and what's your favorite number?"
favnum = gets.to_i
puts "Your favorite number is #{favnum}?" " A better favorite number is #{favnum + 1}!"
puts "Now, what's your favorite number greater than 10?"
favnumOverTen = gets.to_i
if favnumOverTen < 10
loop.do
puts "Hey! I said GREATER than 10! Try again buddy."
favnumOverTen = gets.to_i
until favnumOverTen > 10
else
puts "Your favorite number great than 10 is #{favnumOverTen}?"
puts "A bigger and better number over 10 is #{favnumOverTen * 10}!"
puts "It's literally 10 times better!"
end
Here's a way that's a little shorter than the previous two:
puts "Now, what's your favorite number greater than 10?"
until (favnumOverTen = gets.to_i) > 10
puts "Hey! I said GREATER than 10! try again buddy."
end
This works because assignment returns the value assigned to the variable.
A better solution would be to get the favorite number of 10 once, then start an until loop checking for favnumOverTen > 10 like this:
puts "What is your favorite number greater than 10?"
favnumOverTen = gets.to_i
until favnumOverTen > 10 do
puts "Hey! I said GREATER than 10! Try again buddy."
favnumOverTen = gets.to_i
end
This way, if the initial entry is greater than 10, the until loop is never executed (saves you the if statement).
Also, just so you're aware, it's not considered idiomatic Ruby to write variable names using camel case (i.e. favnumOverTen should be favnum_over_ten).

Resources