Repeatable off-by-one issue in Common Lisp's f

2019-06-22 01:31发布

问题:

I have format's tabs ~VT behaving differently depending on whether the newline ~% is at the beginning or the end of lines, and I wanted to know why. The difference is that when the newline is at the end of lines, there seems to be an extra space in the first instance only of the tab stop. The following examples illustrate. The only difference in the examples is in the format-control string: it's "~%~A~VT= ~A" in the first example and "~A~VT= ~A~%" in the second.

EXAMPLE 1: newline at the beginning of output lines

(let ((sb (make-array 0
                :element-type 'character
                :adjustable t
                :fill-pointer 0)))
           (mapcar (lambda (line)
                     (format sb "~%~A~VT= ~A" line 10 42))
                   '(a abcd asdf foobar g november))
           sb)
"
A        = 42
ABCD     = 42
ASDF     = 42
FOOBAR   = 42
G        = 42
NOVEMBER = 42"

The behavior here is as expected.

EXAMPLE 2: newline at the ends of output lines

The thing to notice in this example is that the first line,

A         = 42

has one more space in it than the corresponding line from example 1:

A        = 42

It's a little hard to see because of the leading double-quote, and that's why I snipped this out: to help you see them better. This is repeatable on much bigger examples and is an MVE stripped out of a much larger program.

(let ((sb (make-array 0
                :element-type 'character
                :adjustable t
                :fill-pointer 0)))
           (mapcar (lambda (line)
                     (format sb "~A~VT= ~A~%" line 10 42))
                   '(a abcd asdf foobar g november))
           sb)
"A         = 42
ABCD     = 42
ASDF     = 42
FOOBAR   = 42
G        = 42
NOVEMBER = 42
"

The big-picture question is "why?" I'm using SBCL 1.3.1 on a Mac and haven't tried it on other implementations. It could be a bug, but it seems more plausible that it's intended behavior, but I don't understand what it could be intended to accomplish and I haven't been able to find an explanation in format's documentation.

回答1:

I think that it is a bug. I can also reproduce it on Linux with SBCL 1.3.1.

~T may need heuristics (that can fail) to determine the current column in some cases, but I guess the start of the string should be considered column 0.

At least on my computer, it seems not to occur when a simple with-output-to-string is used:

(with-output-to-string (s)
  (mapcar (lambda (line)
            (format s "~A~VT= ~A~%" line 10 42))
          '(a abcd asdf foobar g november)))

It does occur, however, when you give the pre-made string to with-output-to-string:

(let ((sb (make-array 0
                      :element-type 'character
                      :adjustable t
                      :fill-pointer 0)))
  (with-output-to-string (s sb)
    (mapcar (lambda (line)
              (format s "~A~VT= ~A~%" line 10 42))
            '(a abcd asdf foobar g november))
    sb))


标签: common-lisp