Format control strings can be quite ugly, and some common
formatting tasks are ugly out of proportion to the complexity of
what is being asked. So I have written out
. It is
inspired somewhat by the
YTools
out
macro for Common Lisp, but right now owes more to
C++.
I was inspiried to search for something nicer when I produced the
following:
(define (seconds->sql-timestamp s)
(match-let ((#(seconds minutes hours mday month year wday yday dst timezone)
(seconds->local-time s)))
(format "~d-~2,'0,d-~2,'0,d ~2,'0,d:~2,'0,d:~2,'0,d"
(+ year 1900) (+ month 1) mday hours minutes seconds)))
Isn't that format control string lovely? I just wanted
"2005-03-10 12:33:05"
.
This little out
makes simple things simple. If you
need to do anything too tricky, you'll need to revert to
format
. I use format
quite a bit in
out
itself.
Here is source (it is not currently set up to be compiled as a module). This is early code. Use with caution.
In the simplest case out
is just like the Chicken
print
. It will take all the arguments and send them to
the current output port:
#;5> (out 1 'wow 2 '(a b c) #\newline)
1wow2(a b c)
#;6>
Notice that there are no spaces between items, nor is a newline produced by default.
out
's more sophisticated operations are handled by
keywords which I call "maninpulators" due to the C++ ancestry of some
of the design. Some of the maninpulators take an argument, which will
be the item after them, some do not. The simplest is sugar:
#:nl
outputs a newline:
#;6> (out 1 'wow 2 '(a b c) #:nl)
1wow2(a b c)
#;7>
You can change the base for exact numbers:
#:dec
decimal
#:bin
binary
#:oct
octal
#:hex
hexidecimal
#;8> (out 33 " " #:bin 33 " " #:oct 33 " " #:hex 33 #:nl) 33 100001 41 21 #;9> (out #:bin 3 " " 5 " " 128 #:nl) 11 101 10000000 #;10>
Notice that the base maninpulator stays in effect for the rest of
the out
stream.
There are parallel sets of manipulators for fixing the width and
setting the pad character for columnar output. One set is for the
numbers and one for everything else. To set the width for numbers use
#:number-width
(or #:nwidth
- they operate
the same).
#;11> (out #:number-width 5 33 #:nl 123 #:nl 3.7 #:nl)
00033
00123
003.7
#;12>
Notice that the default fill character is zero. If you want
spaces, use #:number-fillchar
to change it:
#;12> (out #:number-width 5 #:number-fillchar " "
33 #:nl 123 #:nl 3.7 #:nl)
33
123
3.7
#;13>
I now have the tools to produce a much nicer version of the SQL
timestamp formatter I cited at the beginning:
(define (seconds->sql-timestamp s)
(match-let ((#(seconds minutes hours mday month year wday yday dst timezone)
(seconds->local-time s)))
(out #:number-width 2 #:number-fillchar 0
(+ year 1900) "-" (+ month 1) "-" mday " "
hours ":" minutes ":" seconds #:nl)))
#;14> (seconds->sql-timestamp (file-modification-time ".")) 2005-02-25 08:35:22 #;15>
Of course, I'd want to wrap it in
with-output-to-string
most of the time.
To set the column width and fill character for other types, just
use #:width
and #:fillchar
#;15> (out #:width 10 #:fillchar #\x
"nifty" #:nl "spam" #:nl "eggs" #:nl)
xxxxxnifty
xxxxxxspam
xxxxxxeggs
#;16>
All manipulators
#:nl
output a newline
#:dec
output exact numbers in decimal
#:bin
output exact numbers in binary
#:oct
output exact numbers in octal
#:hex
output exact numbers in hexidecimal
#:precision n
- floating-point precision
#:width n
- column width for non-numbers
#:fillchar c
- fill character for non-numbers; default space
#:number-width n
- column width for numbers
#:number-fillchar c
- fill character for numbers; default '0'
#:reset
reverts to defaults
Due to my limited ability to comprehend everything about the
details of format
columnar output is only right
justified. When I can figure out how to make "~d" and "~f" left
justify, I will update the library.
Output goes to the current output port. Perhaps it should go into a string?
Right now the trickiest operations have to do with exact and
inexact numbers. Every other Scheme type is output via "~a". I may
eventually redesign out
so that you can install your own
formatting handlers by type, and possibly allow for adding
manipulators on the fly.