446 lines
17 KiB
Racket
446 lines
17 KiB
Racket
#lang scribble/doc
|
|
@(require scribble/manual
|
|
scribble/struct
|
|
scribble/eval
|
|
"mz.rkt"
|
|
(for-label racket/contract
|
|
racket/math
|
|
racket/format))
|
|
|
|
@(begin
|
|
(define the-eval (make-base-eval))
|
|
(the-eval '(require racket/math racket/format)))
|
|
|
|
@title[#:tag "format"]{Converting Values to Strings}
|
|
|
|
@note-lib[racket/format]
|
|
|
|
The @racketmodname[racket/format] library provides functions for
|
|
converting Racket values to strings. In addition to features like
|
|
padding and numeric formatting, the functions have the virtue of being
|
|
shorter than @racket[format] (with format string),
|
|
@racket[number->string], or @racket[string-append].
|
|
|
|
@defproc[(~a [v any/c] ...
|
|
[#:separator separator string? ""]
|
|
[#:width width (or/c exact-nonnegative-integer? #f) #f]
|
|
[#:max-width max-width (or/c exact-nonnegative-integer? +inf.0) (or width +inf.0)]
|
|
[#:min-width min-width exact-nonnegative-integer? (or width 0)]
|
|
[#:limit-marker limit-marker string? ""]
|
|
[#:align align (or/c 'left 'center 'right) 'left]
|
|
[#:pad-string pad-string non-empty-string? " "]
|
|
[#:left-pad-string left-pad-string non-empty-string? pad-string]
|
|
[#:right-pad-string right-pad-string non-empty-string? pad-string])
|
|
string?]{
|
|
|
|
Converts each @racket[v] to a string in @racket[display] mode---that
|
|
is, like @racket[(format "~a" v)]---then concatentates the results
|
|
with @racket[separator] between consecutive items, and then pads or
|
|
truncates the string to be at least @racket[min-width] characters and
|
|
at most @racket[max-width] characters.
|
|
|
|
@interaction[#:eval the-eval
|
|
(~a "north")
|
|
(~a 'south)
|
|
(~a #"east")
|
|
(~a #\w "e" 'st)
|
|
(~a (list "red" 'green #"blue"))
|
|
(~a 17)
|
|
(eval:alts (~a @#,(racketvalfont "#e1e20")) (~a #e1e20))
|
|
(~a pi)
|
|
(~a (expt 6.1 87))
|
|
]
|
|
|
|
The @racket[~a] function is primarily useful for strings, numbers, and other
|
|
atomic data. The @racket[~v] and @racket[~s] functions are better suited to
|
|
compound data.
|
|
|
|
Let @racket[_s] be the concatenated string forms of the @racket[v]s
|
|
plus separators. If @racket[_s] is longer than @racket[max-width]
|
|
characters, it is truncated to exactly @racket[max-width]
|
|
characters. If @racket[_s] is shorter than @racket[min-width]
|
|
characters, it is padded to exactly @racket[min-width]
|
|
characters. Otherwise @racket[_s] is returned unchanged. If
|
|
@racket[min-width] is greater than @racket[max-width], an exception is
|
|
raised.
|
|
|
|
If @racket[_s] is longer than @racket[max-width] characters, it is
|
|
truncated and the end of the string is replaced with
|
|
@racket[limit-marker]. If @racket[limit-marker] is longer than
|
|
@racket[max-width], an exception is raised.
|
|
|
|
@interaction[#:eval the-eval
|
|
(~a "abcde" #:max-width 5)
|
|
(~a "abcde" #:max-width 4)
|
|
(~a "abcde" #:max-width 4 #:limit-marker "*")
|
|
(~a "abcde" #:max-width 4 #:limit-marker "...")
|
|
(~a "The quick brown fox" #:max-width 15 #:limit-marker "")
|
|
(~a "The quick brown fox" #:max-width 15 #:limit-marker "...")
|
|
]
|
|
|
|
If @racket[_s] is shorter than @racket[min-width], it is padded to at
|
|
least @racket[min-width] characters. If @racket[align] is
|
|
@racket['left], then only right padding is added; if @racket[align]
|
|
is @racket['right], then only left padding is added; and if
|
|
@racket[align] is @racket['center], then roughly equal amounts of
|
|
left padding and right padding are added.
|
|
|
|
Padding is specified as a non-empty string. Left padding consists of
|
|
@racket[left-pad-string] repeated in its entirety as many times as
|
|
possible followed by a @emph{prefix} of @racket[left-pad-string] to fill
|
|
the remaining space. In contrast, right padding consists of a
|
|
@emph{suffix} of @racket[right-pad-string] followed by a number of copies
|
|
of @racket[right-pad-string] in its entirety. Thus left padding starts
|
|
with the start of @racket[left-pad-string] and right padding ends with
|
|
the end of @racket[right-pad-string].
|
|
|
|
@interaction[#:eval the-eval
|
|
(~a "apple" #:min-width 20 #:align 'left)
|
|
(~a "pear" #:min-width 20 #:align 'left #:right-pad-string " .")
|
|
(~a "plum" #:min-width 20 #:align 'right #:left-pad-string ". ")
|
|
(~a "orange" #:min-width 20 #:align 'center
|
|
#:left-pad-string "- " #:right-pad-string " -")
|
|
]
|
|
|
|
Use @racket[width] to set both @racket[max-width] and @racket[min-width]
|
|
simultaneously, ensuring that the resulting string is exactly
|
|
@racket[width] characters long:
|
|
|
|
@interaction[#:eval the-eval
|
|
(~a "terse" #:width 6)
|
|
(~a "loquacious" #:width 6)
|
|
]
|
|
}
|
|
|
|
@;{----------------------------------------}
|
|
|
|
@defproc[(~v [v any/c] ...
|
|
[#:separator separator string? " "]
|
|
[#:width width (or/c exact-nonnegative-integer? #f) #f]
|
|
[#:max-width max-width (or/c exact-nonnegative-integer? +inf.0) (or width +inf.0)]
|
|
[#:min-width min-width exact-nonnegative-integer? (or width 0)]
|
|
[#:limit-marker limit-marker string? "..."]
|
|
[#:align align (or/c 'left 'center 'right) 'left]
|
|
[#:pad-string pad-string non-empty-string? " "]
|
|
[#:left-pad-string left-pad-string non-empty-string? pad-string]
|
|
[#:right-pad-string right-pad-string non-empty-string? pad-string])
|
|
string?]{
|
|
|
|
Like @racket[~a], but each value is converted like @racket[(format
|
|
"~v" v)], the default separator is @racket[" "], and the default limit
|
|
marker is @racket["..."].
|
|
|
|
@interaction[#:eval the-eval
|
|
(~v "north")
|
|
(~v 'south)
|
|
(~v #"east")
|
|
(~v #\w)
|
|
(~v (list "red" 'green #"blue"))
|
|
]
|
|
|
|
Use @racket[~v] to produce text that talks about Racket values.
|
|
|
|
@interaction[#:eval the-eval
|
|
(let ([nums (for/list ([i 10]) i)])
|
|
(~a "The even numbers in " (~v nums)
|
|
" are " (~v (filter even? nums)) "."))
|
|
]}
|
|
|
|
@;{----------------------------------------}
|
|
|
|
@defproc[(~s [v any/c] ...
|
|
[#:separator separator string? " "]
|
|
[#:width width (or/c exact-nonnegative-integer? #f) #f]
|
|
[#:max-width max-width (or/c exact-nonnegative-integer? +inf.0) (or width +inf.0)]
|
|
[#:min-width min-width exact-nonnegative-integer? (or width 0)]
|
|
[#:limit-marker limit-marker string? "..."]
|
|
[#:align align (or/c 'left 'center 'right) 'left]
|
|
[#:pad-string pad-string non-empty-string? " "]
|
|
[#:left-pad-string left-pad-string non-empty-string? pad-string]
|
|
[#:right-pad-string right-pad-string non-empty-string? pad-string])
|
|
string?]{
|
|
|
|
Like @racket[~a], but each value is converted like @racket[(format
|
|
"~s" v)], the default separator is @racket[" "], and the default limit
|
|
marker is @racket["..."].
|
|
|
|
@interaction[#:eval the-eval
|
|
(~s "north")
|
|
(~s 'south)
|
|
(~s #"east")
|
|
(~s #\w)
|
|
(~s (list "red" 'green #"blue"))
|
|
]
|
|
}
|
|
|
|
@;{----------------------------------------}
|
|
|
|
@defproc[(~e [v any/c] ...
|
|
[#:separator separator string? " "]
|
|
[#:width width (or/c exact-nonnegative-integer? #f) #f]
|
|
[#:max-width max-width (or/c exact-nonnegative-integer? +inf.0) (or width +inf.0)]
|
|
[#:min-width min-width exact-nonnegative-integer? (or width 0)]
|
|
[#:limit-marker limit-marker string? "..."]
|
|
[#:align align (or/c 'left 'center 'right) 'left]
|
|
[#:pad-string pad-string non-empty-string? " "]
|
|
[#:left-pad-string left-pad-string non-empty-string? pad-string]
|
|
[#:right-pad-string right-pad-string non-empty-string? pad-string])
|
|
string?]{
|
|
|
|
Like @racket[~a], but each value is converted like @racket[(format
|
|
"~e" v)], the default separator is @racket[" "], and the default limit
|
|
marker is @racket["..."].
|
|
|
|
@interaction[#:eval the-eval
|
|
(~e "north")
|
|
(~e 'south)
|
|
(~e #"east")
|
|
(~e #\w)
|
|
(~e (list "red" 'green #"blue"))
|
|
]
|
|
|
|
}
|
|
|
|
@;{----------------------------------------}
|
|
|
|
@defproc[(~r [x rational?]
|
|
[#:sign sign
|
|
(or/c #f '+ '++ 'parens
|
|
(let ([ind (or/c string? (list/c string? string?))])
|
|
(list/c ind ind ind)))
|
|
#f]
|
|
[#:base base
|
|
(or/c (integer-in 2 36) (list/c 'up (integer-in 2 36)))
|
|
10]
|
|
[#:precision precision
|
|
(or/c exact-nonnegative-integer?
|
|
(list/c '= exact-nonnegative-integer?))
|
|
3]
|
|
[#:notation notation
|
|
(or/c 'positional 'exponential
|
|
(-> rational? (or/c 'positional 'exponential)))
|
|
'positional]
|
|
[#:format-exponent format-exponent
|
|
(or/c #f string? (-> exact-integer? string?))
|
|
#f]
|
|
[#:min-width min-width exact-positive-integer? 1]
|
|
[#:pad-string pad-string non-empty-string? " "])
|
|
string?]{
|
|
|
|
Converts the rational number @racket[x] to a string in either
|
|
positional or exponential notation, depending on
|
|
@racket[notation]. The exactness or inexactness of @racket[x] does not
|
|
affect its formatting.
|
|
|
|
The optional arguments control number formatting:
|
|
|
|
@itemize[
|
|
|
|
@item{@racket[notation] --- determines whether the number is printed
|
|
in positional or exponential notation. If @racket[notation] is a
|
|
function, it is applied to @racket[x] to get the notation to be used.
|
|
|
|
@interaction[#:eval the-eval
|
|
(~r 12345)
|
|
(~r 12345 #:notation 'exponential)
|
|
(let ([pick-notation
|
|
(lambda (x)
|
|
(if (or (< (abs x) 0.001) (> (abs x) 1000))
|
|
'exponential
|
|
'positional))])
|
|
(for/list ([i (in-range 1 5)])
|
|
(~r (expt 17 i) #:notation pick-notation)))
|
|
]
|
|
}
|
|
|
|
@item{@racket[precision] --- controls the number of digits after the
|
|
decimal point (or more accurately, the
|
|
@hyperlink["http://en.wikipedia.org/wiki/Radix_point"]{radix point}).
|
|
When @racket[x] is formatted in exponential form, @racket[precision]
|
|
applies to the significand.
|
|
|
|
If @racket[precision] is a natural number, then up to @racket[precision] digits are
|
|
displayed, but trailing zeroes are dropped, and if all digits after the decimal
|
|
point are dropped the decimal point is also dropped. If @racket[precision] is
|
|
@racket[(list '= _digits)], then exactly @racket[_digits] digits after the
|
|
decimal point are used, and the decimal point is never dropped.
|
|
|
|
@interaction[#:eval the-eval
|
|
(~r pi)
|
|
(~r pi #:precision 4)
|
|
(~r pi #:precision 0)
|
|
(~r 1.5 #:precision 4)
|
|
(~r 1.5 #:precision '(= 4))
|
|
(~r 50 #:precision 2)
|
|
(~r 50 #:precision '(= 2))
|
|
(~r 50 #:precision '(= 0))
|
|
]}
|
|
|
|
@item{@racket[min-width] --- if @racket[x] would normally be printed
|
|
with fewer than @racket[min-width] digits (including the decimal
|
|
point but not including the sign indicator), the digits are left-padded
|
|
using @racket[pad-string].
|
|
|
|
@interaction[#:eval the-eval
|
|
(~r 17)
|
|
(~r 17 #:min-width 4)
|
|
(~r -42 #:min-width 4)
|
|
(~r 1.5 #:min-width 4)
|
|
(~r 1.5 #:precision 4 #:min-width 10)
|
|
(~r 1.5 #:precision '(= 4) #:min-width 10)
|
|
(eval:alts (~r @#,(racketvalfont "#e1e10") #:min-width 6)
|
|
(~r #e1e10 #:min-width 6))
|
|
]}
|
|
|
|
@item{@racket[pad-string] --- specifies the string used to pad the
|
|
number to at least @racket[min-width] characters (not including the
|
|
sign indicator). The padding is placed between the sign and the normal
|
|
digits of @racket[x].
|
|
|
|
@interaction[#:eval the-eval
|
|
(~r 17 #:min-width 4 #:pad-string "0")
|
|
(~r -42 #:min-width 4 #:pad-string "0")
|
|
]}
|
|
|
|
@item{@racket[sign] --- controls how the sign of the number is
|
|
indicated:
|
|
@itemlist[
|
|
|
|
@item{If @racket[sign] is @racket[#f] (the default), no sign output is
|
|
generated if @racket[x] is either positive or zero, and a minus sign is
|
|
prefixed if @racket[x] is negative.
|
|
|
|
@interaction[#:eval the-eval
|
|
(for/list ([x '(17 0 -42)]) (~r x))
|
|
]}
|
|
|
|
@item{If @racket[sign] is @racket['+], no sign output is generated if
|
|
@racket[x] is zero, a plus sign is prefixed if @racket[x] is positive, and a
|
|
minus sign is prefixed if @racket[x] is negative.
|
|
|
|
@interaction[#:eval the-eval
|
|
(for/list ([x '(17 0 -42)]) (~r x #:sign '+))
|
|
]}
|
|
|
|
@item{If @racket[sign] is @racket['++], a plus sign is prefixed if @racket[x]
|
|
is zero or positive, and a minus sign is prefixed if @racket[x] is negative.
|
|
|
|
@interaction[#:eval the-eval
|
|
(for/list ([x '(17 0 -42)]) (~r x #:sign '++))
|
|
]}
|
|
|
|
@item{If @racket[sign] is @racket['parens], no sign output is generated if
|
|
@racket[x] is zero or positive, and the number is enclosed in parentheses if
|
|
@racket[x] is negative.
|
|
|
|
@interaction[#:eval the-eval
|
|
(for/list ([x '(17 0 -42)]) (~r x #:sign 'parens))
|
|
]}
|
|
|
|
@item{If @racket[sign] is @racket[(list _pos-ind _zero-ind _neg-ind)], then
|
|
@racket[_pos-ind], @racket[_zero-ind], and @racket[_neg-ind] are used to
|
|
indicate positive, zero, and negative numbers, respectively. Each indicator is
|
|
either a string to be used as a prefix or a list containing two strings: a
|
|
prefix and a suffix.
|
|
|
|
@interaction[#:eval the-eval
|
|
(let ([sign-table '(("" " up") "an even " ("" " down"))])
|
|
(for/list ([x '(17 0 -42)]) (~r x #:sign sign-table)))
|
|
]
|
|
|
|
The default behavior is equivalent to @racket['("" "" "-")]; the
|
|
@racket['parens] mode is equivalent to @racket['("" "" ("(" ")"))].
|
|
}
|
|
]}
|
|
|
|
@item{@racket[base] --- controls the base that @racket[x] is formatted in. If
|
|
@racket[base] is a number greater than @racket[10], then lower-case letters are
|
|
used. If @racket[base] is @racket[(list 'up _base*)] and @racket[_base*] is
|
|
greater than @racket[10], then upper-case letters are used.
|
|
|
|
@interaction[#:eval the-eval
|
|
(~r 100 #:base 7)
|
|
(~r 4.5 #:base 2)
|
|
(~r 3735928559 #:base 16)
|
|
(~r 3735928559 #:base '(up 16))
|
|
(~r 3735928559 #:base '(up 16) #:notation 'exponential)
|
|
]}
|
|
|
|
@item{@racket[format-exponent] --- determines how the exponent is displayed.
|
|
|
|
If @racket[format-exponent] is a string, the exponent is displayed with an
|
|
explicit sign (as with a @racket[sign] of @racket['++]) and at least two
|
|
digits, separated from the significand by the ``exponent marker''
|
|
@racket[format-exponent]:
|
|
|
|
@interaction[#:eval the-eval
|
|
(~r 1234 #:notation 'exponential #:format-exponent "E")
|
|
]
|
|
|
|
If @racket[format-exponent] is @racket[#f], the ``exponent marker'' is
|
|
@racket["e"] if @racket[base] is @racket[10] and a string involving
|
|
@racket[base] otherwise:
|
|
|
|
@interaction[#:eval the-eval
|
|
(~r 1234 #:notation 'exponential)
|
|
(~r 1234 #:notation 'exponential #:base 8)
|
|
]
|
|
|
|
If @racket[format-exponent] is a procedure, it is applied to the exponent and
|
|
the resulting string is appended to the significand:
|
|
|
|
@interaction[#:eval the-eval
|
|
(~r 1234 #:notation 'exponential
|
|
#:format-exponent (lambda (e) (format "E~a" e)))
|
|
]}
|
|
|
|
]
|
|
}
|
|
|
|
@; ----------------------------------------
|
|
|
|
@deftogether[(
|
|
@defproc[(~.a [v any/c] ...
|
|
[#:separator separator string? ""]
|
|
[#:width width (or/c exact-nonnegative-integer? #f) #f]
|
|
[#:max-width max-width (or/c exact-nonnegative-integer? +inf.0) (or width +inf.0)]
|
|
[#:min-width min-width exact-nonnegative-integer? (or width 0)]
|
|
[#:limit-marker limit-marker string? ""]
|
|
[#:align align (or/c 'left 'center 'right) 'left]
|
|
[#:pad-string pad-string non-empty-string? " "]
|
|
[#:left-pad-string left-pad-string non-empty-string? pad-string]
|
|
[#:right-pad-string right-pad-string non-empty-string? pad-string])
|
|
string?]
|
|
@defproc[(~.v [v any/c] ...
|
|
[#:separator separator string? " "]
|
|
[#:width width (or/c exact-nonnegative-integer? #f) #f]
|
|
[#:max-width max-width (or/c exact-nonnegative-integer? +inf.0) (or width +inf.0)]
|
|
[#:min-width min-width exact-nonnegative-integer? (or width 0)]
|
|
[#:limit-marker limit-marker string? "..."]
|
|
[#:align align (or/c 'left 'center 'right) 'left]
|
|
[#:pad-string pad-string non-empty-string? " "]
|
|
[#:left-pad-string left-pad-string non-empty-string? pad-string]
|
|
[#:right-pad-string right-pad-string non-empty-string? pad-string])
|
|
string?]
|
|
@defproc[(~.s [v any/c] ...
|
|
[#:separator separator string? " "]
|
|
[#:width width (or/c exact-nonnegative-integer? #f) #f]
|
|
[#:max-width max-width (or/c exact-nonnegative-integer? +inf.0) (or width +inf.0)]
|
|
[#:min-width min-width exact-nonnegative-integer? (or width 0)]
|
|
[#:limit-marker limit-marker string? "..."]
|
|
[#:align align (or/c 'left 'center 'right) 'left]
|
|
[#:pad-string pad-string non-empty-string? " "]
|
|
[#:left-pad-string left-pad-string non-empty-string? pad-string]
|
|
[#:right-pad-string right-pad-string non-empty-string? pad-string])
|
|
string?]
|
|
)]{
|
|
|
|
Like @racket[~a], @racket[~v], and @racket[~s], but each @racket[v] is
|
|
formatted like @racket[(format "~.a" v)], @racket[(format "~.v" v)],
|
|
and @racket[(format "~.s" v)], respectively.}
|
|
|
|
|
|
@; ----------------------------------------
|
|
|
|
@(close-eval the-eval)
|