Home | January Newsletter | About | Brochure | Contact | Migration | Papers | Downloads  
 
 
 
 
 
Good code: trim cleaned up

   const
      blank       = ' ';

   type
       pac80      = packed array [1..80] of char;

   {---------------------------------------------------}
   function trim (
                  buf         : pac80;
              var f_width     : integer)
         : pac80;

      {trim takes the 80-byte array passed and physically trims }
      {the leading blanks off (by left-shifting the text), and  }
      {logically trims any trailing blanks.  The resulting width}
      {is returned in "f_width".                                }
      {                                                         }
      {Example:       trim ('  cat  ', W);                      }
      {           returns:  'cat    ', and W = 3                }
      {                                                         }
      {Note: in left-shifting, the "new" characters at the end  }
      {of the buffer will be blanks.                            }

      const
         max_inx  = sizeof (buf);

      var
         first_inx: integer;  {index of first non-blank in buf}
         last_inx : integer;  {index of last non-blank in buf}
         inx      : integer;  {used to index in buf/new_buf}
         new_buf  : pac80;    {will hold left-shifted text}
         width    : integer;  {length of "trimmed" text}

      begin

            {Find first non-blank character in buf...}

      first_inx := 1;

      while (first_inx <= max_inx) and (buf [first_inx] = blank) do
         first_inx := first_inx + 1;

            {Continue only if we found a non-blank..   .}
            {Note that if the first non-blank is the    }
            {first character, then we don't need to     }
            {"shift" the text, so we can do a high-     }
            {speed copying of the data in buf to trim's }
            {functional result.                         }

      if first_inx > max_inx then
         begin                {buf is entirely blank}
         trim := ' ';         {pass back a blank buffer}
         width := 0;          {0 -> no non-blank text!}
         end

      else
         begin                {must copy data to new_buf}

               {First, determine width...}

               {Note that we can avoid a complex while}
               {condition because we know that we'll  }
               {find a non-blank before last_inx hits 0. }

         last_inx := max_inx;
         while buf [last_inx] = ' ' do
            last_inx := last_inx - 1;

         width := (last_inx - first_inx) + 1;

               {Copy the data to new_buf...}

               {Note that since the "for" below might not}
               {copy max_inx chars, we need to make sure }
               {that the trailing part of new_buf is     }
               {blanked out ... to be nice to the caller.}
               {This could be omitted if the definition  }
               {of "trim" made it clear that the trailing}
               {data should be undefined.                }
               {(See commentary for alternative)         }

         if width < max_inx then
            new_buf := ' ';      {blank out entire new_buf}

         for inx := 1 to width do
            new_buf [inx] := buf [inx + (first_inx - 1)];

         trim := new_buf;
         end;

      f_width := width;       {tell caller the width}

      end {trim proc};
Notes:
  • I'm assuming that, on average, the cost of blanking the entire NEW_BUF array is less than the cost of an additional FOR loop to blank out the tail end. This becomes less likely to be true as (a) new_buf gets bigger; (b) the probability of a large number of leading/trailing blanks decreases; and (c) the quality of compiler optimization increases.

    On the other hand, the *human* time to understand the new_buf := ' ' is significantly less than to understand an additional FOR loop. Remember to ask: will it take more time to write faster code than will ever be saved by that new code?

    Still, here's the FOR loop code...replace the "new_buf := ' ';" with:

                      {Blank out tail end}
    
             for inx := width + 1 to max_inx do
                new_buf [inx] := ' ';
    

  • Note that if your compiler doesn't support: <PAC> := ' ' to blank an entire PAC variable, then the trim := ' ' can be replaced easily (exercise for the reader).

  • We also avoided using any predefined functions, to preseve the comparison of coding style/methods.

  • I used a local variable, width, instead of the f_width parameter for efficiency. Since f_width is a call-by-reference, every use of it would require a memory access. width, being a local variable, is more likely to be promoted to a register by a compiler (and is therefore likely to be faster).

    In general, I avoid accessing simple by-reference parameters via this technique.

(Click here to go back to the commented bad version.)

(Click here to go back to the uncommented bad version.)
(Click here for the "How To Code Pascal" paper.)


(Updated 2000-05-04)


Back to top

 
   
  Home | January Newsletter | About | Brochure | Contact | Migration | Papers | Downloads  
 
All content copyright Resource3000 | Privacy Policy | www.Resource3000.com
 

Updated [3khat16.ico]HP3000 [3khat16.ico]3kMail [archive16.gif]