Pretty print sprintf()

  • Follow


Hi.

Mr Coffee asked for a pretty print function  some time ago.
With the handy string functions  like LEFT(), Right(), Center(), Format(),
and so on there is not much demand for such a function.

In  *xenix  the printf() and sprintf() functions are widely used though.
So I wrote a simple implementation of some of sprintf 's functionality.
Please suggest improvements. I haven't got time to respond daily though.

I  used "classic" rexx.  Would an  oo approach be better?

In case your mail folds the lines inadvertantly, I put  "/*R*/"  in pos
80-84.
Use that to fix alignment.

---- snippet below ------------------------------------ 
-- rexx
/*R*/
signal on novalue
/*R*/
trace o
/*R*/

/*R*/
tdate.1 = '2007-10-01';  ttime.1 = '07.15' ; ttemp.1 = 5;
/*R*/
tdate.2 = '2007-10-02';  ttime.2 = '07.14' ; ttemp.2 = 8;
/*R*/
tdate.3 = '2007-10-03';  ttime.3 = '07.01' ; ttemp.3 = 11;
/*R*/
tdate.4 = '2007-10-04';  ttime.4 = '07.14' ; ttemp.4 = 6;
/*R*/
tdate.5 = '2007-10-05';  ttime.5 = '07.00' ; ttemp.5 = 5;
/*R*/
tdate.6 = '2007-10-06';  ttime.6 = '06.48' ; ttemp.6 = 2;
/*R*/
tdate.7 = '2007-10-07';  ttime.7 = '07.15' ; ttemp.7 = -1.5;
/*R*/
tdate.8 = '2007-10-08';  ttime.8 = '07.14' ; ttemp.8 = -2.5;
/*R*/
tdate.9 = '2007-10-09';  ttime.9 = '07.21' ; ttemp.9 = +3.599;
/*R*/
tdate.0 = 9
/*R*/

/*R*/

/*R*/
say sprintf("%-10s %5s %s", "Date", "Time", "Temperature")
/*R*/
say sprintf("%-10s %5s %s", "----", "----", "-----------")
/*R*/

/*R*/
do ix = 1 to  tdate.0
/*R*/
  if ix = 7 then weekday = "Sunday"; else weekday = ""
/*R*/
  say sprintf("%s %s %3.1f C %s", tdate.ix , ttime.ix, ttemp.ix, weekday )
/*R*/
end
/*R*/

/*R*/
exit 0
/*R*/
-- --------------------------------------------------------------- 
/*R*/
sprintf: procedure
/*R*/

/*R*/
/*
/*R*/
 see Unix/C/perl sprintf documentation for general usage
/*R*/

/*R*/
Implemented datatypes
/*R*/
   d integer
/*R*/
   f float fixed decimal
/*R*/
   s string
/*R*/
   defualt none
/*R*/

/*R*/
Implemented width modifiers
/*R*/
    n      length of element
/*R*/
    n1.n2  n1 = length before decimal point  including +/- default length
/*R*/
           n2= number of decimal places      default zero
/*R*/
    decimals will be rounded to n2 positions
/*R*/

/*R*/
Implemented modifiers
/*R*/
     %%  is  "%"
/*R*/
     + prefix postive number with "+" and right adjust
/*R*/
     + right  adjust string
/*R*/
     - left  justify number
/*R*/
     - left  adjust string
/*R*/
     0 right adjust zero fill for numbers and strings
/*R*/
     default for strings "as is"
/*R*/
     default for numbers is right adjust
/*R*/
*/
/*R*/

/*R*/
types     = 'dfs'   -- case  sensitive
/*R*/
modifiers = "+-0"
/*R*/

/*R*/
-- limited to 12 arguments  presently
/*R*/
-- how to change this to unlimited  ???
/*R*/
use arg fmstring, arg.1, arg.2, arg.3, arg.4,  arg.5, arg.6,
/*R*/
                , arg.7, arg.8, arg.9, arg.10, arg.11, arg.12
/*R*/

/*R*/
org_fmstring = fmstring
/*R*/

/*R*/
-- handle any %% in format string  '19'x is off keyboard (in most
countries??) /*R*/
fmstring = CHANGESTR("%%",fmstring,'19'x)
/*R*/

/*R*/
-- loop over "%" in fmstring
/*R*/
aix = 0
/*R*/
do while pos("%",fmstring) > 0
/*R*/
  parse value fmstring  with part1 '%' fmstring
/*R*/
  aix = aix + 1
/*R*/

/*R*/
  -- the first pos in fmstring is a modifier[+-0],
/*R*/
  --  a width [n[.n]]  or a type [dfs] in this order
/*R*/
  if POS(SUBSTR(fmstring,1,1),modifiers) > 0 then do
/*R*/
     modifier = SUBSTR(fmstring,1,1)
/*R*/
     fmstring = SUBSTR(fmstring,2)
/*R*/
  end
/*R*/
  else
/*R*/
    modifier  = ""
/*R*/

/*R*/
  -- is there anything before types [dfs]
/*R*/
  -- loop over types and find the first data type modifier postition
/*R*/
  nextpos  = 9999
/*R*/
  do ix = 1 to LENGTH(types)
/*R*/
    type = SUBSTR(types,ix,1)
/*R*/
    wpos = POS(type,fmstring)
/*R*/
    if wpos > 0 ,
/*R*/
    &  wpos < nextpos then
/*R*/
      nextpos = wpos
/*R*/
  end
/*R*/
  if nextpos < 1,
/*R*/
  |  nextpos = 9999 then
/*R*/
  do
/*R*/
    return "Unknown type format:" org_fmstring
/*R*/
  end
/*R*/
  else
/*R*/
  do
/*R*/
    thistype =  SUBSTR(fmstring,nextpos,1)
/*R*/
    if nextpos > 1 then
/*R*/
    do
/*R*/
      width    = SUBSTR(fmstring,1,nextpos -1)
/*R*/
    end
/*R*/
    else
