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:
Avoid using a global parameter at all
Call READ-LINE instead of READ to get the count of input elements
Use *QUERY-IO* consistently
Call FINISH-OUTPUT consistently
Use better variable names (A and NUM are not very descriptive here)
Use ~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
Related
It's a simple college problem. I have to get the result using the BMI calc
My code below:
(write-line "BMI CALC")
(defun calc nil
(prog (w h) ; define p e h as local variables init with nil
(print "Weight: ")
(setq w (read))
(print "Height: ")
(setq h (read))
(return (/ w (* h h)))
)
)
(format t "BMI: ~D~%" (calc))
(setq bmi calc)
(cond
((< bmi 18.5) (print "Under weight"))
((< bmi 24.9) (print "Normal weight"))
((< bmi 29.9) (print "Overweight"))
((< bmi 34.9) (print "Obesity 1"))
((< bmi 39.9) (print "Obesity 2"))
(t (print "Obesity 3"))
)
And I got this result below:
BMI CALC
"Weight: " 78
"Height: " 1.7
BMI: 26.989618
*** - SETQ:variable CALC has no value
I really don't understand why this error.
I expected to print the BMI result, like "Under weight" or "Obesity 1".
What value do you think variable calc has on this line: (setq bmi calc)?
Because, as that error says, it doesn't have any.
Also, ending parentheses belong to the same line and you should read about let (a special operator for creating local variables).
Here is an improved version, which you can test by calling (my-bmi):
(defun calc ()
(prog (w h)
(print "Weight: ")
(setq w (read *query-io*))
(print "Height: ")
(setq h (read *query-io*))
(return (/ w (* h h)))))
(defun my-bmi ()
(print "BMI CALC")
(let ((bmi (calc)))
(format t "BMI: ~D~%" bmi)
(print
(cond
((< bmi 18.5) "Under weight")
((< bmi 24.9) "Normal weight")
((< bmi 29.9) "Overweight")
((< bmi 34.9) "Obesity 1")
((< bmi 39.9) "Obesity 2")
(t "Obesity 3")))))
Remarks about your code
You are using a global variable and writing the whole assignment as a script: namely you call (setq bmi ...) where bmi is not introduced by a let. And there is very few reusable parts. It's a bit of a bad practice to have side-effects like that. Here you are only writing a small program for an assignment so it is not very bad but, as you are also learning programming, you should also try to structure your code as small functions that don't have side-effects. This would be cleaner and would help the program grow in a real situation.
This is related to the previous point, but you are mixing different things in functions: parsing input, computing the BMI, outputting the result. In practice we often need to architecture code into layers: here you can have a pure mathematics data layer that computes the BMI, a input/ouptut layer that prompt for values, and a main program that glues all of those parts together.
Regarding I/O, you are likely to experience difficulties later if you don't flush the output or clear the input streams during your interactions with the user: as streams are typically buffered, sometimes format won't display immediately a string and this will be confusing. Using force-output is a way to flush all the buffers and ensure the user sees the text you wrote.
Alternative implementation
This is an example of how I would do it, using intermediate functions to encapsulate different tasks.
For example:
(defun bmi-formula (&key height weight)
(/ weight (* height height)))
This is a function that you can invoke interactively as follows in the REPL:
CL-USER> (bmi-formula :height 1.80 :weight 60)
18.51852
Notice that it only computes a value from numerical values, there is no parsing involved.
Likewise, you can convert a BMI to an obesity judgment as follows (as an aside, using BMI for that is an outdated practice, I am a bit suprised/annoyed that you are being asked to write this function):
(defun bmi-obesity-judgment (bmi)
(cond
((< bmi 18.5) 'underweight)
;; etc.
))
Here the function takes a bmi and returns a symbol. If you want later to localize your application in another language, you can map each symbol to a word in another language (e.g. (ecase judgment (underweight "magro") ...) please forgive my attempt at Portuguese).
Now you can write a function that grabs input from the user. Typically I would write an auxiliary function as follows:
(defun prompt (message type)
(loop
(clear-input *query-io*)
(fresh-line *query-io*)
(write-string message *query-io*)
(let ((value (read *query-io*)))
(when (typep value type)
(return value))
(warn "~a is not of type ~a" value type))))
What it does is ensure the input is cleared (there is no pending/buffered values to be read), it prints a new-line if necessary, a custom message, flush the output stream, then read a value and check if that value matches a given type. If that's not the case the code is executed in a loop.
For example, you can produce a property list as follows:
(defun input-bmi-parameters ()
(list
:weight (prompt "Weight (kgs): " '(integer 0 1000))
:height (prompt "Height (meters): " '(real 0 5))))
Finally you can piece all the functions together, write a title, write the output, etc:
(defun bmi-program ()
(format *query-io* "~&BMI Calculator. Enter weight and height and be judged.~%")
(let ((parameters (input-bmi-parameters)))
...))
Notice how I am using let to introduce a local variable parameters bound to a list (see also in prompt where I use local variable value). Apart from interacting with the *query-io*, the code does not have side-effects, all the state is contained in the function. That means that you could run more than one bmi-program in parallel, without them messing around with the same set of global variables: you could have a server where each connection executes bmi-program in a different thread, in which *query-io* is bound to the current TCP stream.
I am supposed to write a program that gets simple user input as a string and the code supposed to writes back a response (name, are you a person etc.) The program suppose to terminate when word 'bye' is typed. The code is below:
(defun x()
(setq words "empty")
(loop while (string/= words "BYE")
(setq words (read-delimited-list #\~)
(write-line words)
(format t "ROBBIE%: Hello, who are you?")
(case (string-include "I'm" words)
(format t "ROBBIE%: Nice to see you, how are you?")
((string-include "Hi" words)
(format t "ROBBIE%: How are you?")
(or (string-include "fine" words) (string-include "person" words))
(format t "ROBBIE%: No I'm a computer")))
(format t "BYE"))
(x)
However, when I compile this on program 2 errors pop up:
Line2:3 warning: undefined variable: COMMON-LISP-USER:: WORDS
Line3:3 error: during macroexpansion of (LOOP WHILE (STRING/= WORDS "BYE") ...). Use BREAK-ON-SIGNALS to intercept.
I've done programming in python but this is extremely complicated lang for me and I need some help understanding why this isn't working? Any advice is greatly appreciated!
When you do this:
(defun x ()
(setf words "foo"))
then words is not defined. It references some global variable, and if that doesn't exist, it will create it, but possibly with some strange behaviour regarding scope and extent. This is not portable code.
In order to introduce a local variable, use let:
(defun x ()
(let ((words "foo"))
;; words is is scope here
)
;; but not here
)
Loop (in the more usual »extended« form) uses loop keywords for all its clauses. There is no implicit body. In order to do something, you might use do, which allows multiple forms to follow:
(defun x ()
(let ((words "foo"))
(loop while (string/= words "bye")
do (setf words (read-line …))
(format …))))
Case uses compile-time values to compare using eql:
(case something
(:foo (do-a-foo))
((:bar :baz) (do-a-bell))
(t (moooh)))
This doesn't work with strings, because strings are not eql unless they are the same object (i. e. they are eq). In your case, you want a cond:
(cond ((string-include-p words "Whatever")
…)
((string-include-p words "yo man")
…))
For interaction with the user, you'd maybe want to use the bidirectional *query-io* stream:
(format *query-io* "Who am I?")
and
(read-line *query-io*)
Read-line gives you strings, and seems much better suited to your task than read-delimited-list, which has other use cases.
Let me focus on aspects of your code not already covered by other solutions.
Loop
Here is your loop structure:
(let ((words "empty"))
(loop
while (string/= words "BYE")
do
(progn
(setq words (read-line)))))
First of all, after do you don't need (progn ...). You could write equally:
(let ((words "empty"))
(loop
while (string/= words "BYE")
do (setq words (read-line))))
Having to initialize words to some arbitrary value (called sometime a sentinel value) is a code smell (not always a bad thing, but there might be better alternatives). Here you can simplify the loop by using a for clause.
(loop
for words = (read-line)
while (string/= words "BYE")
do ...)
Also, you may want to use until with a string= test, this might be more readable:
(loop
for words = (read-line)
until (string= words "BYE")
do ...)
Search
You can test for string inclusion with SEARCH. Here is a little commented snippet of code to showcase how string manipulation function could work:
(defun test-I-am (input)
(let ((start (search "I'am" input)))
(when start
;; we found an occurrence at position start
;; let's find the next space character
(let ((space (position #\space input :start start)))
(when space
;; we found a space character, the name starts just after
(format nil "Hello ~a!" (subseq input (1+ space))))))))
With this simple algorithm, here is a test (e.g. in your REPL):
> (test-i-am "I'am tired")
"Hello tired!"
Replace read-delimited-list with read-line, case with cond and balance some parentheses. Here is working solution, including some function for string-inclusion:
(defun string-include (string1 string2)
(let* ((string1 (string string1)) (length1 (length string1)))
(if (zerop length1)
nil
(labels ((sub (s)
(cond
((> length1 (length s)) nil)
((string= string1 s :end2 (length string1)) string1)
(t (sub (subseq s 1))))))
(sub (string string2))))))
(defun x ()
(let ((words "empty"))
(format t "ROBBIE%: Hello, who are you?~%")
(loop while (string/= words "BYE") do
(progn
(finish-output)
(setq words (read-line))
(cond ((string-include "I'm" words)
(format t "ROBBIE%: Nice to see you, how are you?~%"))
((string-include "Hi" words)
(format t "ROBBIE%: How are you?~%"))
((or (string-include "fine" words)
(string-include "person" words))
(format t "ROBBIE%: No I'm a computer~%")))))
(format t "BYE")))
Then you just call it:
(x)
I'm very new to Lisp and am trying to write a program that simply asks a user to enter 3 numbers and then sums them and prints the output.
I've read that you can you a function like:
(defvar a)
(setq a (read))
To set a variable in Lisp, but when I try to compile my code using LispWorks I get the following error:
End of file while reading stream #<Concatenated Stream, Streams = ()>
I feel like this should be relatively simple and have no idea where I'm going wrong.
I've not worked with LispWorks, so it's only a guess.
When compiler traverses your code it gets to the line (setq a (read)), it tries to read input, but there is no input stream while compiling, thus you get an error.
Write a function:
(defvar a)
(defun my-function ()
(setq a (read))
It should work.
This should evaluate properly in your Lisp:
(defun read-3-numbers-&-format-sum ()
(flet ((prompt (string)
(format t "~&~a: " string)
(finish-output)
(read nil 'eof nil)))
(let ((x (prompt "first number"))
(y (prompt "second number"))
(z (prompt "third number")))
(format t "~&the sum of ~a, ~a, & ~a is:~%~%~a~%"
x y z (+ x y z)))))
Simply evaluate the above function definition, then run the form:
(read-3-numbers-&-format-sum)
at your LispWorks interpreter.
I am having some trouble working out how to return a line of text if a match is found.
(set 'wireshark "http://anonsvn.wireshark.org/wireshark/trunk/manuf")
(set 'arptable (map (fn (x) (parse x " ")) (exec "arp -a")))
(define (cleanIPaddress x)
(slice x 1 -1))
(define (cleanMACaddress x)
(upper-case (join (slice (parse x ":") 0 3) ":")))
(define (addIPandMACaddress x)
(list (cleanIPaddress (nth 1 x)) (cleanMACaddress (nth 3 x))))
(set 'arplist (map addIPandMACaddress arptable))
(set 'routerMAC (last (assoc (exec "ipconfig getoption en1 router") arplist)))
(find-all routerMAC (get-url wireshark))
returns
("20:AA:4B")
so I know that the code "works"
but I would like to retrieve the full line of text
"20:AA:4B Cisco-Li # Cisco-Linksys, LLC"
This can be performed simply by using a string-split procedure that allows us to use remove-if (the Common Lisp version of filter) to search through a string split by newlines removing any lines that do not contain the string we are searching for. That would result in a list of every line containing the string. The functions we will define here are already available via various Common Lisp libraries, but for the education purposes, we will define them all ourselves. The code you need works like so:
; First we need a function to split a string by character
(defun string-split (split-string string)
(loop with l = (length split-string)
for n = 0 then (+ pos l)
for pos = (search split-string string :start2 n)
if pos collect (subseq string n pos)
else collect (subseq string n)
while pos))
; Now we will make a function based on string-split to split by newlines
(defun newline-split (string)
(string-split "
" string))
; Finally, we go through our text searching for lines that match our string.
; Make sure to replace 'needle' with the string you wish to search for.
(remove-if #'(lambda (x)
(equal 'nil (search (string-upcase "needle")
(string-upcase x))))
(newline-split haystack))
You should be able to apply this strategy to the code you posted with a few small modifications. This code was tested on SBCL 1.0.55.0-abb03f9, an implementation of ANSI Common Lisp, on Mac OS X 10.7.5.
In the end I used:
(find-all (string routerMAC ".*") (get-url wireshark))
I want to get output like 0 1, but the code below just print nil. I use type-of to test the (first hex), it is integer. %d should work, right? If I use message, it works in Emacs.
(defun draw-board (board)
(loop for x below 2
for hex = (aref board x)
do (format "%d " (first hex))))
(draw-board [(0 2) (1 2) (0 3) (0 2)])
1- emacs lisp format is not Common Lisp format. Notice the missing
argument!
(format "%d" 42) == (cl:format NIL "~D" 42)
2- therefore the only things your loop does are:
- to check that board is a vector with at least two slots. (aref
signals an error if the index is out of bound).
- to check that each of those two slots are lists (first signals an
error when passed a non list).
- to check that the first element of each each of those two slots
are numbers. (format signals an error when you pass a non number
for %d).
That's all.
You never said you wanted to print anything.
To print something, you must put it in a buffer, and use
ps-print-buffer:
(defun draw-board (board)
(with-temp-buffer
(loop for x below 2
for hex = (aref board x)
do (insert (format "%d " (first hex))))
(ps-print-buffer)))