Soloway's recurring rainfall: Difference between revisions
No edit summary |
(→{{header|ALGOL 68}}: Allow for negative rainfall) |
||
Line 76: | Line 76: | ||
<syntaxhighlight lang="algol68"> |
<syntaxhighlight lang="algol68"> |
||
BEGIN # read a sequence of integers, terminated by 99999 and outpout their average # |
BEGIN # read a sequence of integers, terminated by 99999 and outpout their average # |
||
INT end value = 99999; |
INT end value = 99999; |
||
INT sum := 0; |
INT sum := 0; |
||
INT count := 0; |
INT count := 0; |
||
BOOL invalid value := FALSE; |
|||
on value error( stand in, ( REF FILE f )BOOL: invalid value := TRUE ); |
|||
WHILE |
WHILE |
||
INT n := 0; |
INT n := 0; |
||
WHILE |
WHILE |
||
STRING s; |
|||
print( ( "Enter rainfall (integer) or ", whole( end value, 0 ), " to quit: " ) ); |
print( ( "Enter rainfall (integer) or ", whole( end value, 0 ), " to quit: " ) ); |
||
read( ( |
read( ( n, newline ) ); |
||
invalid value |
|||
FOR s pos FROM LWB s TO UPB s WHILE valid DO |
|||
IF valid := s[ s pos ] >= "0" AND s[ s pos ] <= "9" |
|||
⚫ | |||
n *:= 10 +:= ( ABS s[ s pos ] - ABS "0" ) |
|||
FI |
|||
OD; |
|||
NOT valid |
|||
DO |
DO |
||
print( ( "Invalid input, please enter an integer", newline ) ) |
print( ( "Invalid input, please enter an integer", newline ) ); |
||
⚫ | |||
OD; |
OD; |
||
n /= end value |
n /= end value |
Revision as of 17:20, 11 September 2022
You are encouraged to solve this task according to the task description, using any language you may know.
Soloway's Recurring Rainfall is commonly used to assess general programming knowledge by requiring basic program structure, input/output, and program exit procedure.
The problem:
Write a program that will read in integers and output their average. Stop reading when the value 99999 is input.
For languages that aren't traditionally interactive, the program can read in values as makes sense and stopping once 99999 is encountered. The classic rainfall problem comes from identifying success of Computer Science programs with their students, so the original problem statement is written above -- though it may not strictly apply to a given language in the modern era.
Implementation Details:
- Only Integers are to be accepted as input
- Output should be floating point
- Rainfall can be negative (https://www.geographyrealm.com/what-is-negative-rainfall/)
- For languages where the user is inputting data, the number of data inputs can be "infinite"
- A complete implementation should handle error cases reasonably (asking the user for more input, skipping the bad value when encountered, etc)
References:
- http://cs.brown.edu/~kfisler/Pubs/icer14-rainfall/icer14.pdf
- https://www.curriculumonline.ie/getmedia/8bb01bff-509e-48ed-991e-3ab5dad74a78/Seppletal-2015-DoweknowhowdifficulttheRainfallProblemis.pdf
- https://en.wikipedia.org/wiki/Moving_average#Cumulative_average
Ada
with Ada.Text_IO;
with Ada.Text_IO.Unbounded_IO;
with Ada.Strings.Unbounded;
with Ada.Integer_Text_IO;
with Ada.IO_Exceptions;
procedure RecurringRainfall is
Current_Average : Float := 0.0;
Current_Count : Integer := 0;
Input_Integer : Integer;
-- Recursively attempt to get a new integer
function Get_Next_Input return Integer is
Input_Integer : Integer;
Clear_String : Ada.Strings.Unbounded.Unbounded_String;
begin
Ada.Text_IO.Put("Enter rainfall int, 99999 to quit: ");
Ada.Integer_Text_IO.Get(Input_Integer);
return Input_Integer;
exception
when Ada.IO_Exceptions.Data_Error =>
Ada.Text_IO.Put_Line("Invalid input");
-- We need to call Get_Line to make sure we flush the kb buffer
-- The pragma is to ignore the fact that we are not using the result
pragma Warnings (Off, Clear_String);
Clear_String := Ada.Text_IO.Unbounded_IO.Get_Line;
-- Recursively call self -- it'll break when valid input is hit
-- We disable the infinite recursion because we're intentionally
-- doing this. It will "break" when the user inputs valid input
-- or kills the program
pragma Warnings (Off, "infinite recursion");
return Get_Next_Input;
pragma Warnings (On, "infinite recursion");
end Get_Next_Input;
begin
loop
Input_Integer := Get_Next_Input;
exit when Input_Integer = 99999;
Current_Count := Current_Count + 1;
Current_Average := Current_Average + (Float(1) / Float(Current_Count))*Float(Input_Integer) - (Float(1) / Float(Current_Count))*Current_Average;
Ada.Text_IO.Put("New Average: ");
Ada.Text_IO.Put_Line(Float'image(Current_Average));
end loop;
end RecurringRainfall;
ALGOL 68
BEGIN # read a sequence of integers, terminated by 99999 and outpout their average #
INT end value = 99999;
INT sum := 0;
INT count := 0;
BOOL invalid value := FALSE;
on value error( stand in, ( REF FILE f )BOOL: invalid value := TRUE );
WHILE
INT n := 0;
WHILE
print( ( "Enter rainfall (integer) or ", whole( end value, 0 ), " to quit: " ) );
read( ( n, newline ) );
invalid value
DO
print( ( "Invalid input, please enter an integer", newline ) );
invalid value := FALSE
OD;
n /= end value
DO
sum +:= n;
count +:= 1;
print( ( "New average: ", fixed( sum / count, -12, 4 ), newline ) )
OD
END
BASIC
QBasic
n = 0
sum = 0
DO
INPUT "Enter integral rainfall (99999 to quit): ", i
IF i = 99999 THEN
EXIT DO
ELSEIF (i < 0) OR (i <> INT(i)) THEN
PRINT "Must be an integer no less than 0, try again."
ELSE
n = n + 1
sum = sum + i
PRINT " The current average rainfall is"; sum / n
END IF
LOOP
- Output:
Same as FreeBASIC entry.
BASIC256
n = 0
sum = 0
while True
input "Enter integral rainfall (99999 to quit): ", i
if i = 99999 then exit while
if (i < 0) or (i <> int(i)) then
print "Must be an integer no less than 0, try again."
else
n += 1
sum += i
print " The current average rainfall is "; sum/n
end if
end while
- Output:
Same as FreeBASIC entry.
True BASIC
LET n = 0
LET sum = 0
DO
PRINT "Enter integral rainfall (99999 to quit): "
INPUT i
IF i = 99999 THEN
EXIT DO
ELSEIF (i < 0) OR (i <> INT(i)) THEN
PRINT "Must be an integer no less than 0, try again."
ELSE
LET n = n + 1
LET sum = sum + i
PRINT " The current average rainfall is"; sum / n
END IF
LOOP
END
- Output:
Same as QBasic entry.
Yabasic
// Rosetta Code problem: https://rosettacode.org/wiki/Soloway%27s_Recurring_Rainfall
// by Jjuanhdez, 09/2022
n = 0
sum = 0
do
input "Enter integral rainfall (99999 to quit): " i
if (i < 0) or (i <> int(i)) then
print "Must be an integer no less than 0, try again."
elsif i = 99999
break
else
n = n + 1
sum = sum + i
print " The current average rainfall is ", sum/n
end if
loop
- Output:
Same as FreeBASIC entry.
C
#include <stdio.h>
int main(int argc, char **argv)
{
// Unused variables
(void)argc;
(void)argv;
float currentAverage = 0;
unsigned int currentEntryNumber = 0;
for (;;)
{
int ret, entry;
printf("Enter rainfall int, 99999 to quit: ");
ret = scanf("%d", &entry);
if (ret)
{
if (entry == 99999)
{
printf("User requested quit.\n");
break;
}
else
{
currentEntryNumber++;
currentAverage = currentAverage + (1.0f/currentEntryNumber)*entry - (1.0f/currentEntryNumber)*currentAverage;
printf("New Average: %f\n", currentAverage);
}
}
else
{
printf("Invalid input\n");
while (getchar() != '\n'); // Clear input buffer before asking again
}
}
return 0;
}
C++
#include <iostream>
#include <limits>
int main()
{
float currentAverage = 0;
unsigned int currentEntryNumber = 0;
for (;;)
{
int entry;
std::cout << "Enter rainfall int, 99999 to quit: ";
std::cin >> entry;
if (!std::cin.fail())
{
if (entry == 99999)
{
std::cout << "User requested quit." << std::endl;
break;
}
else
{
currentEntryNumber++;
currentAverage = currentAverage + (1.0f/currentEntryNumber)*entry - (1.0f/currentEntryNumber)*currentAverage;
std::cout << "New Average: " << currentAverage << std::endl;
}
}
else
{
std::cout << "Invalid input" << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
return 0;
}
C#
namespace RosettaCode
{
class CSharpRecurringRainfall
{
static int ReadNextInput()
{
System.Console.Write("Enter rainfall int, 99999 to quit: ");
string input = System.Console.ReadLine();
if (System.Int32.TryParse(input, out int num))
{
return num;
}
else
{
System.Console.WriteLine("Invalid input");
return ReadNextInput();
}
}
static void Main()
{
double currentAverage = 0;
int currentEntryNumber = 0;
for (int lastInput = ReadNextInput(); lastInput != 99999; lastInput = ReadNextInput())
{
currentEntryNumber++;
currentAverage = currentAverage + (1.0/(float)currentEntryNumber)*lastInput - (1.0/(float)currentEntryNumber)*currentAverage;
System.Console.WriteLine("New Average: " + currentAverage);
}
}
}
}
D
import std.stdio;
void main()
{
float currentAverage = 0;
uint currentEntryNumber = 0;
for (;;)
{
int entry;
write("Enter rainfall int, 99999 to quit: ");
try {
readf("%d", entry);
readln();
}
catch (Exception e) {
writeln("Invalid input");
readln();
continue;
}
if (entry == 99999) {
writeln("User requested quit.");
break;
} else {
currentEntryNumber++;
currentAverage = currentAverage + (1.0/currentEntryNumber)*entry - (1.0/currentEntryNumber)*currentAverage;
writeln("New Average: ", currentAverage);
}
}
}
Fortran
Fortran 90
function getNextInput() result(Input)
implicit none
integer :: Input
integer :: Reason
Reason = 1
do while (Reason > 0)
print *, "Enter rainfall int, 99999 to quit: "
read (*,*,IOSTAT=Reason) Input
if (Reason > 0) then
print *, "Invalid input"
end if
enddo
end function getNextInput
program recurringrainfall
implicit none
real :: currentAverage
integer :: currentCount
integer :: lastInput
integer :: getNextInput
currentAverage = 0
currentCount = 0
do
lastInput = getNextInput()
if (lastInput == 99999) exit
currentCount = currentCount + 1
currentAverage = currentAverage + (1/real(currentCount))*lastInput - (1/real(currentCount))*currentAverage
print *, 'New Average: ', currentAverage
enddo
end program recurringrainfall
FreeBASIC
Dim As Integer n = 0, sum = 0
Dim As Single i
Do
Input "Enter integral rainfall (99999 to quit): ", i
If i = 99999 Then
Exit Do
Elseif (i < 0) Or (i <> Int(i)) Then
Print "Must be an integer no less than 0, try again."
Else
n += 1
sum += i
Print " The current average rainfall is"; sum/n
End If
Loop
Sleep
- Output:
Enter integral rainfall (99999 to quit): 5.4 Must be an integer no less than 0, try again. Enter integral rainfall (99999 to quit): -2 Must be an integer no less than 0, try again. Enter integral rainfall (99999 to quit): 5 The current average rainfall is 5 Enter integral rainfall (99999 to quit): 2 The current average rainfall is 3.5 Enter integral rainfall (99999 to quit): 10 The current average rainfall is 5.6666666666667 Enter integral rainfall (99999 to quit): 99999
J
The specification for this task seems inadequate. So, we'll copy some features from other implementations of this task.
Specifically, we'll issue a prompt for every number requested, and reject lines which contain something that is not a number.
However, since we're supposedly collecting information about rainfall, and rainfall is not necessarily an integer, we'll allow arbitrary numbers here and trust the judgement of the person entering the numbers.
Implementation:
require'general/misc/prompt'
task=: {{
list=. ''
while. do.
y=. _.".prompt'Enter rainfall int, 99999 to quit: '
if. 99999 e. y do. (+/%#)list return. end.
if. y-:y do. echo 'New average: ',":(+/%#)list=. list,y
else. echo 'invalid input, reenter'
end.
end.
}}
Example use:
task''
Enter rainfall int, 99999 to quit: 2
New average: 2
Enter rainfall int, 99999 to quit: 3
New average: 2.5
Enter rainfall int, 99999 to quit: 5
New average: 3.33333
Enter rainfall int, 99999 to quit: 7
New average: 4.25
Enter rainfall int, 99999 to quit: 11
New average: 5.6
Enter rainfall int, 99999 to quit: 99999
5.6
Java
class recurringrainfall
{
private static int GetNextInt()
{
while (true)
{
System.out.print("Enter rainfall int, 99999 to quit: ");
String input = System.console().readLine();
try
{
int n = Integer.parseInt(input);
return n;
}
catch (Exception e)
{
System.out.println("Invalid input");
}
}
}
private static void recurringRainfall() {
float currentAverage = 0;
int currentEntryNumber = 0;
while (true) {
int entry = GetNextInt();
if (entry == 99999)
return;
currentEntryNumber++;
currentAverage = currentAverage + ((float)1/currentEntryNumber)*entry - ((float)1/currentEntryNumber)*currentAverage;
System.out.println("New Average: " + currentAverage);
}
}
public static void main(String args[]) {
recurringRainfall();
}
}
Julia
Written for simplicity of reading rather than brevity.
"""
Two annotated example outputs
were given: 1) a run with three positive inputs, a zero, and
a negative number before the sentinel; 2) a run in which
the sentinel was the first and only input.
"""
function rainfall_problem(sentinel = 999999)
total, entries = 0, 0
while true
print("Enter rainfall as nonnegative integer ($sentinel to exit): ")
n = tryparse(Int, readline())
if n == sentinel
break
elseif n == nothing || n < 0
println("Error: bad input. Try again\n")
else
total += n
entries += 1
println("Average rainfall is currently ", total / entries)
end
end
if entries == 0
println("No entries to calculate!")
end
end
rainfall_problem()
PureBasic
Define.i n=0, sum=0
Define.f i
If OpenConsole()
Repeat
Print("Enter integral rainfall (99999 to quit): ")
i=ValF(Input())
If i=99999
Break
ElseIf i<0 Or i<>Int(i)
PrintN("Must be an integer no less than 0, try again.")
Else
n+1
sum+i
PrintN(" The current average rainfall is "+StrF(sum/n,5))
EndIf
ForEver
EndIf
- Output:
Same as FreeBASIC entry.
Python
import sys
def get_next_input():
try:
num = int(input("Enter rainfall int, 99999 to quit: "))
except:
print("Invalid input")
return get_next_input()
return num
current_average = 0.0
current_count = 0
while True:
next = get_next_input()
if next == 99999:
sys.exit()
else:
current_count += 1
current_average = current_average + (1.0/current_count)*next - (1.0/current_count)*current_average
print("New average: ", current_average)
Raku
This task seems to be more about following (kind of vague) instructions and anticipating error conditions rather than solving a rather simple problem.
I notice that units are specified for neither measurement; time nor rainfall accumulation. Time is assumed to always increment in units of one, rainfall in some non-negative integer. "Integer" gets a little leeway in this entry. Floating point numbers or Rational numbers that are exactly equal to an Integer are allowed, even if they are technically not integers.
# Write a program that will read in integers and
# output their average. Stop reading when the
# value 99999 is input.
my ($periods, $accumulation, $rainfall) = 0, 0;
loop {
loop {
$rainfall = prompt 'Integer units of rainfall in this time period? (999999 to finalize and exit)>: ';
last if $rainfall.chars and $rainfall.Numeric !~~ Failure and $rainfall.narrow ~~ Int and $rainfall ≥ 0;
say 'Invalid input, try again.';
}
last if $rainfall == 999999;
++$periods;
$accumulation += $rainfall;
say-it;
}
say-it;
sub say-it { printf "Average rainfall %.2f units over %d time periods.\n", ($accumulation / $periods) || 0, $periods }
- Output:
# Normal operation Integer units of rainfall in this time period? (999999 to finalize and exit)>: 0 Average rainfall 0.00 units over 1 time periods. Integer units of rainfall in this time period? (999999 to finalize and exit)>: 2 Average rainfall 1.00 units over 2 time periods. Integer units of rainfall in this time period? (999999 to finalize and exit)>: 4 Average rainfall 2.00 units over 3 time periods. Integer units of rainfall in this time period? (999999 to finalize and exit)>: 6 Average rainfall 3.00 units over 4 time periods. Integer units of rainfall in this time period? (999999 to finalize and exit)>: 8 Average rainfall 4.00 units over 5 time periods. Integer units of rainfall in this time period? (999999 to finalize and exit)>: 999999 Average rainfall 4.00 units over 5 time periods. # Invalid entries and valid but not traditional "Integer" entries demonstrated Integer units of rainfall in this time period? (999999 to finalize and exit)>: a Invalid input, try again. Integer units of rainfall in this time period? (999999 to finalize and exit)>: 1.1 Invalid input, try again. Integer units of rainfall in this time period? (999999 to finalize and exit)>: Invalid input, try again. Integer units of rainfall in this time period? (999999 to finalize and exit)>: 2.0 Average rainfall 2.00 units over 1 time periods. Integer units of rainfall in this time period? (999999 to finalize and exit)>: .3e1 Average rainfall 2.50 units over 2 time periods. Integer units of rainfall in this time period? (999999 to finalize and exit)>: 999999 Average rainfall 2.50 units over 2 time periods. # Valid summary with no entries demonstrated. Integer units of rainfall in this time period? (999999 to finalize and exit)>: 999999 Average rainfall 0.00 units over 0 time periods.
Rust
fn main() {
let mut current_average:f32 = 0.0;
let mut current_entry_number:u32 = 0;
loop
{
let current_entry;
println!("Enter rainfall int, 99999 to quit: ");
let mut input_text = String::new();
std::io::stdin().read_line(&mut input_text).expect("Failed to read from stdin");
let trimmed = input_text.trim();
match trimmed.parse::<u32>() {
Ok(new_entry) => current_entry = new_entry,
Err(..) => { println!("Invalid input"); continue; }
};
if current_entry == 99999
{
println!("User requested quit.");
break;
}
else
{
current_entry_number = current_entry_number + 1;
current_average = current_average + (1.0 / current_entry_number as f32)*(current_entry as f32) - (1.0 / current_entry_number as f32)*current_average;
println!("New Average: {}", current_average);
}
}
}
Wren
import "./ioutil" for Input
var n = 0
var sum = 0
while (true) {
var i = Input.integer("Enter integral rainfall (99999 to quit): ")
if (i == 99999) break
n = n + 1
sum = sum + i
System.print(" The current average rainfall is %(sum/n)")
}
- Output:
Sample session:
Enter integral rainfall (99999 to quit): 5.4 Must be an integer, try again. Enter integral rainfall (99999 to quit): five Must be an integer, try again. Enter integral rainfall (99999 to quit): 5 The current average rainfall is 5 Enter integral rainfall (99999 to quit): -2 The current average rainfall is 1.5 Enter integral rainfall (99999 to quit): 4 The current average rainfall is 2.3333333333333 Enter integral rainfall (99999 to quit): 10 The current average rainfall is 4.25 Enter integral rainfall (99999 to quit): 99999