with-open-file explanation in layman terms - lisp

I'm learning CL, and I have minimal experience in other languages. Could someone explain to me in layman terms what this means, especially what "out" here represents, and how it all fits together:
(defun save-db (filename)
(with-open-file (out filename
:direction :output
:if-exists :supersede)
(with-standard-io-syntax
(print *db* out))))
Mostly, the bit I don't understand is "out", but an explanation of the whole thing would be nice.
Thanks

out is the stream variable bound to the open file.
with-open-file guarantees that the file is open inside the scope, and closed
outside the scope, no matter how you exit.

As an addition to ddyer, you can also use MACROEXPAND or MACROEXPAND-1 to see what WITH-OPEN-FILE does:
(macroexpand '(with-open-file (out filename
:direction :output
:if-exists :supersede)
(with-standard-io-syntax
(print *db* out))))
tells us
(LET ((OUT (OPEN FILENAME :DIRECTION :OUTPUT :IF-EXISTS :SUPERSEDE)) (#:G748 T))
(UNWIND-PROTECT
(MULTIPLE-VALUE-PROG1 (PROGN (WITH-STANDARD-IO-SYNTAX (PRINT *DB* OUT)))
(SETQ #:G748 NIL))
(WHEN OUT (CLOSE OUT :ABORT #:G748))))
We can see that we open the file called filename and assign that open file stream to out , and do something. Should something bad happen, UNWIND-PROTECT will CLOSE the stream, should it be non-nil.
The #:G748 variable is a GENSYMed symbol (so it's a fresh, uninterned, symbol). If nothing goes wrong writing the file, we set #:G748 to nil.
Thus, when we CLOSE the stream, if something went wrong #:G748 will be T, so CLOSE will attempt to clean up any side effects of having created the stream.

Related

elisp: Handling wrong-type-argument exception

I want to kill all buffers visiting a file in a given directory, keeping all others. However, my function bombs out when it reaches the #<buffer *Minibuf-1*>; the minibuffer isn't visiting a file.
(defun my-kill-all-visiting-buffers (dir)
"Kill all buffers visiting DIR."
(interactive)
(mapcar
(lambda (buf)
(and (string-match-p
(regexp-quote dir)
(file-name-directory (buffer-file-name buf)))
(kill-buffer buf)))
(buffer-list)))
In Python-land, I would ask for forgiveness and wrap it in a try-except. How would I handle this in the land of lisp?
You could handle the exception with something like condition-case which elisp has. Or you could just do something like this (avoiding the regexp-quotery as well):
(defun my-kill-all-visiting-buffers (dir)
"Kill all buffers visiting DIR or any subdirectory of DIR"
(interactive "Ddirectory: ")
(mapc
(lambda (buf)
(let ((bfn (buffer-file-name buf)))
(when (and (not (null bfn))
(file-in-directory-p bfn dir))
(kill-buffer buf))))
(buffer-list)))
There are probably better approaches.
Notes
(not (null x)) is the same as x but I use it intentionally so when I read it I know I am checking for x not being a conceptually void value, rather than it just being false.
This avoids regexp operations (originally string= now string-prefix-p), because, famously:
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
Instead it uses file-in-directory-p which asks exactly the question it wants the answer to: is file in a directory or any subdirectory of dir? (Thanks to Stefan for this, which I didn't know about.)
All of the above is legal in Emacs 26.2: I don't keep track of which things in elisp changed & arrived when any more.
Elisp is not that close to many other dialects of Lisp in use other than rather superficially: techniques which are used in elisp may or may not apply to other languages in the family but often don't.
I found that it wasn't necessary to catch all possible errors. Handling the case where a buffer is not associated with a file is sufficient.
The use of regexp-quote is used to handle closing files in subdirectories.
(defun my-kill-all-visiting-buffers (dir)
"Kill all buffers visiting DIR."
(interactive)
(mapcar
(lambda (buf)
(let ((bfn (buffer-file-name buf)))
(and (not (null bfn))
(string-match-p (regexp-quote dir) (file-name-directory bfn))
(kill-buffer buf))))
(buffer-list)))

Suppress output from print function in Lisp

I'm fairly new to Lisp, and I've run into a printing issue. I have one function which does printing to the standard output (among other things). I want to then run this function through another function where it still runs the same but instead nothing gets printed to the standard output.
Here is a simple example of what I mean. I have the following two functions described:
(defun does-printing()
(print "This goes to standard output."))
(defun run-other-function (function)
(funcall function)
(values))
Here's a dribble of what happens when I run this,
;; Dribble of #<IO TERMINAL-STREAM> started on 2014-10-05 21:49:49.
#<OUTPUT BUFFERED FILE-STREAM CHARACTER #P"example.out">
[7]> (run-other-function #'does-printing)
"This goes to standard output."
[8]> (dribble)
;; Dribble of #<IO TERMINAL-STREAM> finished on 2014-10-05 21:50:09.
Note that the printing function still prints to the standard output. It'd like to be able to suppress this printing somehow when running does-printing through run-other-function. I have tried many different variations of phrasing of my problem when searching for solutions, but nothing is getting at what I would like to do.
The simplest solution is to create an empty broadcast stream.
(with-open-stream (*standard-output* (make-broadcast-stream))
(call-some-function-1)
...
(call-some-function-n))
If a broadcast stream has no component stream all output will be discarded. Above binds the *standard-output* to such a stream. This does not cons up any data and it is portable.
You can just redirect your standard-output to some place. For example into /dev/null if you have one in your operating system. It looks like very idiomatic UNIX-way output suppressing.
Note, that you shouldn't set it to NIL, because print will signal type error in this case.
(defun does-printing()
(print "This goes to standard output."))
(defun run-other-function (function)
(with-open-file (*standard-output*
"/dev/null"
:direction :output
:if-exists :supersede)
(funcall function)
(values)))
CL-USER> (run-other-function #'does-printing)
; No value
Other option (and it may be better) is to use with-output-to-string, so you can capture this output value or just ignore it. Is think it's better, because why to do IO if we don't need it, and also it must work on any OS.
(defun run-other-function (function)
(with-output-to-string (*standard-output*
(make-array '(0)
:element-type 'base-char
:fill-pointer 0 :adjustable t))
(funcall function)
(values)))
If you doing it a lot, you can wrap it into macro or even function, to use in place of funcall.
(defun does-printing()
(print "This goes to standard output.")
"My result")
(defun funcall-with-no-output (fn)
(with-output-to-string (*standard-output*
(make-array '(0)
:element-type 'base-char
:fill-pointer 0 :adjustable t))
(funcall fn)))
CL-USER> (funcall-with-no-output #'does-printing)
"My result"
But i think macro will be more general and idiomatic for this case (may be I'm wrong).
(defmacro with-suppressed-output (&body body)
`(with-output-to-string (*standard-output*
(make-array '(0)
:element-type 'base-char
:fill-pointer 0 :adjustable t))
,#body))
So you can call many forms in with-suppressed-output.

Lisp: reading file containing url

I'm new to Lisp and have a quite basic question.
I'm working with a list containing lists.
(defvar *stationen* nil)
(defun make-station (name uri)
(list :name name :uri uri))
(defun save-db ()
(with-open-file (out "list.txt"
:direction :output
:if-exists :supersede)
(with-standard-io-syntax
(princ *stationen* out))))
(defun load-db ()
(with-open-file (in "list.txt")
(with-standard-io-syntax
(setf *stationen* (READ in)))))
(load-db)
The data contains an uri http://www..... Well, I get the following error, when I try to read that list:
The name "HTTP" does not designate any package.
[Condition of type SB-KERNEL:SIMPLE-PACKAGE-ERROR]
I can guess why this happens (Lisp is trying to interpret "http" as a package, right?). But how can I save my uri to the file and read it again, without Lisp throwing this error?
Alternatives:
Just write it as a string.
use READ-CHAR, READ-LINE or READ-SEQUENCE
advanced: write and read using a custom reader macro
Btw., please post formatted Lisp code.
Alternatively, you can use PRINT instead of PRINC to write out the URL.

Get files in directory, print each by lines

I'm porting some of my python scripts to Common Lisp. I need to get list of files in some directory and print each file's content by lines.
This code shows me all filenames. But lines are printed only for last file. Why?
Also, what is the best way ti iterate by file lines?
Thanks.
(dolist (file (directory (make-pathname :name :wild
:type :wild
:defaults "path\\to\\files\\")))
(print file)
(with-open-file (stream file)
(do ((line (read-line stream) (read-line stream)))
(nil t)
(print line))))
I would propose to write a function which prints a file given a pathname and a stream.
You iterate with DO. That's okay, but I would use something like LOOP which allows slightly easier to read code.
Your DO is an endless loop. You might want to end the loop when the EOF is reached.
READ-LINE generates an error when it reads past the end of the file. Thus your code signals an error on the end of the first file. This error causes your code to only print the first file.
You need to call READ-LINE such a way that you test for EOF and end the iteration then. See the arguments to READ-LINE. Alternatively you can catch the EOF error, but the other solution is slightly easier.
This seems to work for me:
(dolist (file (directory (make-pathname :name :wild
:defaults "/tmp/lt/files/")))
(print file)
(with-open-file (stream file)
(do ((line (read-line stream nil) (read-line stream nil)))
((null line))
(print line))))

Print output into a file or not print output?

I'd like to save or ignore outputs when I execute a specific function in lisp. I use Emacs and CCL. For example,
(defun foo (x) (format t "x = ~s~%" x))
and if I execute the function, it prints out "x = 5". But I don't want to printout in a buffer, because if I have a large amount of iteration, the speed of simulation will be decreased.
Any idea?
You can temporarily redirect standard output by binding *standard-output* to a stream. For example, a broadcast stream with no output streams will serve as a black hole for output:
(let ((*standard-output* (make-broadcast-stream)))
(foo 10)
(foo 20))
;; Does not output anything.
You can also do this with other binding constructs, such as with-output-to-string or with-open-file:
(with-output-to-string (*standard-output*)
(foo 10)
(foo 20))
;; Does not print anything;
;; returns the output as a string instead.
(with-open-file (*standard-output* "/tmp/foo.txt" :direction :output)
(foo 10)
(foo 20))
;; Does not print anything;
;; writes the output to /tmp/foo.txt instead.
Instead of t as the first argument to format, you can give it an output file stream and your output for that statement will be sent to that file stream.
However having excessive disk I/O will also will increase your running time, hence you can consider having two modes like a debug and a release mode for your program where the debug mode prints all the diagnostic messages and the release mode does not print anything at all.
I'm not sure I understand your question, but the second argument to format is a stream. If you set it to t it prints to standard output, but you can also set it to an open file.
So something like this would allow you to select where the output goes:
;;output to file:
(setf *stream* (open "myfile" :direction :output
:if-exists :supersede)
;;alternative to output to standard output:
;;(setf *stream* t)
(defun foo (x) (format *stream* "x = ~s~%" x))
(foo 10)
(close *stream*) ;; only if output sent to a file