How to read input until EOF in Lisp - lisp

How do I read an input stream until EOF in Lisp? In C, you might do it like this:
while ((c = getchar()) != EOF)
{
// Loop body...
}
I would like to be able to pipe data to my Lisp programs without having to specify the data size in advance. Here's an example from something I'm doing now:
(dotimes (i *n*)
(setf *t* (parse-integer (read-line) :junk-allowed T))
(if (= (mod *t* *k*) 0) (incf *count*)))
In this loop, the variable *n* specifies the number of lines I'm piping to the program (the value is read from the first line of input), but I would like to just process an arbitrary and unknown number of lines, stopping when it reaches the end of the stream.

See the HyperSpec for READ-LINE
(loop for line = (read-line stream nil :eof) ; stream, no error, :eof value
until (eq line :eof)
do ... )
or sometimes with nil
(loop for line = (read-line stream nil nil)
while line
do ... )

read-line takes an optional argument (eof-error-p) allowing it to return either NIL (default) or a user-specified value (eof-value) on hitting an EOF, instead of signalling an error.
From Chapter 19 of Successful Lisp:
READ-LINE &optional stream eof-error-p eof-value recursive-p
In the read functions listed above, optional arguments EOF-ERROR-P and EOF-VALUE specify what happens when your program makes an attempt to read from an exhausted stream. If EOF-ERROR-P is true (the default), then Lisp will signal an error upon an attempt to read an exhausted stream. If EOF-ERROR-P is NIL, then Lisp returns EOF-VALUE (default NIL) instead of signalling an error.
You can use this as a simple termination condition for your function.

Related

returning a value in lisp programming

