In this program I want to get the average of the entered numbers not only the sum but I can't get the average and the "Enter a number: " keeps repeating.
here's my code
(princ"Enter how many numbers to read: ")
(defparameter a(read))
(defun num ()
(loop repeat a
sum (progn
(format *query-io* "Enter a number: ")
(finish-output)
(parse-integer (read-line *query-io* )))))
(format t "Sum: ~d ~%" (num))
(format t "Average: ~d ~%" (/ (num) a)) ;; I can't get the output for the average and the "Enter a number: " keeps repeating.
Enter how many numbers to read: 5
Enter a number: 4
Enter a number: 3
Enter a number: 2
Enter a number: 1
Enter a number: 3
Sum: 13
Enter a number: <-------
The reason that your prompt is repeating is because your code is calling NUM
twice. Each time NUM
is called, it asks for more input. A basic fix is to wrap the part of your program that shows the results in a function, just calling NUM
once and binding the results to a variable:
(princ "Enter how many numbers to read: ")
(defparameter a (read))
(defun num ()
(loop repeat a
sum (progn
(format *query-io* "Enter a number: ")
(finish-output)
(parse-integer (read-line *query-io*)))))
(defun stats ()
(let ((sum (num)))
(format t "Sum: ~d ~%" sum)
(format t "Average: ~d ~%" (/ sum a))))
This "works", but when the code is loaded the user is prompted for input to set the A
parameter; then the user needs to call STATS
to enter data and see the results. This is awkward to say the least. But there are lots of little problems with the program. Here are some suggestions:
READ-LINE
instead of READ
to get the count of input elements*QUERY-IO*
consistentlyFINISH-OUTPUT
consistentlyA
and NUM
are not very descriptive here)~A
instead of ~D
The average may not be an integer; it could be a fraction. When its argument is not an integer, FORMAT
uses ~A
in place of ~D
anyway, but it is probably better to be explicit about this. I would just use ~A
in both lines of output.
You can write one function to incorporate all of the above suggestions:
(defun stats ()
(format *query-io* "Enter how many numbers to read: ")
(finish-output *query-io*)
(let* ((count (parse-integer (read-line *query-io*)))
(sum
(loop repeat count
do (format *query-io* "Enter a number: ")
(finish-output *query-io*)
summing (parse-integer (read-line *query-io*)))))
(format *query-io* "Sum: ~A~%Average: ~A~%" sum (/ sum count))
(finish-output *query-io*)))
Here COUNT
replaces the earlier A
, and it is a local variable within the STATS
function. LET*
is used instead of LET
so that SUM
can make use of COUNT
, but nested LET
expressions could be used instead. Note that SUM
is bound to the result of the loop, which is the result from the SUMMING
keyword. *QUERY-IO*
is used consistently throughout, and printed output is always followed by FINISH-OUTPUT
when its sequencing is important.
There is a lot that could be done to further improve this code. There is no input validation in this code; that should be added. It might be good to break STATS
into smaller functions that separate input, calculation, and output operations. It might be nice to be able to handle floats in input and output.
Sample interaction:
CL-USER> (stats)
Enter how many numbers to read: 3
Enter a number: 1
Enter a number: 2
Enter a number: 2
Sum: 5
Average: 5/3
OP has asked in a comment:
I tried to add (min count) (max count)) in your given code to get the minimum and maximum value but the output is the sum. How can I get minimum and maximum number?
This is really a new question, but it points to shortcomings in the design of the above function that were hinted at with "It might be good to break STATS
into smaller functions...."
Firstly, note that (MIN COUNT)
or (MAX COUNT)
will not be helpful, since we want the minimum or maximum of the data entered; COUNT
is just the number of values that the user wants to enter. The original code directly summed the values as they were input; a more flexible approach would be to collect the input in a list, and then to operate on that list to get the desired results. The MIN
and MAX
functions operate on a number of values, not on a list, so we will need to use APPLY
to apply them to a list of results. We can also apply +
to the list of input values to get the sum of the elements in the list:
(defun stats ()
(format *query-io* "Enter how many numbers to read: ")
(finish-output *query-io*)
(let* ((count (parse-integer (read-line *query-io*)))
(data
(loop repeat count
do (format *query-io* "Enter a number: ")
(finish-output *query-io*)
collecting (parse-integer (read-line *query-io*))))
(min (apply #'min data))
(max (apply #'max data))
(sum (apply #'+ data))
(avg (float (/ sum count))))
(format *query-io* "Minimum: ~A~%" min)
(format *query-io* "Maximum: ~A~%" max)
(format *query-io* "Sum: ~A~%" sum)
(format *query-io* "Average: ~A~%" avg)
(finish-output *query-io*)))
Here the loop collects input in a list by using the COLLECTING
keyword, and the result is bound to DATA
. The desired calculations are made, and the results are printed.
This works, but it is really begging for a better design; there are too many different things happening in one function. You could move the display code into another function, returning MIN
, MAX
, etc. in a list or as multiple values. But then it would be even more flexible if the calculations were made independent of the code that gathers input by allowing STATS
to return a list of input. The calculation of AVG
requires COUNT
; the new code could return COUNT
too, but it is not needed anyway, since we can call LENGTH
on the input list to get a count. One wonders why we need the user to enter a number of elements at all. What if we took numbers from the user until a non-numeric value is entered?
This answer has already gotten quite long, but below is some code that breaks the STATS
function into smaller parts, refining it in the process. By using smaller function definitions that are more focused on their tasks, the functions can be reused or combined with other functions to accomplish other goals more easily, and it will be easier to modify the definitions when changes are required. The final function, PROMPT-FOR-STATS-REPORT
does essentially what the earlier STATS
function did. It is now much easier to add new functionality by modifying PROMPT-FOR-STATS
. New accessor functions can be added as needed when PROMPT-FOR-STATS
is augmented, and PROMPT-FOR-STATS-REPORT
can be modified to display results differently, or to access and display newly added functionality.
This is by no means the optimal solution to OP's problem, if there is an "optimal" solution. I encourage OP to try to find ways to improve upon the design of this code. There are some comments scattered throughout:
;;; Here is a function to prompt for an integer. Since we want to receive
;;; non-numeric input `:JUNK-ALLOWED` is set to `T`. When input that can not be
;;; parsed into an integer is provided, `PARSE-INTEGER` will now return `NIL`
;;; instead of signalling an error condition.
(defun prompt-for-int (msg)
(format *query-io* "~A" msg)
(finish-output *query-io*)
(parse-integer (read-line *query-io*) :junk-allowed t))
;;; Here is a function to prompt for input until some non-numeric value
;;; is given. The results are collected in a list and returned.
(defun prompt-for-ints (msg)
(format *query-io* "~A~%" msg)
(finish-output *query-io*)
(let (input-num)
(loop
do (setf input-num (prompt-for-int "Enter a number ENTER to quit: "))
while input-num
collecting input-num)))
;;; Some functions to calculate statistics:
(defun count-of-nums (xs)
(length xs))
(defun min-of-nums (xs)
(apply #'min xs))
(defun max-of-nums (xs)
(apply #'max xs))
(defun sum-of-nums (xs)
(apply #'+ xs))
(defun avg-of-nums (xs)
(float (/ (sum-of-nums xs)
(count-of-nums xs))))
;;; A function to prompt the user for input which returns a list of statistics.
(defun prompt-for-stats (msg)
(let ((data (prompt-for-ints msg)))
(list (count-of-nums data)
(min-of-nums data)
(max-of-nums data)
(sum-of-nums data)
(avg-of-nums data))))
;;; Accessor functions for a list of statistics:
(defun get-stats-count (stats)
(first stats))
(defun get-stats-min (stats)
(second stats))
(defun get-stats-max (stats)
(third stats))
(defun get-stats-sum (stats)
(fourth stats))
(defun get-stats-avg (stats)
(fifth stats))
;;; A function that prompts for input and displays results.
(defun prompt-for-stats-report ()
(let ((results (prompt-for-stats "Enter some integers to view statistics")))
(format *query-io* "Count: ~A~%" (get-stats-count results))
(format *query-io* "Minimum: ~A~%" (get-stats-min results))
(format *query-io* "Maximum: ~A~%" (get-stats-max results))
(format *query-io* "Sum: ~A~%" (get-stats-sum results))
(format *query-io* "Average: ~A~%" (get-stats-avg results))
(finish-output *query-io*)))
Sample interaction:
CL-USER> (prompt-for-stats-report)
Enter some integers to view statistics
Enter a number ENTER to quit: 1
Enter a number ENTER to quit: 2
Enter a number ENTER to quit: 1
Enter a number ENTER to quit:
Count: 3
Minimum: 1
Maximum: 2
Sum: 4
Average: 1.3333334