/*R*/
    do
/*R*/
      width = MIN(LENGTH(arg.aix),POS('.',arg.aix))
/*R*/
      if width = 0 then
/*R*/
         width = LENGTH(arg.aix)
/*R*/
      if thistype = 'd',
/*R*/
      & modifier = '+' then
/*R*/
        width = width +1
/*R*/
    end
/*R*/
    fmstring =  SUBSTR(fmstring,nextpos+1)
/*R*/
  end
/*R*/

/*R*/
  parse value width with length '.' decimals
/*R*/
  if length    = "" then
/*R*/
     length = MIN(LENGTH(arg.aix),POS('.',arg.aix))
/*R*/
  if decimals  = "" then
/*R*/
     decimals = 0
/*R*/

/*R*/
  select
/*R*/
    ------------------------------   handle  strings
/*R*/
    when thistype = 's' then
/*R*/
    do
/*R*/
      select
/*R*/
        when modifier = '+' then
/*R*/
           arg.aix = RIGHT(arg.aix,length)
/*R*/
        when modifier = '-' then
/*R*/
           arg.aix = LEFT(arg.aix,length)
/*R*/
        when modifier = '0' then
/*R*/
           arg.aix = RIGHT(arg.aix,length,'0')
/*R*/
        otherwise
/*R*/
         arg.aix = SUBSTR(arg.aix,1,length)
/*R*/
       end
/*R*/
    end
/*R*/
    ------------------------------   handle  Integers
/*R*/
    when thistype  = 'd' then
/*R*/
    do
/*R*/
      if VERIFY(arg.aix,"+-0123456789") = 0  then do
/*R*/
        arg.aix = STRIP(arg.aix,"L",0)
/*R*/
        select
/*R*/
          when modifier = '+' then
/*R*/
            if arg.aix > 0  then
/*R*/
              arg.aix   = RIGHT('+' || arg.aix, length+1)
/*R*/
            else
/*R*/
              arg.aix   = RIGHT(arg.aix, length+1)
/*R*/
          when modifier = '-' then
/*R*/
              arg.aix    =RIGHT(arg.aix, length+1)
/*R*/
          when modifier = '0' then
/*R*/
           if  arg.aix > 0 then
/*R*/
             arg.aix    = RIGHT(arg.aix,length,'0')
/*R*/
           else
/*R*/
                          -- Numeric digits may influence outcome here
/*R*/
             arg.aix    = "-"||RIGHT(-arg.aix,length-1,'0')
/*R*/
          otherwise
/*R*/
             arg.aix   = RIGHT(arg.aix, length)
/*R*/
        end
/*R*/
      end
/*R*/
    end
/*R*/
    ------------------------------   handle  real numbers
/*R*/
    when thistype = 'f' then
/*R*/
    do
/*R*/
      if VERIFY(arg.aix,"+-.0123456789") = 0  then do
/*R*/
        -- remove Leading/Trailing zeroes  if any
/*R*/
        arg.aix = STRIP(arg.aix,"B",0)
/*R*/
        -- add a decimal point if missing
/*R*/
        if decimals  > 0,
/*R*/
        & POS('.',arg.aix) = 0 then
/*R*/
        do
/*R*/
           arg.aix  = arg.aix||".0"
/*R*/
        end
/*R*/

/*R*/
        -- remove "+" if present
/*R*/
        arg.arg.ix = CHANGESTR('+',arg.aix,'')
/*R*/

/*R*/
        -- round the decimals if necessary
/*R*/
        parse value arg.aix  with . '.' decimalnumbers
/*R*/
        if decimals  < LENGTH(decimalnumbers)  then
/*R*/
          arg.aix = roundup(arg.aix,decimals)
/*R*/

/*R*/
        -- add zeroes to end of decimals if needed
 /*R*/
        if decimals  > LENGTH(decimalnumbers)  then
/*R*/
          arg.aix = arg.aix||LEFT('0',decimals-LENGTH(decimalnumbers),'0')
/*R*/

/*R*/
        select
/*R*/
          when modifier = '+' then
/*R*/
            if arg.aix > 0  then
/*R*/
              arg.aix   = RIGHT('+'||strip(arg.aix),length+decimals+1)
/*R*/
            else
/*R*/
              arg.aix   = RIGHT(strip(arg.aix),length+decimals+1)
/*R*/
          when modifier = '-' then
/*R*/
            if arg.aix > 0  then
/*R*/
              arg.aix   = LEFT(arg.aix,length+decimals+1)
/*R*/
            else
/*R*/
              arg.aix   = LEFT(arg.aix,length+decimals+1)
/*R*/
          when modifier = '0' then
/*R*/
           if  arg.aix > 0 then
/*R*/
           do
/*R*/
             arg.aix = CHANGESTR(" ",RIGHT(arg.aix,length+decimals+1),"0")
/*R*/
           end
/*R*/
           else do
/*R*/
             arg.aix = CHANGESTR("-",arg.aix," ")
/*R*/
             arg.aix = CHANGESTR(" ",right(arg.aix,length+decimals+1,),"0")
/*R*/
             arg.aix = OVERLAY("-",arg.aix,1,1)
/*R*/
           end
/*R*/
          otherwise -- no modifier  default is right adjust
/*R*/
            if arg.aix > 0  then
/*R*/
              arg.aix = RIGHT(strip(arg.aix),length+decimals+1)
/*R*/
            else
/*R*/
              arg.aix = RIGHT(strip(arg.aix),length+decimals+1)
/*R*/
        end  --select
/*R*/
      end  -- if VERIFY
/*R*/
    end --do
/*R*/
  end  -- select
/*R*/
  fmstring = part1 || arg.aix  || fmstring
/*R*/
end -- do while POS("%",fmstring) > 0
/*R*/

/*R*/
-- handle any %% in format string
/*R*/
fmstring = CHANGESTR('19'x,fmstring,"%")
/*R*/