I have a function
(defun read-as-list (filename)
(defparameter parlist(list nil) )
(let ((in (open filename :if-does-not-exist nil)))
(when in
(loop for line = (read-line in nil)
while line do
(defparameter mylist line)
(push mylist (cdr (last parlist)))
;(append parlist (list mylist))
;(print mylist)
;(format t "~a~%" line)
)
(close in)
)
)
(print parlist)
(return-from read-as-list parlist)
)
which simply takes a file name and reads it into a nested list and returns the list
I call it in the down function like:
(defun test_on_test_data ()
(print "....................................................")
(print "Testing ....")
(print "....................................................")
(let (con (read-as-list "document1.txt"))
(print con)
)
)
(test_on_test_data)
in the function test-on-test-data, con prints nil and it does not call the function read-as-list
instead of printing the content of the files as list it prints nil.
can someone please help me out on this.
Here is an example function that you can use to test how to iterate over lines in a file. It takes a pathname designator and a callback function, and execute the function for all lines in a file. The function must accept a single parameter, the line being read.
(defun maplines (function path)
(with-open-file (in path)
(loop for line = (read-line in nil nil)
while line
do (funcall function line))))
Note how the code is indented, and how the opening and closing of the file is handled using with-open-file.
Also, there is no defparameter inside the function's body since that form is used to declare global variables.
There is no need to use return since the last value in the function body is automatically the value of the function call.
Then, you can for example call it as follows:
(maplines #'print "/tmp/test.data")
Unlike in your code, if the file does not exist, an error will be signaled. In your case you silently ignored errors by giving nil and doing nothing on a null stream.
Finally, all you need is to use a function that stores the lines being read. Or if you don't know yet how to do that, modify the above snippet to remove the call to funcall and use collect in the loop. You will get a list of all lines.

LISP break a function execution

How can I break a function execution in LISP if I get a certain value?
For example, I have a main function like this:
(defun recognize-a (arg input)
(if (equal (recognize-b arg input) '())
T
NIL
))
I want to break the function recognize-b in case the input is an empty list, without passing any values to the main function:
(defun recognize-b (fa input)
(if (equal input '())
<<<WANTED BREAK>>>
(<Else branch>)))
You can use ERROR to signal an error from RECOGNIZE-B when INPUT is empty.
(defun recognize-b (arg input)
(when (emptyp input)
(error "INPUT is empty!"))
;; Do whatever the function normally does...
:return-value-from-b)
I'll just return :RETURN-VALUE-FROM-B since I don't know what the function is supposed to do. You could define an error type to signal, but by default ERROR will signal a SIMPLE-ERROR.
To handle the error in RECOGNIZE-A, you can use HANDLER-CASE.
(defun recognize-a (arg input)
(handler-case (recognize-b arg input)
(simple-error () t)))
This simply returns the value from RECOGNIZE-B if there was no error, or T if there was.
(recognize-a 10 '(1 2)) ;=> :RETURN-VALUE-FROM-B
(recognize-a 10 '()) ;=> T
There is a good introduction to the condition system in the book Practical Common Lisp, Chapter 19. Beyond Exception Handling: Conditions and Restarts.

symbolic expression stream I/O

In Common Lisp, how can one read & write symbolic expressions from/to streams? For example, I might want to write an anonymous function to file and then read and funcall it:
;;; sexp-io.lisp
;;; Try writing a sexp to file and reading it back in
(with-open-file (file "~/Documents/Lisp/Concurrency/sexp.lisp"
:direction :output :if-exists :supersede)
(print #'(lambda () (+ 1 1)) file))
(with-open-file (file "~/Documents/Lisp/Concurrency/sexp.lisp"
:direction :input)
(read file))
However, that code results in dubious output
#<Anonymous Function #x3020018F950F>
which does result in an error when I try reading it back in:
> Error: Reader error on #<BASIC-FILE-CHARACTER-INPUT-STREAM ("/Users/frank/Documents/Lisp/Concurrency/sexp.lisp"/7 UTF-8) #x3020018F559D>, near position 3, within "
> #<Anonymous ":
> "#<" encountered.
> While executing: CCL::SIGNAL-READER-ERROR, in process Listener(4).
You are doing TRT, except for #' which turns the list (lambda () (+ 1 1)) into a function object. Just replace the sharp-quote (which is read as function) with a simple quote (which is read as quote) and it should work.
Another change you might want to make is replacing print with write with argument :readably t:
(write my-object :stream out :readably t)
The benefit of :readably is that it fails if it cannot write in a way that will preserve print-read consistency.

Reading file to list of lists using Common Lisp

I need to read a text file using Common Lisp. File must be read to list of lists. Each list in the list must consist of a line from file.
Now, I have done such code:
(with-open-file (in file)
(loop for line = (read-line in nil nil)
while line
collect (coerce line 'list)))
But, for example, the rusult looks as: ((#\0 #\0 #\0) (#\1 #\0 #\1)). But I need to have result without #\ characters: ((0 0 0) (1 0 1)). How to fix it?
You are already converting the line to a list of characters;
all you need to do is convert the characters to numbers:
(with-open-file (in file)
(loop for line = (read-line in nil nil)
while line
collect (map 'list #'digit-char-p line)))
You can also use (parse-integer (string c)) instead of digit-char-p, but that seems an overkill.

How to explicitly use a standard function?

I'm running into a name collision with iterate and count standard function in the example below:
(defun svs-to-images (file)
(with-open-file (stream file)
(iterate:iter
(iterate:for line #:= (read-line stream nil nil))
(iterate:while line)
(line-to-image
(iterate:iter
(iterate:for c #:in-string line)
(iterate:with word)
(iterate:with pos #:= 0)
(iterate:with result #:= ; ---------\/ here
(make-array (list (1+ (count #\, line)))
:element-type 'fixnum))
(if (char= c #\,)
(setf (aref result pos)
(parse-integer
(coerce (reverse word) 'string))
pos (1+ pos)
word nil)
(setf word (cons c word)))
(iterate:finally result)) 28))))
The error I'm getting is:
csv-parser.lisp:19:5:
error:
during macroexpansion of
(ITERATE:ITER
(ITERATE:FOR LINE #:= ...)
(ITERATE:WHILE LINE)
...).
Use *BREAK-ON-SIGNALS* to intercept:
Iterate, in (COUNT , LINE):
Missing value for LINE keyword
Compilation failed.
And, if I understood it correctly, it is trying to use count as if it was the count driver from iterate, instead of the original function. How would I make it so that the correct count is used?
In comp.lang.lisp Chris Riesbeck offered this as a workaround for a similar question a few years ago:
(remprop 'count 'iter::synonym)
From then you need to use COUNTING as the iterate clause. CL:COUNT then should refer to the Common Lisp function. You would need to recompile the code.
This is a bug/feature of how iterate processes its body.
You can use a version of iterate from rutils - it uses keywords instead of plain symbols, so there will be no symbol clashes.