lisp: capture stdout and stderr, store it in separate variables - lisp

I have a function which returns a value and prints data to stdout and stderr. I cannot modify this function. I would now like to execute this function, capturing the data printed to stdout and stderr, storing it in two separate variables. If possible, I'd also like to store the function's return value in a third variable.
I've come accross (with-output-to-string (*standard-output*) ...) but this won't let me capture both stdout and stderr. What options do I have?

You could just use let to bind the streams to output string streams. For example:
(defun print-stuff (x y)
(format t "standard output ~a" x)
(format *error-output* "error output ~a" y)
(+ x y))
(defun capture (x y)
(let ((*standard-output* (make-string-output-stream))
(*error-output* (make-string-output-stream)))
(values (print-stuff x y)
(get-output-stream-string *standard-output*)
(get-output-stream-string *error-output*))))
(capture 43 12)
; => 55
; "standard output 43"
; "error output 12"

Related

Lisp - Functions passed into another function as arguments and called from within a Let

I am learning Lisp and, just for practice/education, am trying to define a function that will
ask the user to enter a number until they enter an integer > 0 [copied from Paul Graham's Ansi Common Lisp]
print that number and subtract 1 from it, repeat until the number hits 0, then return.
I am trying to do this via passing 2 functions into a higher-order function - one to get the number from the user, and another recursive [just for fun] function that prints the number while counting it down to 0.
Right now my higher-order function is not working correctly [I've tested the first 2 and they work fine] and I cannot figure out why. I am using SBCL in SLIME. My code for the 3 functions looks like this:
(defun ask-number ()
(format t "Please enter a number. ")
(let ((val (read))) ; so val is a single-item list containing the symbol 'read'?
(cond ; no here read is a function call
((numberp val)
(cond
((< val 0) (ask-number))
(T val)))
(t (ask-number))))))
(defun count-down (n)
(cond
((eql n 0) n)
(t
(progn
(format t "Number is: ~A ~%" n)
(let ((n (- n 1)))
(count-down n))))))
(defun landslide (f1 f2)
(let (x (f1))
(progn
(format t "x is: ~A ~%" x)
(f2 x)))))
but calling slime-eval-defun in landslide yields:
; SLIME 2.27; in: DEFUN LANDSLIDE
; (F1)
;
; caught STYLE-WARNING:
; The variable F1 is defined but never used.
; (SB-INT:NAMED-LAMBDA LANDSLIDE
; (F1 F2)
; (BLOCK LANDSLIDE
; (LET (X (F1))
; (PROGN (FORMAT T "x is: ~A ~%" X) (F2 X)))))
;
; caught STYLE-WARNING:
; The variable F1 is defined but never used.
;
; caught STYLE-WARNING:
; The variable F2 is defined but never used.
; in: DEFUN LANDSLIDE
; (F2 X)
;
; caught STYLE-WARNING:
; undefined function: COMMON-LISP-USER::F2
;
; compilation unit finished
; Undefined function:
; F2
; caught 4 STYLE-WARNING conditions
I have tried several [what I consider] obvious modifications to the code, and they all fail with different warnings. Calling the function like (landslide (ask-number) (count-down)), ask-number prompts for user input as expected, but then SLIME fails with
invalid number of arguments: 0
[Condition of type SB-INT:SIMPLE-PROGRAM-ERROR]
I know I have to be missing something really obvious; can someone tell me what it is?
First: You are missing a set of parens in your let:
You have (let (x (f1)) ...) which binds 2 variables x and f1 to nil.
What you want is (let ((x (f1))) ...) which binds 1 variable x to the values of function call (f1)
Second: Common Lisp is a "lisp-2", so to call f2 you need to use funcall: (funcall f2 ...).
Finally: all your progns are unnecessary, and your code is hard to read because of broken indentation, you can use Emacs to fix it.
Before I reach an error in landslide, there are some notes about this code:
Your first function is hard to read- not just because of indentation, but because of nested cond.
You should always think about how to simplify condition branches- using and, or, and if you have only two branches of code, use if instead.
There are predicates plusp and minusp.
Also, don't forget to flush.
I'd rewrite this as:
(defun ask-number ()
(format t "Please enter a number. ")
(finish-output)
(let ((val (read)))
(if (and (numberp val)
(plusp val))
val
(ask-number))))
Second function, count-down.
(eql n 0) is zerop
cond here has only two branches, if can be better
cond has implicit progn, so don't use progn inside cond
let is unnecessary here, you can use 1- directly when you call count-down
Suggested edit:
(defun count-down (n)
(if (zerop n) n
(progn
(format t "Number is: ~A ~%" n)
(count-down (1- n)))))
Also, this function can be rewritten using loop and downto keyword, something like:
(defun count-down (n)
(loop for i from n downto 0
do (format t "Number is: ~A ~%" i)))
And finally, landslide. You have badly formed let here and as Common Lisp is Lisp-2, you have to use funcall. Note that let has also implicit progn, so you can remove your progn:
(defun landslide (f1 f2)
(let ((x (funcall f1)))
(format t "x is: ~A ~%" x)
(finish-output)
(funcall f2 x)))
Then you call it like this:
(landslide #'ask-number #'count-down)

Read stdin to string in Newlisp

How do you read the entire contents of standard input into a string in Newlisp? (i.e the entire remaining contents after the current read position - this operation is commonly called "slurp file")
You can use this:
(define (read-all)
(let (r "" ch "")
(while (setf ch (read-char))
(setf r (append r (char ch))))
r))
See also: http://www.newlisp.org/downloads/newlisp_manual.html#read-char

How to read user input in Lisp

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.

Convert char to number

I'm in the process of reading a flat file - to use the characters read I want to convert them into numbers. I wrote a little function that converts a string to a vector:
(defun string-to-vec (strng)
(setf strng (remove #\Space strng))
(let ((vec (make-array (length strng))))
(dotimes (i (length strng) vec)
(setf (svref vec i) (char strng i)))))
However this returns a vector with character entries. Short of using char-code to convert unit number chars to numbers in a function, is there a simple way to read numbers as numbers from a file?
In addition to Rainer's answer, let me mention read-from-string (note that Rainer's code is more efficient than repeated application of read-from-string because it only creates a stream once) and parse-integer (alas, there is no parse-float).
Note that if you are reading a CSV file, you should probably use an off-the-shelf library instead of writing your own.
Above is shorter:
? (map 'vector #'identity (remove #\Space "123"))
#(#\1 #\2 #\3)
You can convert a string:
(defun string-to-vector-of-numbers (string)
(coerce
(with-input-from-string (s string)
(loop with end = '#:end
for n = (read s nil end)
until (eql n end)
unless (numberp n) do (error "Input ~a is not a number." n)
collect n))
'vector))
But it would be easier to read the numbers directly form the file. Use READ, which can read numbers.
Note that read-like functions are affected by reader macros.
Pick an example:
* (defvar *foo* 'bar)
*FOO*
* (read-from-string "#.(setq *foo* 'baz)")
BAZ
19
* *foo*
BAZ
As you can see read-from-string can implicitly set a variable. You can disable the #. reader macro by setting *read-eval* to nil but anyway if you have only integers on the input then consider using parse-integer instead.

Confused about format function in elisp

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)))