/*R*/
return fmstring
/*R*/
-- -------------------------------------------------- 
/*R*/
roundup:  procedure
/*R*/
  --  round number  to decimals  decimal places
/*R*/
  use arg  number, decimals
/*R*/

/*R*/
  ln  = LENGTH(number)
/*R*/
  saved = digits()
/*R*/
  if ln > saved then
/*R*/
    numeric digits ln
/*R*/

/*R*/
  parse value number with whole '.' rest
/*R*/
  roundwith =  "0."||RIGHT("5",decimals+1,"0")
/*R*/
  number = number + roundwith
/*R*/
  parse value number with whole '.' rest
/*R*/

/*R*/
  number  = whole||.||SUBSTR(rest,1,decimals)
/*R*/
  number  = 1 + number - 1
/*R*/

/*R*/
  if ln > saved then
/*R*/
    numeric digits saved
/*R*/

/*R*/
return  number
/*R*/

---  end of snippet ----------------------------------

/dg



0
Reply Dan 11/2/2007 8:49:06 PM

| Dan van Ginhoven wrote:
| Hi.
|
| Mr Coffee asked for a pretty print function  some time ago.
| With the handy string functions  like LEFT(), Right(), Center(), Format(),
| and so on there is not much demand for such a function.
|
| In  *xenix  the printf() and sprintf() functions are widely used though.
| So I wrote a simple implementation of some of sprintf 's functionality.
| Please suggest improvements. I haven't got time to respond daily though.
|
| I  used "classic" rexx.  Would an  oo approach be better?

Your code is not "classic" REXX.



-----[-----snipped-----]-----

| sprintf: procedure
| -- limited to 12 arguments  presently
| -- how to change this to unlimited  ???
| use arg fmstring, arg.1, arg.2, arg.3, arg.4,  arg.5, arg.6,
|                , arg.7, arg.8, arg.9, arg.10, arg.11, arg.12

 arg fmstring
 arg.=''
 nargs=arg()-1    /*contains # of args not counting fmstring*/

   do j=1 to nargs
   arg.j=arg(j+1)
   end
_____________________________________________________Gerard S.





0
Reply Gerard 11/3/2007 12:11:57 AM


On Nov 2, 8:49 pm, "Dan van Ginhoven" <danfa...@hotmail.com> wrote:

Hi Dan,

looks good!  I'm not a *xenix user, but I do have experience of a
language that includes a cut down version of sprintf.  I believe that
%s should return the entire string.

I had to change your line
>   say sprintf("%s %s %3.1f C %s", tdate.ix , ttime.ix, ttemp.ix, weekday )
to
say sprintf("%s %5s %3.1f C %s", tdate.ix , ttime.ix, ttemp.ix,
weekday )
in order to prevent truncation of the time after the decimal point.

Keep up the good work.

As for providing an OO version, just for information:  Rick sort of
threw down a challenge to us to write a java-style formatting class
for ooRexx - I don't know whether anyone has yet taken up that
challenge.

Jon

0
Reply Sahananda 11/4/2007 8:55:35 AM

| Jon Sahananda wrote:
|> Dan van Ginhoven wrote:
| Hi Dan,
|
| looks good!  I'm not a *xenix user, but I do have experience of a
| language that includes a cut down version of sprintf.  I believe that
| %s should return the entire string.
|
| I had to change your line
|>   say sprintf("%s %s %3.1f C %s", tdate.ix , ttime.ix, ttemp.ix, weekday )
| to
| say sprintf("%s %5s %3.1f C %s", tdate.ix , ttime.ix, ttemp.ix,
| weekday )
| in order to prevent truncation of the time after the decimal point.
|
| Keep up the good work.
|
| As for providing an OO version, just for information:  Rick sort of
| threw down a challenge to us to write a java-style formatting class
| for ooRexx - I don't know whether anyone has yet taken up that
| challenge.


Yes, I also noticed the trucation of strings (to the
length of the digits up to the decimal point).



In the following snippet of your code:

====================================================
  select
    ------------------------------   handle  strings
    when thistype = 's' then
    do
      select
        when modifier = '+' then
           arg.aix = RIGHT(arg.aix,length)
        when modifier = '-' then
           arg.aix = LEFT(arg.aix,length)
        when modifier = '0' then
           arg.aix = RIGHT(arg.aix,length,'0')
        otherwise
         arg.aix = SUBSTR(arg.aix,1,length)
       end
    end
====================================================

I changed it to:

----------------------------------------------------
  select                       /* handle  strings.*/
  when thistype=='s' then
    select
    when modifier=='+' then arg.aix=right(arg.aix,length)
    when modifier=='-' then arg.aix= left(arg.aix,length)
    when modifier==0   then arg.aix=right(arg.aix,length,0)
    otherwise               arg.aix= left(arg.aix,length)
    end
----------------------------------------------------

I changed the comparators from equal to exactly equal,
  eliminated the  DO --- END,
  change the last  SUBSTR  to  LEFT,
  and combined\ the  WHEN --- THEN  clauses to one line,
  and did some further text alignment.

You can now see that  WHEN MODIFIER  is a minus sign,
  it's the same as the OTHERWISE clause, so it's now:

,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  select                       /* handle  strings.*/
  when thistype=='s' then
    select
    when modifier=='+' then arg.aix=right(arg.aix,length)
    when modifier==0   then arg.aix=right(arg.aix,length,0)
    otherwise               arg.aix= left(arg.aix,length)
    end
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,





I also changed the following snippet of your code from:

===============================================================
          otherwise -- no modifier  default is right adjust
            if arg.aix > 0  then
              arg.aix = RIGHT(strip(arg.aix),length+decimals+1)
            else
              arg.aix = RIGHT(strip(arg.aix),length+decimals+1)
===============================================================

to:

-------------------------------------------------------------------
                           /*no modifier default is right adjust.*/
          otherwise arg.aix=right(strip(arg.aix),length+decimals+1)
-------------------------------------------------------------------

I hope that wasn't a coding error.



