5 Converting Values to Strings
(require unstable/cat) |
This module provides a few functions for converting Racket values to strings. In addition to features like padding and numeric formatting, the functions have the virtue of being shorter and more pleasant to type than format (with format string), number->string, or string-append.
procedure
(cat v ... [ #:width width #:limit limit #:limit-marker limit-marker #:pad-to pad-to #:align align #:padding padding #:left-padding left-padding #:right-padding right-padding]) → string? v : any/c width : (or/c exact-nonnegative-integer? #f) = #f
limit : (or/c exact-nonnegative-integer? +inf.0) = (or width +inf.0) limit-marker : string? = "..." pad-to : exact-nonnegative-integer? = (or width 0) align : (or/c 'left 'center 'right) = 'left padding : non-empty-string? = " " left-padding : non-empty-string? = padding right-padding : non-empty-string? = padding
> (cat "north") "north"
> (cat 'south) "south"
> (cat #"east") "east"
> (cat #\w "e" 'st) "west"
> (cat (list "red" 'green #"blue")) "(red green blue)"
> (cat 17) "17"
> (cat 100000000000000000000) "100000000000000000000"
> (cat pi) "3.141592653589793"
> (cat (expt 6.1 87)) "2.1071509386211452e+68"
The cat function is primarily useful for strings, numbers, and other atomic data. The catp and catw functions are better suited to compound data.
Let s be the concatenated string forms of the vs. If s is longer than limit characters, it is truncated to exactly limit characters. If s is shorter than pad-to characters, it is padded to exactly pad-to characters. Otherwise s is returned unchanged. If pad-to is greater than limit, an exception is raised.
If s is longer than limit characters, it is truncated and the end of the string is replaced with limit-marker. If limit-marker is longer than limit, an exception is raised.
> (cat "abcde" #:limit 5) "abcde"
> (cat "abcde" #:limit 4) "a..."
> (cat "abcde" #:limit 4 #:limit-marker "*") "abc*"
> (cat "abcde" #:limit 4 #:limit-marker "") "abcd"
> (cat "The quick brown fox" #:limit 15 #:limit-marker "") "The quick brown"
> (cat "The quick brown fox" #:limit 15 #:limit-marker "...") "The quick br..."
If s is shorter than pad-to, it is padded to at least pad-to characters. If align is 'left, then only right padding is added; if align is 'right, then only left padding is added; and if align is '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 left-padding repeated in its entirety as many times as possible followed by a prefix of left-padding to fill the remaining space. In contrast, right padding consists of a suffix of right-padding followed by a number of copies of right-padding in its entirety. Thus left padding starts with the start of left-padding and right padding ends with the end of right-padding.
> (cat "apple" #:pad-to 20 #:align 'left) "apple "
> (cat "pear" #:pad-to 20 #:align 'left #:right-padding " .") "pear . . . . . . . ."
> (cat "plum" #:pad-to 20 #:align 'right #:left-padding ". ") ". . . . . . . . plum"
> (cat "orange" #:pad-to 20 #:align 'center #:left-padding "- " #:right-padding " -") "- - - -orange- - - -"
Use width to set both limit and pad-to simultaneously, ensuring that the resulting string is exactly width characters long:
> (cat "terse" #:width 6) "terse "
> (cat "loquacious" #:width 6) "loq..."
procedure
(catp v [ #:width width #:limit limit #:limit-marker limit-marker #:pad-to pad-to #:align align #:padding padding #:left-padding left-padding #:right-padding right-padding]) → string? v : any/c width : (or/c exact-nonnegative-integer? #f) = #f
limit : (or/c exact-nonnegative-integer? +inf.0) = (or width +inf.0) limit-marker : string? = "..." pad-to : exact-nonnegative-integer? = (or width 0) align : (or/c 'left 'center 'right) = 'left padding : non-empty-string? = " " left-padding : non-empty-string? = padding right-padding : non-empty-string? = padding
> (catp "north") "\"north\""
> (catp 'south) "'south"
> (catp #"east") "#\"east\""
> (catp #\w) "#\\w"
> (catp (list "red" 'green #"blue")) "'(\"red\" green #\"blue\")"
Use catp to produce text that talks about Racket values.
> (let ([nums (for/list ([i 10]) i)]) (cat "The even numbers in " (catp nums) " are " (catp (filter even? nums)) ".")) "The even numbers in '(0 1 2 3 4 5 6 7 8 9) are '(0 2 4 6 8)."
procedure
(catw v [ #:width width #:limit limit #:limit-marker limit-marker #:pad-to pad-to #:align align #:padding padding #:left-padding left-padding #:right-padding right-padding]) → string? v : any/c width : (or/c exact-nonnegative-integer? #f) = #f
limit : (or/c exact-nonnegative-integer? +inf.0) = (or width +inf.0) limit-marker : string? = "..." pad-to : exact-nonnegative-integer? = (or width 0) align : (or/c 'left 'center 'right) = 'left padding : non-empty-string? = " " left-padding : non-empty-string? = padding right-padding : non-empty-string? = padding
> (catw "north") "\"north\""
> (catw 'south) "south"
> (catw #"east") "#\"east\""
> (catw #\w) "#\\w"
> (catw (list "red" 'green #"blue")) "(\"red\" green #\"blue\")"
procedure
(catn x [ #:sign sign #:base base #:precision precision #:pos/exp-range pos/exp-range #:exp-precision exp-precision #:exp-format-exponent exp-format-exponent #:pad-digits-to pad-digits-to #:digits-padding digits-padding]) → string? x : rational?
sign :
(or/c #f '+ '++ 'parens (let ([ind (or/c string? (list/c string? string?))]) (list/c ind ind ind))) = #f
base : (or/c (integer-in 2 36) (list/c 'up (integer-in 2 36))) = 10
precision :
(or/c exact-nonnegative-integer? (list/c '= exact-nonnegative-integer?)) = 3
pos/exp-range :
(list/c (or/c exact-integer? +inf.0) (or/c exact-integer? -inf.0)) = (list -inf.0 +inf.0)
exp-precision :
(or/c exact-nonnegative-integer? (list/c '= exact-nonnegative-integer?)) = 5
exp-format-exponent : (or/c #f string? (-> exact-integer? string?)) = #f pad-digits-to : exact-positive-integer? = 1 digits-padding : non-empty-string? = " "
Numbers whose order of magnitude (with respect to base) fall strictly within pos/exp-range are formatted using positional notation. More precisely, if the following condition holds:
(or (zero? x) (< (expt base (car pos/exp-range)) (abs x) (expt base (cadr pos/exp-range))))
then the result is equivalent to
(catnp x #:sign sign-mode #:base base #:precision precision #:pad-digits-to pad-digits-to #:digits-padding digits-padding)
Otherwise, the number is formatted in exponential notation, and the result is equivalent to
(catne x #:sign sign-mode #:base base #:precision exp-precision #:format-exponent exp-format-exponent #:pad-digits-to pad-digits-to #:digits-padding digits-padding)
Examples: | ||||||
|
Note that the default value of pos/exp-range ensures that positional notation will be used for any rational x.
procedure
(catnp x [ #:sign sign #:base base #:precision precision #:pad-digits-to pad-digits-to #:digits-padding digits-padding]) → string? x : rational?
sign :
(or/c #f '+ '++ 'parens (let ([ind (or/c string? (list/c string? string?))]) (list/c ind ind ind))) = #f
base : (or/c (integer-in 2 36) (list/c 'up (integer-in 2 36))) = 10
precision :
(or/c exact-nonnegative-integer? (list/c '= exact-nonnegative-integer?)) = 3 pad-digits-to : exact-positive-integer? = 1 digits-padding : non-empty-string? = " "
precision controls the number of digits after the decimal point (or more accurately, the radix point). If precision is a natural number, then up to 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 precision is (list '= digits), then exactly digits digits after the decimal point are used, and the decimal point is never dropped.
> (catnp pi) "3.142"
> (catnp pi #:precision 4) "3.1416"
> (catnp pi #:precision 0) "3"
> (catnp 1.5 #:precision 4) "1.5"
> (catnp 1.5 #:precision '(= 4)) "1.5000"
> (catnp 50 #:precision 2) "50"
> (catnp 50 #:precision '(= 2)) "50.00"
> (catnp 50 #:precision '(= 0)) "50."
pad-digits-to: if x would normally be printed with fewer than pad-digits-to digits (including the decimal point but not including the sign indicator), the output is left-padded using digits-padding.
> (catnp 17) "17"
> (catnp 17 #:pad-digits-to 4) " 17"
> (catnp -42 #:pad-digits-to 4) "- 42"
> (catnp 1.5 #:pad-digits-to 4) " 1.5"
> (catnp 1.5 #:precision 4 #:pad-digits-to 10) " 1.5"
> (catnp 1.5 #:precision '(= 4) #:pad-digits-to 10) " 1.5000"
digits-padding specifies the string used to pad the number to at least pad-digits-to characters (not including the sign indicator). The padding is placed between the sign and the normal digits of x.
> (catnp 17 #:pad-digits-to 4 #:digits-padding "0") "0017"
> (catnp -42 #:pad-digits-to 4 #:digits-padding "0") "-0042"
- sign controls how the sign of the number is indicated.
If sign is #f (the default), no sign output is generated if x is either positive or zero, and a minus sign is prefixed if x is negative.
> (for/list ([x '(17 0 -42)]) (catnp x)) '("17" "0" "-42")
If sign is '+, no sign output is generated if x is zero, a plus sign is prefixed if x is positive, and a minus sign is prefixed if x is negative.
> (for/list ([x '(17 0 -42)]) (catnp x #:sign '+)) '("+17" "0" "-42")
If sign is '++, a plus sign is prefixed if x is zero or positive, and a minus sign is prefixed if x is negative.
> (for/list ([x '(17 0 -42)]) (catnp x #:sign '++)) '("+17" "+0" "-42")
If sign is 'parens, no sign output is generated if x is zero or positive, and the number is enclosed in parentheses if x is negative.
> (for/list ([x '(17 0 -42)]) (catnp x #:sign 'parens)) '("17" "0" "(42)")
If sign is (list pos-ind zero-ind neg-ind), then pos-ind, zero-ind, and 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.
> (let ([sign-table '(("" " up") "an even " ("" " down"))]) (for/list ([x '(17 0 -42)]) (catnp x #:sign sign-table))) '("17 up" "an even 0" "42 down")
The default behavior is equivalent to '("" "" "-"); the 'parens mode is equivalent to '("" "" ("(" ")")).
base controls the base that x is formatted in. If base is a number greater than 10, then lower-case letters are used. If base is (list 'up base*) and base* is greater than 10, then upper-case letters are used.
> (catnp 100 #:base 7) "202"
> (catnp 4.5 #:base 2) "100.1"
> (catnp 3735928559 #:base 16) "deadbeef"
> (catnp 3735928559 #:base '(up 16)) "DEADBEEF"
procedure
(catne x [ #:sign sign #:base base #:precision precision #:format-exponent format-exponent #:pad-digits-to pad-digits-to #:digits-padding digits-padding]) → string? x : rational?
sign :
(or/c #f '+ '++ 'parens (let ([ind (or/c string? (list/c string? string?))]) (list/c ind ind ind))) = #f
base : (or/c (integer-in 2 36) (list/c 'up (integer-in 2 36))) = 10
precision :
(or/c exact-nonnegative-integer? (list/c '= exact-nonnegative-integer?)) = 5
format-exponent : (or/c #f string? (-> exact-integer? string?)) = #f pad-digits-to : exact-positive-integer? = 1 digits-padding : non-empty-string? = " "
base, sign are interpreted as in positional notation, described above, except that they apply only to the significand, not the exponent.
> (catne -100 #:base 2) "-1.1001×2^+06"
format-exponent determines how the exponent is displayed.
If format-exponent is a string, the exponent is displayed with an explicit sign (as with a sign-mode of '++) and at least two digits, separated from the significand by the “exponent marker” format-exponent:
> (catne 1234 #:format-exponent "E") "1.234E+03"
If format-exponent is #f, the “exponent marker” is "e" if base is 10 and a string involving base otherwise:
> (catne 1234) "1.234e+03"
> (catne 1234 #:base 8) "2.322×8^+03"
If format-exponent is a procedure, it is applied to the exponent and the resulting string is appended to the significand:
> (catne 1234 #:format-exponent (lambda (e) (format "E~a" e))) "1.234E3"
precision determines how many digits after the radix point the significand contains. Like the precision argument of catnp, the form (list '= digits) causes trailing zeroes to be retained.
> (catne 12345 #:precision 3) "1.235e+04"
> (catne 12345 #:precision 2) "1.23e+04"
> (catne 10000 #:precision 2) "1e+04"
> (catne 10000 #:precision '(= 2)) "1.00e+04"
pad-digits-to and digits-padding are interpreted as in positional notation.
> (catne 12345 #:pad-digits-to 12 #:digits-padding " ") " 1.2345e+04"