Sign In/My Account | View Cart  
advertisement


Listen Print Discuss

Exegesis 7
by Damian Conway | Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13

Editor's note: this document is out of date and remains here for historic interest. See Synopsis 7 for the current design information.

Command our present numbers be muster'd...

A field specifier of the form {>>>>.<<} or {]]]].[[} represents a decimal-aligned numeric field. The decimal marker always appears in exactly the position indicated and the rest of the number is aligned around it. The decimal places are rounded to the specific number of places indicated, but only "significant" digits are shown. For example:


    @nums = (1, 1.2, 1.23, 11.234, 111.235, 1.0001);

    print form "Thy score be: {]]]].[[}",
                              @nums;

prints:


    Thy score be:     1.0
    Thy score be:     1.2
    Thy score be:     1.23
    Thy score be:    11.234
    Thy score be:   111.235
    Thy score be:     1.000

The points are all aligned, the minimal number of decimal places are shown, and the decimals are rounded (using the same rounding protocol that printf employs). Note in particular that, even though both 1 and 1.0001 would normally convert to the same 3-decimal-place value (1.000), a form call only shows all three zeros in the second case since only in the second case are they "significant".

In other words, unless we tell it otherwise, form tries to avoid displaying a number with more accuracy than it actually possesses (within the constraint that it must always show at least one decimal place).

Here are only numbers ratified.

You're probably wondering what happens if we try to format a number that's too large for the available places (as 123456.78 would be in the above format). Whereas sprintf would extend a numeric field to accommodate the number, form insists on preserving the specified layout; in particular, the position of the decimal point. But it obviously can't just cut off the extra high-order digits; that would change the value:


    Thy score be: 23456.78

So, instead, it indicates that the number doesn't fit by filling the field with octothorpes (the way many spreadsheets do):


    Thy score be: #####.###

Note, however, that it is possible to change this behaviour should we need to.

It's also possible that someone (not you, of course!) might attempt to pass a numeric field some data that isn't numeric at all:


    my @mixed_data = (1, 2, "three", {4=>5}, "6", "7-Up");

    print form 'Thy score be: {]]]].[[}', 
                              @mixed_data;

Unlike Perl itself, form doesn't autoconvert non-numeric values. Instead it marks them with another special string, by filling the field with question-marks:


    Thy score be:     1.0  
    Thy score be:     2.0  
    Thy score be: ?????.???
    Thy score be: ?????.???
    Thy score be:     6.0  
    Thy score be: ?????.???

Note that strings per se aren't a problem – form will happily convert strings that contain valid numbers, such as "6" in the above example. But it does reject strings that contain anything else besides a number (even when Perl itself would successfully convert the number – as it would for "7-Up" above).

Those who'd prefer Perl's usual, more laissez-faire attitude to numerical conversion can just pre-numerify the values themselves using the unary numerification operator (shown here in its list form – – since we have an array of values to be numerified):


    print form 'Thy score be: {]]]].[[}',
                              +« @mixed_data;

This version would print:


    Thy score be:     1.0  
    Thy score be:     2.0  
    Thy score be:     0.0  
    Thy score be:     1.0  
    Thy score be:     6.0  
    Thy score be:     7.0

(The 1.0 on the fourth line appears because Perl 6 hashes numerify to the number of entries they contain).

See how the giddy multitude do point...

Of course, not everyone uses a dot for their decimal point. The other main contender is the comma, and naturally form supports that as well. If we specify a numeric field with a comma between the brackets:


    @les_nums = (1, 1.2, 1.23, 11.234, 111.235, 1.0001);

    print form 'Votre score est: {]]]],[[}',
                                 @les_nums;

the call prints:


    Votre score est:     1,0
    Votre score est:     1,2
    Votre score est:     1,23
    Votre score est:    11,234
    Votre score est:   111,235
    Votre score est:     1,000

In fact, form is extremely flexible about the characters we're allowed to use as a decimal marker: anything except an angle- or square bracket or a plus sign is acceptable.

As a bonus, form allows us to use the specified decimal marker in the data as well as in the format. So this works too:


    @les_nums = ("1", "1,2", "1,23", "11,234", "111,235", "1,0001");

    print form 'Vos score est: {]]]],[[}',
                               @les_nums;

Or else be impudently negative...

Negative numbers work as expected, with the minus sign taking up one column of the field's allotted span:


    @nums = ( 1, -1.2,  1.23, -11.234,  111.235, -12345.67);

    print form 'Thy score be: {]]]].[[}',
                              @nums;

This would print:


    Thy score be:     1.0  
    Thy score be:    -1.2  
    Thy score be:     1.23 
    Thy score be:   -11.234
    Thy score be:   111.235
    Thy score be: #####.###

However, form can also format numbers so that the minus sign trails the number. To do that we simple put an explicit minus sign inside the field specification, at the end:


    print form 'Thy score be: {]]]].[[-}',
                              @nums;

which would then print:


    Thy score be:     1.0   
    Thy score be:     1.2-  
    Thy score be:     1.23  
    Thy score be:    11.234-
    Thy score be:   111.235 
    Thy score be: 12345.67-

form also understands the common financial usage where negative numbers are represented as positive numbers in parentheses. Once again, we draw an abstract picture of what we want (by putting parens at either end of the field specification):


    print form 'Thy dividend be: {(]]]].[[)}',
                                 @nums;

and form obliges:


    Thy dividend be:      1.0   
    Thy dividend be:     (1.2)  
    Thy dividend be:      1.23  
    Thy dividend be:    (11.234)
    Thy dividend be:    111.235 
    Thy dividend be: (12345.67)

Note that the parens have to go inside the field's braces. Otherwise, they're just literal parts of the format string:


    print form 'Thy dividend be: ({]]]].[[})',
                                  @nums;

and we'd get:


    Thy dividend be: (    1.0  ) 
    Thy dividend be: (   -1.2  )  
    Thy dividend be: (    1.23 )
    Thy dividend be: (  -11.234)
    Thy dividend be: (  111.235)
    Thy dividend be: (#####.###)

And stand a comma 'tween their amities...

If we add so-called "thousands separators" inside a numeric field at the usual places, form includes them appropriately in its output. It can handle the five major formatting conventions:


    my @nums = (0, 1, 1.1, 1.23, 4567.89, 34567.89, 234567.89, 1234567.89);

    print form
        "Brittannic      Continental     Subcontinental   Tyrolean        Asiatic",
        "_____________   _____________   ______________   _____________   _____________",
        "{],]]],]]].[}   {].]]].]]],[}    {]],]],]]].[}   {]']]]']]],[}   {]]]],]]]].[}",
         @nums,          @nums,          @nums,           @nums,          @nums;

to produce:


    Brittannic      Continental     Subcontinental   Tyrolean        Asiatic
    _____________   _____________   ______________   _____________   _____________
             0.0             0,0              0.0             0,0             0.0 
             1.0             1,0              1.0             1,0             1.0 
             1.1             1,1              1.1             1,1             1.1 
             1.23            1,23             1.23            1,23            1.23
         4,567.89        4.567,89         4,567.89        4'567,89         4567.89
        34,567.89       34.567,89        34,567.89       34'567,89       3,4567.89
       234,567.89      234.567,89      2,34,567.89      234'567,89      23,4567.89
     1,234,567.89    1.234.567,89     12,34,567.89    1'234'567,89     123,4567.89

It also accepts a space character as a "thousands separator" (with, of course, any decimal marker we might like):


    print form
        "Hyperspatial",
        "_____________",
        "{] ]]] ]]]:[}",
         @nums;

to produce:


    Hyperspatial
    _____________
             0:0 
             1:0 
             1:1 
             1:23
         4 567:89
        34 567:89
       234 567:89
     1 234 567:89

And gives to airy nothing a local habitation and a name

Of course, sometimes we don't know ahead of time just where in the world our formatted numbers will be displayed. Locales were invented to address that very problem, and form supports them.

If we use the :locale option, form detects the current locale and converts any numerical formats it finds to the appropriate layout. For example, if we wrote:


    @nums = ( 1, -1.2,  1.23, -11.234,  111.235, -12345.67);

    print form 
            "{],]]],]]].[[}",
            @nums;

then we'd get:


          1.0
         -1.2
          1.23
        -11.234
        111.235
    -12,345.67

wherever the program was run. But if we had written:


    print form
            :locale,
            "{],]]],]]].[[}",
            @nums;

then we'd get:


          1.0
         -1.2
          1.23
        -11.234
        111.235
    -12,345.67

or:


          1,0
          1,2-
          1,23
         11,23-
        111,235
     12.345,67-
  
or:

          1,0
         (1,2)
          1,23
        (11,23)
        111,235
    (12'345,67)

or whatever else the current locale indicated was the correct local layout for numbers.

That is, when the :locale option is specified, form ignores the actual decimal point, thousands separator, and negation sign we specified in the call, and instead uses the values for these markers that are returned by the POSIX localeconv function. That means that we can specify our numerical formatting in a style that seems natural to us, and at the same time allow the numbers to be formatted in a style that seems natural to the user.

Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13

Next Pagearrow