Likewise, I also changed all examples in your REXX code
from      xxx=substr(yyy,1,length)
  to      xxx=left(yyy,length)




I deleted the somewhat confusting syntax of the ommited third
argument in:

   arg.aix = CHANGESTR(" ",right(arg.aix,length+decimals+1,),"0")

If now looks like:

   arg.aix=changestr(" ",right(arg.aix,length+decimals+1),0)



Note that several statements that use  CHANGESTR  can be changed
from:

arg.aix =changestr(' ',right(arg.aix,length+decimals+1),0)
arg.aix =changestr(' ',right(arg.aix,length+decimals+1),0)
fmstring=changestr('19'x,fmstring,"%")  /*handle any %% in format string*/

to the use of the  TRANSLATE  built-in function which is faster.




I also change the preamble of the  SPRINTF  procedure to:

=========================================================================
sprintf: procedure
/*----------------------------------------------------------------------+
 | see Unix/C/perl sprintf documentation for general usage              |
 | Implemented datatypes                                                |
 |    d integer                                                         |
 |    f float fixed decimal                                             |
 |    s string                                                          |
 |    default none                                                      |
 |                                                                      |
 | Implemented width modifiers                                          |
 |     n      length of element                                         |
 |     n1.n2                                                            |
 |           n1=length before decimal point including +/- default length|
 |           n2=number of decimal places      default zero              |
 |     decimals will be rounded to n2 positions                         |
 |                                                                      |
 | Implemented modifiers                                                |
 |      %%  is  "%"                                                     |
 |      + prefix postive number with "+" and right adjust               |
 |      + right  adjust string                                          |
 |      - left  justify number                                          |
 |      - left  adjust string                                           |
 |      0 right adjust zero fill for numbers and strings                |
 |      default for strings "as is"                                     |
 |      default for numbers is right adjust                             |
 +----------------------------------------------------------------------*/

types    ='dfs'
modifiers="+-0"
parse arg fmstring
org_fmstring=fmstring
arg.=

  do j=1 to arg()-1
  arg.j=arg(j+1)
  end
=========================================================================

making the comments much easier to scan and also making the   ARG.
variables now limitless.


I also made a lot of other simple changes, but I don't want to turn this
into a tome longer than the original posting. ___________________Gerard S.


 


0
Reply Gerard 11/4/2007 3:47:08 PM

| Dan van Ginhoven wrote:
| Hi.
|
| Mr Coffee asked for a pretty print function  some time ago.
| With the handy string functions  like LEFT(), Right(), Center(), Format(),
| and so on there is not much demand for such a function.
|
| In  *xenix  the printf() and sprintf() functions are widely used though.
| So I wrote a simple implementation of some of sprintf 's functionality.
| Please suggest improvements. I haven't got time to respond daily though.

----snipped----

I looked at your   ROUNDUP  procedure (below):

====================================================
roundup:  procedure
  --  round number  to decimals  decimal places
  use arg  number, decimals

  ln  = LENGTH(number)
  saved = digits()
  if ln > saved then
    numeric digits ln

  parse value number with whole '.' rest
  roundwith =  "0."||RIGHT("5",decimals+1,"0")
  number = number + roundwith
  parse value number with whole '.' rest

  number  = whole||.||SUBSTR(rest,1,decimals)
  number  = 1 + number - 1

  if ln > saved then
    numeric digits saved

return  number
===================================================

and reduced it to this:

---------------------------------------------------
roundup: procedure; parse arg n,ddd     /*round N to DDD decimal places.*/
if length(n)>digits() then numeric digits length(n)
n=n+'0.'||right(5,ddd+1,0)
parse var n whole '.' rest
return whole'.'left(rest,1,ddd)+0
---------------------------------------------------


Is there any reason that you can't use the  FORMAT
bif  to accomplish the same thing  (with added sign
preservation)? ______________________________Gerard S.




0
Reply Gerard 11/4/2007 5:01:09 PM

| Dan van Ginhoven wrote:
| Hi.
|
| Mr Coffee asked for a pretty print function  some time ago.
| With the handy string functions  like LEFT(), Right(), Center(), Format(),
| and so on there is not much demand for such a function.
|
| In  *xenix  the printf() and sprintf() functions are widely used though.
| So I wrote a simple implementation of some of sprintf 's functionality.
| Please suggest improvements. I haven't got time to respond daily though.

-----[-----snipped------]-----

|    -- remove "+" if present
|    arg.arg.ix = CHANGESTR('+',arg.aix,'')

what did you really intend here as the
     ARG.ARG.IX   is never used elsewhere ?

Perhaps:     arg.aix =  ...        _______________Gerard S.

 


0
Reply Gerard 11/4/2007 5:52:26 PM

Hi Gerald!

About  roundup.

Yes.I  had trouble to make FORMAT() respect and use  numeric settings.
Format turned numbers with more than 9 figures into E-format.
Now when I try  to repeat the error it works OK.
Probably had some other bug when I tested it.
/dg


"Gerard Schildberger" <Gerard46@rrt.net> wrote in message
news:472dfa72$1@news.702com.net...
> | Dan van Ginhoven wrote:
> | Hi.
> |
> | Mr Coffee asked for a pretty print function  some time ago.
> | With the handy string functions  like LEFT(), Right(), Center(),
Format(),
> | and so on there is not much demand for such a function.
> |
> | In  *xenix  the printf() and sprintf() functions are widely used though.
> | So I wrote a simple implementation of some of sprintf 's functionality.
> | Please suggest improvements. I haven't got time to respond daily though.
>
> ----snipped----
>
> I looked at your   ROUNDUP  procedure (below):
>
> ====================================================
> roundup:  procedure
>   --  round number  to decimals  decimal places
>   use arg  number, decimals
>
>   ln  = LENGTH(number)
>   saved = digits()
>   if ln > saved then
>     numeric digits ln
>
>   parse value number with whole '.' rest
>   roundwith =  "0."||RIGHT("5",decimals+1,"0")
>   number = number + roundwith
>   parse value number with whole '.' rest
>
>   number  = whole||.||SUBSTR(rest,1,decimals)
>   number  = 1 + number - 1
>
>   if ln > saved then
>     numeric digits saved
>
> return  number
> ===================================================
>
> and reduced it to this:
>
> ---------------------------------------------------
> roundup: procedure; parse arg n,ddd     /*round N to DDD decimal places.*/
> if length(n)>digits() then numeric digits length(n)
> n=n+'0.'||right(5,ddd+1,0)
> parse var n whole '.' rest
> return whole'.'left(rest,1,ddd)+0
> ---------------------------------------------------
>
>
> Is there any reason that you can't use the  FORMAT
> bif  to accomplish the same thing  (with added sign
> preservation)? ______________________________Gerard S.
>
>
>
>


0
Reply Dan 11/4/2007 8:20:36 PM

| Dan van Ginhoven wrote:
| About  roundup.
|
| Yes.I  had trouble to make FORMAT() respect and use  numeric settings.
| Format turned numbers with more than 9 figures into E-format.
| Now when I try  to repeat the error it works OK.
| Probably had some other bug when I tested it.
| /dg

What if you would just set  NUMERIC DIGITS 2000
(or some such number that would conceivably be used
as a maximum, presumably for a printout or screen
display)?  _____________________________________Gerard S.

The only places it would effect performance is in the
few additions in the  ROUNDUP  procedure.



|> Gerard Schildberger wrote:
-----[snipped]-----
|> Is there any reason that you can't use the  FORMAT
|> bif  to accomplish the same thing  (with added sign
|> preservation)? ______________________________Gerard S.


0
Reply Gerard 11/4/2007 9:52:10 PM

| Jon Sahananda wrote:
|> Dan van Ginhoven wrote:
| Hi Dan,
|
| looks good!  I'm not a *xenix user, but I do have experience of a
| language that includes a cut down version of sprintf.  I believe that
| %s should return the entire string.

----[----snipped----]----

I fixed the problem of  %s  not returning the whole string  (it was
finding a decimal point in the string and using that length instead).

I also re-worked most  (well, ok, all) of the code, using shorter
REXX variables to make the logic fit on a single line.  I removed
roughly 100 statements from the orignal code.   I also removed
defaults if the default was specified or was used as a placeholder,
i.e.:          xxx=right(yyy,10,)

I was having trouble visualizing the major  DO  loop,  so I added
a prefix loop indicator (in the form of a REXX comment).

I also added a DO loop index (#) instead of incrmenting it manualy
in the code  (AIX).       ARG.AIX     is now known as  @.#

I also made each of the   XTYPEs  parsers into subroutines to make
the code much easier to read and you can get an overview much
simplier.   I imagine you can change the code back to use the
original REXX variable names.  _______________________________Gerard S.



=======================================================================
/**/ trace o; signal on novalue
tdate.1='2007-10-01'; ttime.1=07.15; ttemp.1=  5
tdate.2='2007-10-02'; ttime.2=07.14; ttemp.2=  8
tdate.3='2007-10-03'; ttime.3=07.01; ttemp.3= 11
tdate.4='2007-10-04'; ttime.4=07.14; ttemp.4=  6
tdate.5='2007-10-05'; ttime.5=07.00; ttemp.5=  5
tdate.6='2007-10-06'; ttime.6=06.48; ttemp.6=  2
tdate.7='2007-10-07'; ttime.7=07.15; ttemp.7= -1.5
tdate.8='2007-10-08'; ttime.8=07.14; ttemp.8= -2.5
tdate.9='2007-10-09'; ttime.9=07.21; ttemp.9= +3.599
tdate.0=9
say sprintf("%-10s %+9s %6s %s", '   date   ',"dayofweek"," time", 'temperature')
say sprintf("%-10s %+9s %6s %s", '----------',"---------","-----", '-----------')

if 'f0'x==0 then degc='a1'x              /*if  EBCDIC, then use  x'a1'. */
                 degc='f8'x              /*if   ASCII, then use  x'f8'. */
degc=degc'C'                             /*append C to the degree symbol*/

  do j=1 to tdate.0
  _=j//7
    select                     /*calc DayOfWeek, this is data dependent.*/
    when _==0 then weekday='Sunday'
    when _==1 then weekday='Monday'
    when _==2 then weekday='Tuesday'
    when _==3 then weekday='Wednesday'
    when _==4 then weekday='Thursday'
    when _==5 then weekday='Friday'
    when _==6 then weekday='Saturday'
    end
  say sprintf("%-10s %+9s %6s %5.1f" degc,tdate.j,weekday,ttime.j,ttemp.j)
  end

exit 0


sprintf: procedure; parse arg y
/*----------------------------------------------------------------------+
 |               see Unix/C/perl sprintf documentation for general usage|
 | Implemented datatypes                                                |
 |    d  integer                                                        |
 |    f  float fixed decimal                                            |
 |    s  string                                                         |
 |    default  none                                                     |
 |                                                                      |
 | Implemented width modifiers                                          |
 |     n      length of element                                         |
 |     n1.n2                                                            |
 |           n1=length before decimal point including +|- default length|
 |           n2=number of decimal places,  default zero                 |
 |     decimals will be rounded to n2 positions                         |
 |                                                                      |
 | Implemented modifiers                                                |
 |      %%  is  "%"                                                     |
 |      +  prefix postive number with "+" and right adjust              |
 |      +  right  adjust string                                         |
 |      -  left  justify number                                         |
 |      -  left  adjust string                                          |
 |      0  right adjust zero fill for numbers and strings               |
 |      default for strings "as is"                                     |
 |      default for numbers is right adjust                             |
 +----------------------------------------------------------------------*/
                           /* handle any   %%   in format string.       */
                           /* '19'x isn't on keyboards in most countries*/
y=changestr("%%",y,'19'x)
@.=''                                          /*nullify all  @.  (args)*/
types    ='dfs'                                /*this is case sensitive.*/
modifiers='+-0'

      do j=1 to arg()      /*tokenize all possible arguments, no limit. */
      @.j=arg(j+1)         /*assign the argument to an array element.   */
      w.j=length(@.j)      /*also, compute the length of the argument.  */
      end

/*#*/  do #=1 while pos('%',y)\==0                /*process each % in y.*/
/*#*/  parse var y part1 '%' y
/*#*/
/*#*/                     /*the 1st pos in  y  is a  modifier[+-0],   a */
/*#*/                     /*width [n[.n]] or a type [dfs] in this order.*/
/*#*/  mof=''
/*#*/  if pos(left(y,1),modifiers)\==0 then parse var y mof 2 y
/*#*/  nextpos=99999
/*#*/
/*#*/           /*is there anything before types [dfs] ?                */
/*#*/           /*loop over types & find the 1st data type modifier pos.*/
/*#*/
/*#*/    do jj=1 to length(types)
/*#*/    _=pos(substr(types,jj,1),y)
/*#*/    if _\==0 & _<nextpos then nextpos=_
/*#*/    end
/*#*/
/*#*/  if nextpos==0 then return 'Unknown type format:' arg(1)
/*#*/  xtype=substr(y,nextpos,1)
/*#*/
/*#*/  if nextpos>1 then width=left(y,nextpos-1)
/*#*/               else do
/*#*/                    if xtype=='s' then width=w.#
/*#*/                                  else width=min(w.#,pos('.',@.#))
/*#*/                    if xtype=='d' & mof=='+' then width=width+1
/*#*/                    end
/*#*/
/*#*/  y=substr(y,nextpos+1)
/*#*/  parse var width len '.' decs
/*#*/  if len=='' then len=min(w.#,pos('.',@.#))
/*#*/  if decs=='' then decs=0
/*#*/
/*#*/    select
/*#*/    when xtype=='s' then @.#=sprintf_s()
/*#*/    when xtype=='d' then @.#=sprintf_d()
/*#*/    when xtype=='f' then @.#=sprintf_f()
/*#*/    end
/*#*/
/*#*/  y=part1 || @.# || y
/*#*/  end

y=translate(y,'%',"19"x)                /*handle any %% in format string*/
return y


sprintf_s:   /*--------------------------------------------------strings*/
if mof=='+' then return right(@.#,len)
if mof==0   then return right(@.#,len,0)
return left(@.#,len)


sprintf_d:   /*-------------------------------------------------integers*/
if verify(@.#,'+-0123456789')\==0 then return @.#
@.#=strip(@.#,'L',0)
if mof=='+' then if @.#>0 then return right('+'@.#,len+1)
                          else return right(@.#,len+1)
if mof=='-' then return right(@.#,len+1)
if mof==0   then if @.#>0 then return right(@.#,len,0)
                          else return @.#='-'right(-@.#,len-1,0)
return right(@.#,len)


sprintf_f:   /*----------------------------------------------------reals*/
if verify(@.#,'+-.0123456789')\==0 then return @.#
@.#=strip(@.#,,0)                   /*remove leading/trail zeroes if any*/
if decs>0 & pos('.',@.#)==0 then @.#=@.#'.0'       /*no period?  Add one*/
@.#=strip(@.#,'L',"+")              /*remove leading    +    if present.*/
parse var @.# '.' dn
if decs<length(dn) then @.#=roundup(@.#,decs)      /*round if necessary.*/
if decs>length(dn) then @.#=@.#||left(0,decs-length(dn),0)   /*append 0s*/
if mof=='+' then if @.#>0 then return right('+'strip(@.#),len+decs+1)
                          else return right(strip(@.#),len+decs+1)
if mof=='-' then if @.#>0 then return left(@.#,len+decs+1)
                          else return left(@.#,len+decs+1)
if mof==0   then if @.#>0 then return translate(right(@.#,len+decs+1),0)
                          else return sprintf_o()
return right(strip(@.#),len+decs+1)             /*no mod, right justify.*/


sprintf_o: @.#=translate(@.#,,'_')          /*trans underbars to blanks.*/
@.#=right(@.#,len+dec+1,0)                  /*add leading zeroes.       */
return overlay('-',@.#)                     /*leading char to a minus.  */


roundup: procedure; parse arg n,ddd     /*round N to DDD decimal places.*/
if length(n)>digits() then numeric digits length(n)
n=n+'0.'right(5,ddd+1,0); parse var n whole '.' fract
return whole'.'left(fract,1,ddd)+0
==========================================================================

 


0
Reply Gerard 11/5/2007 2:49:57 AM

In Message-ID:<472e847a@news.702com.net>,
"Gerard Schildberger" <Gerard46@rrt.net> wrote: 

>if 'f0'x==0 then degc='a1'x              /*if  EBCDIC, then use  x'a1'. */
>                 degc='f8'x              /*if   ASCII, then use  x'f8'. */

     I like this technique for the program to decide EBCDIC vs.
ASCII.  But aren't you missing an "else"?

-- 
Arthur T. - ar23hur "at" intergate "dot" com
Looking for a z/OS (IBM mainframe) systems programmer position
0
Reply Arthur 11/5/2007 3:11:21 AM

| Arthur T. wrote:
|> Gerard Schildberger wrote:
|
|>if 'f0'x==0 then degc='a1'x              /*if  EBCDIC, then use  x'a1'. */
|>                 degc='f8'x              /*if   ASCII, then use  x'f8'. */

|     I like this technique for the program to decide EBCDIC vs.
| ASCII.  But aren't you missing an "else"?

To quote many a REXX programm, "oh crap!"  __________________Gerard S.

 


0
Reply Gerard 11/5/2007 5:50:21 AM

Thanks Gerard!

You have done a great job ( and had all the fun  I hope).

I had to change your variable names @,  and #
since  oorexx  does not include these in Symbol names.

I don't agree with the need to have as few lines as possible, unless I 'm on
a mainframe editor.
Personaly I'd rather have a greater readability, adding blank lines and so
on.

Again, thanks!
/dg


0
Reply Dan 11/5/2007 5:43:53 PM

| Dan van Ginhoven:
| Thanks Gerard!
|
| You have done a great job ( and had all the fun  I hope).
|
| I had to change your variable names @,  and #
| since  oorexx  does not include these in Symbol names.
|
| I don't agree with the need to have as few lines as possible, unless I 'm on
| a mainframe editor.
| Personaly I'd rather have a greater readability, adding blank lines and so
| on.
|
| Again, thanks!

I didn't reduce the number of lines just for the sake of
reduction, but to get more code on a "screen" so I could
get a better visualize the overall code flow, for a lack
of a better term.  I use a PC (with KEDIT) with a 50x80
screen.

In my version of KEDIT, that's the maximum screen size
that is supported.  Bummer.  I also don't have a printer,
so making a hardcopy is out.   It's the screen viewing
or nothing.

By making the REXX variables shorter, I was able to
condense a lot of
  IF ... THEN   DO ... END   ELSE DO ... END  statements
to fewer lines, especially where the   DO ... END   was
just one REXX clause, or the REXX clause followed the
THEN on the next line  (same with the ELSE clause).

It's more different to follow someone else's logic than
if I had originaly written the code, so I had a steeper
steeper learning curve to come up to speed.  That was
one main reason that I broken part of the parsing into
subroutines.  In my old(er) age, the amount of code I
can "hold" in my head is getting more and more
miniscule, soon I will be down to holding parts of a
clause, heaven help me if it continues on another line.

Now that I understand (I think I do, that is) what the
code is doing and how it's doing it, I could go back
and make the variable names more descriptive and add a
lot more white space everywhere  (not to mention block
style comments, sometimes called flower boxes).

It was a fun project. _____________________________Gerard S.


 


0
Reply Gerard 11/5/2007 7:25:49 PM

Hello G.

I did a few changes to your code.

1. I saved the original string  y:
errmsg = y

2. In order to have an error message in
    select
    when xtype == 's' then arg.ix = sprintf_s()
    when xtype == 'd' then arg.ix = sprintf_d()
    when xtype == 'f' then arg.ix = sprintf_f()
    otherwise         return "invalid modifier in" errmsg      --DG
    end

3. I added one line  for the case when there is no length modifier and no
decimalpoint  (Integer)
  y = substr(y,nextpos+1)
  parse var width len '.' decs
  if len ==  '' then len=min(w.ix,pos('.',arg.ix))
  if len == 0   then len=length(arg.ix)                       --DG
  if decs == '' then decs=0

4. At the end of  sprintf_f:    translate needed tableo, tablei
if mof == 0   then if arg.ix > 0 then return
translate(right(arg.ix,len+decs+1),'0',' ')  --DG
                                 else return sprintf_o()

5.  A few changes  here
sprintf_o:
arg.ix = strip(translate(arg.ix,,'-'))           --DG     (not underscore)
/* trans minus to blanks  and strip spaces*/
arg.ix = right(arg.ix,len + decs + 1,0)     --DG decs  replaced dec     /*
add leading zeroes.       */
return overlay('-',arg.ix)
/* leading char to a minus.  */

6.  in sprintf_d:
if mof == 0   then if arg.ix > 0 then return right(arg.ix,len,0)
                                 else return '-'right(-arg.ix,len-1,0)  --DG
removed arg.ix =

Tested with the below variations, it works OK:
say "------------------------------------------------------------"
say "         1         2         3         4         5         6"
say "1234+6789+1234+6789+1234+6789+1234+6789+1234+6789+1234+6789+"
say  sprintf("%%+3s<%+3s>", "abcdefg")
say  sprintf("%%+13s<%+13s>", "abcdefg")
say  sprintf("%%-3s<%-3s>", "abcdefg")
say  sprintf("%%-13s<%-13s>", "abcdefg")
say  sprintf("%%013s<%013s>", "abcdefg")
say " "
say  sprintf("%%d   <%d>",4096)
say  sprintf("%%d   <%d>",-4096)
say  sprintf("%%+d  <%+d>",4096)
say  sprintf("%%+d  <%+d>",-4096)
say  sprintf("%%3d  <%3d>",00004096)
say  sprintf("%%16d <%16d>",1234567890123456)
say  sprintf("%%8d  <%8d>",4096)
say  sprintf("%%08d <%08d>",4096)
say  sprintf("%%08d <%08d>",-4096)  -- 1
say  sprintf("%%-8d <%-8d>",4096)
say  sprintf("%%-8d <%-8d>",-4096)
say  sprintf("%%0d  <%0d>",4096)
say  sprintf("%%0d  <%0d>",-4096)

say " "
say "------------------------------------------------------------"
say "         1         2         3         4         5         6"
say "1234+6789+1234+6789+1234+6789+1234+6789+1234+6789+1234+6789+"
say  sprintf("%%f      :%f",4096.1024)
say  sprintf("%%f      :%f",4096.599)
say  sprintf("%%6.1f   :%6.1f",4096.1024)
say  sprintf("%%6.6f   :%6.6f",4096.1024)
say  sprintf("%%+6.6f  :%+6.6f",4096.1024)
say  sprintf("%%-6.6f  :%-6.6f",-4096.1024)
say  sprintf("%%+6.6f  :%+6.6f",124096.1024)
say  sprintf("%%-6.6f  :%-6.6f",-124096.1024)
say  sprintf("%%010.6f :%010.6f",6789.1024)
say  sprintf("%%010.6f :%010.6f",-6789.1024)
say  sprintf("%%+7.6f  :%+7.6f",123456.1024)
say  sprintf("%%+7.6f  :%+7.6f",-123456.1024)
say  sprintf("%%+7.2f  :%+7.2f",-123456.4555)
say  sprintf("%%+7.2f  :%+7.2g",-123456.4555)  /* invalid modifier */

/dg




0
Reply Dan 11/5/2007 8:01:42 PM

| Dan van Ginhoven wrotea:
| Hello G.
|
| I did a few changes to your code.
|
| 1. I saved the original string  y:
| errmsg = y

(see below).




| 2. In order to have an error message in
|    select
|    when xtype == 's' then arg.ix = sprintf_s()
|    when xtype == 'd' then arg.ix = sprintf_d()
|    when xtype == 'f' then arg.ix = sprintf_f()
|    otherwise         return "invalid modifier in" errmsg      --DG
|    end

There is no need to "save" the original string  Y   as it can be
referenced as     ARG(1)    ______________________________Gerard S.




| 3. I added one line  for the case when there is no length modifier and no
| decimalpoint  (Integer)
|  y = substr(y,nextpos+1)
|  parse var width len '.' decs
|  if len ==  '' then len=min(w.ix,pos('.',arg.ix))
|  if len == 0   then len=length(arg.ix)                       --DG
|  if decs == '' then decs=0
|
| 4. At the end of  sprintf_f:    translate needed tableo, tablei

It only needs one of them, if any of tableo or tableei (but not
both) is ommitted, blanks are assumed.  __________________Gerard S.




| if mof == 0   then if arg.ix > 0 then return
| translate(right(arg.ix,len+decs+1),'0',' ')  --DG
|                                 else return sprintf_o()
|
| 5.  A few changes  here
| sprintf_o:
| arg.ix = strip(translate(arg.ix,,'-'))           --DG     (not underscore)

Another case of bad (old) eyes.   Well, it was close  (but no cigar).





| /* trans minus to blanks  and strip spaces*/
| arg.ix = right(arg.ix,len + decs + 1,0)     --DG decs  replaced dec     /*
| add leading zeroes.       */
| return overlay('-',arg.ix)
| /* leading char to a minus.  */
|
| 6.  in sprintf_d:
| if mof == 0   then if arg.ix > 0 then return right(arg.ix,len,0)
|                                 else return '-'right(-arg.ix,len-1,0)  --DG
| removed arg.ix =
|
| Tested with the below variations, it works OK:
| say "------------------------------------------------------------"
| say "         1         2         3         4         5         6"
| say "1234+6789+1234+6789+1234+6789+1234+6789+1234+6789+1234+6789+"
| say  sprintf("%%+3s<%+3s>", "abcdefg")
| say  sprintf("%%+13s<%+13s>", "abcdefg")
| say  sprintf("%%-3s<%-3s>", "abcdefg")
| say  sprintf("%%-13s<%-13s>", "abcdefg")
| say  sprintf("%%013s<%013s>", "abcdefg")
| say " "
| say  sprintf("%%d   <%d>",4096)
| say  sprintf("%%d   <%d>",-4096)
| say  sprintf("%%+d  <%+d>",4096)
| say  sprintf("%%+d  <%+d>",-4096)
| say  sprintf("%%3d  <%3d>",00004096)
| say  sprintf("%%16d <%16d>",1234567890123456)
| say  sprintf("%%8d  <%8d>",4096)
| say  sprintf("%%08d <%08d>",4096)
| say  sprintf("%%08d <%08d>",-4096)  -- 1
| say  sprintf("%%-8d <%-8d>",4096)
| say  sprintf("%%-8d <%-8d>",-4096)
| say  sprintf("%%0d  <%0d>",4096)
| say  sprintf("%%0d  <%0d>",-4096)
|
| say " "
| say "------------------------------------------------------------"
| say "         1         2         3         4         5         6"
| say "1234+6789+1234+6789+1234+6789+1234+6789+1234+6789+1234+6789+"
| say  sprintf("%%f      :%f",4096.1024)
| say  sprintf("%%f      :%f",4096.599)
| say  sprintf("%%6.1f   :%6.1f",4096.1024)
| say  sprintf("%%6.6f   :%6.6f",4096.1024)
| say  sprintf("%%+6.6f  :%+6.6f",4096.1024)
| say  sprintf("%%-6.6f  :%-6.6f",-4096.1024)
| say  sprintf("%%+6.6f  :%+6.6f",124096.1024)
| say  sprintf("%%-6.6f  :%-6.6f",-124096.1024)
| say  sprintf("%%010.6f :%010.6f",6789.1024)
| say  sprintf("%%010.6f :%010.6f",-6789.1024)
| say  sprintf("%%+7.6f  :%+7.6f",123456.1024)
| say  sprintf("%%+7.6f  :%+7.6f",-123456.1024)
| say  sprintf("%%+7.2f  :%+7.2f",-123456.4555)
| say  sprintf("%%+7.2f  :%+7.2g",-123456.4555)  /* invalid modifier */


 


0
Reply Gerard 11/5/2007 8:34:34 PM

....

And 7.

Your version of Roundup  does  not round but truncate  the decimals.
and it does not  handle zero decimals.

No solution.
Bedtime.
/dg



0
Reply Dan 11/5/2007 8:55:42 PM

 >And 7.

 > Your version of Roundup  does  not round but truncate  the decimals.
 
8. Rounding XOR truncating won't do, try sprintf("%1.1f %1.1f",1.25,1.35)
   which fails with Format()-like rounding XOR Left()-like truncating.
   The output should read "1.2 1.4".

9. Why *s*printf(), it's printf() formatting.

10. Why sprintf() numbers the original sprintf() cannot store?



---
0
Reply spamgate 11/5/2007 11:59:49 PM

 > try sprintf("%1.1f %1.1f",1.25,1.35)

Snafu. Try this instead:

This should sprint 7.32:

   f=7.325
   sprintf("%1.2f",f)

This should sprint 7.33:

   f=7.32501
   sprintf("%1.2f",f) 



---           
0
Reply spamgate 11/6/2007 12:49:42 AM

17 Replies
197 Views

(page loaded in 0.178 seconds)

Similiar Articles:














7/28/2012 10:49:18 PM


Reply: