[Back to TUTOR SWAG index] [Back to Main SWAG index] [Original]

                             Turbo Pascal for DOS Tutorial
                                  by Glenn Grotzinger
             Part 7 -- Records usage and Mathematics Concepts of Computers
                    All parts copyright 1995 (c) by Glenn Grotzinger

            Here is a solution to part 6...

    {$B+}
    program part6;

      type
        ltrtype = array['A'..'Z'] of longint; {type dec for arrays}

      var
        ltrstorage, sums: ltrtype; { hold array and sums array }
        filelist, counts: text;
        filename: string;
        filenums: integer;
        ltrs: longint;

      procedure countletters(str: string; var ltrstorage, sums: ltrtype);
        var
          i: integer; {65..90}
          j: char;
        begin
          for i := 1 to length(str) do  { for length of string }
            for j := 'A' to 'Z' do    { for 26 letters }
              if upcase(str[i]) = j then
                begin
                  inc(ltrstorage[j]); {var = var + 1 }
                  inc(sums[j]);
                  ltrs := ltrs + 1;
                end;
        end;

      procedure countfile(filename: string; var ltrstorage:ltrtype; var
                          filenums: integer);
        var
          cntfile: text;
          chkstr: string;

        begin
          writeln('Processing ', filename);
          filenums := filenums + 1;
          assign(cntfile, filename);
          reset(cntfile);
          readln(cntfile, chkstr);
          while not eof(cntfile) do
            begin
              countletters(chkstr, ltrstorage, sums);
              readln(cntfile, chkstr);
            end;
          countletters(chkstr, ltrstorage, sums);
          close(cntfile);
        end;

      procedure writefdata(filename: string; var ltrstorage: ltrtype);
        var
          i: integer;
          j: char;
        begin
          { write headers }
          writeln(counts, 'Alphabetical Count Data':53);
          writeln(counts, 'for ':40, filename);
          writeln(counts);
          writeln(counts, 'Letter':10, 'Count':10, 'Letter':25, 'Count':10);
          for i := 1 to 13 do
            writeln(counts, chr(i+64):8, ltrstorage[chr(i+64)]:12,
                    chr(i+77):23, ltrstorage[chr(i+77)]:12);
          writeln(counts);
          writeln(counts);
          for j := 'A' to 'Z' do
            ltrstorage[j] := 0; { zero back out storage array }
        end;

      procedure doend(filenums: integer; ltrstorage: ltrtype);
        var
          i: integer;
          j: char;

        begin
          writeln(counts, filenums, ' files processed.');
          writeln(counts, ltrs, ' letters processed.');
          writeln(counts);
          writeln(counts);
          writefdata('all files', sums);
        end;

      begin
        assign(filelist, 'FILES.TXT');
        reset(filelist);
        assign(counts, 'FRNDDATA.TXT');
        rewrite(counts);
        readln(filelist, filename);
        filenums := 0;
        while (not eof(filelist)) and (filenums < 9) do
          begin
            countfile(filename, ltrstorage, filenums);
            writefdata(filename, ltrstorage);
            readln(filelist, filename);
          end;
        countfile(filename, ltrstorage, filenums);
        writefdata(filename, ltrstorage);
        doend(filenums, ltrstorage);
        if not eof(filelist) then
          writeln(counts, 'You gave me more than 10 files to process!');
        close(filelist);
        close(counts);
      end.

    On with the show....

    Records Definition
    ==================
            You can group up different types of data using records.  We MUST
    use the type statement pretty well to use this record type.  Here is an
    example of the defining of a record type in the type section.

      type
        employeerecord = record
          number: longint;
          surname: string[18];
          firstname: string[10];
          position: string[10];
          yrsworked: integer;
        end;

    As you can see, we are grouping many different types of information into
    one record type.  Then for the var section, all we need to do is define
    ONE variable to be of type employeerecord and we'll have the record variable.

    Accessing Records in a Program
    ==============================
            You can work with sections of a record variable, or the whole record
    variable.  The whole record variable can be accessed just by calling the
    record variable used.  The parts of it require the use of a . followed by
    the name of the part as specified in the type statement.  Also, the WITH
    command may be used to simplify typing in working with a specific record.
    The WITH command specifies a specific record variable to work with.  An
    example can be seen below.

    program tutorial18;

      type
        {Employeerecord as above}
      var
        workrecord: employeerecord;
      begin
        { get workrecord into memory here. }
        writeln(workrecord.number);
        writeln(workrecord.surname);
        writeln('I''m getting sick of typing workrecord all the time!');
        with workrecord do
          writeln(firstname);
          writeln(position);
          writeln(yrsworked);
        end;
      end.

    Hopefully, you can see what exactly is going on in working with records.
    WITH only reduces typing.  It's good to use to reduce clutter if you do
    a lot with one type of record in a section of code.  Also, keep in mind
    that record types can be used in an array....

      var
        workinfo: array[1..10] of employeerecord;

    Each section of the record works just like ordinary variables, and are
    addressable like ordinary variables.  Also the whole record itself can
    be addressable and moved around (written possibly to binary files?) to
    other like records....The above array can be addressed like this:

    3rd record in array, surname: workinfo[3].surname

    Mathematics Concepts in Computers
    =================================
    Hopefully, you got the mathematics lesson from someone, or are already
    familiar with conversion of number bases.  If not, I will run through
    a quick example...

    Convert 10 (base 10) to base 2.

    A base tells us how many numbers are used in counting.  Typical numerical
    usage is base 10 (we use the numbers 0-9 in that order -- we always start
    from zero in counting.   To look at this problem, we look at the right
    and move to the left with regards to number bases.  To use the example of
    543 in base 10, it's 5X10^2+4X10^1+3X10^0. Remember, anything to the 0th
    power is 1. All number bases work this way.  The exception is the changing
    of the multiple.  We use 10 in the example above.  Using that background,
    10 in base 10 is 1X10^1+0x10^0.  So, if we go through the process of actually
    converting a base from base 10....

    10 / 2 = 5 rem 0. (we keep going until the quotient is 0.  Right now it is 5.)
    5 /  2 = 2 rem 1.
    2 / 2 = 1 rem 0.
    1 / 2 = 0 rem 1. (We quit here.  Then look at the remainders from down to
     up to get our converted base).

    1010 is 10 in base 2....Now, if we want to go to base 10 from another number.

    Convert 4A (base 16 to base 10).
    Here, since we want to go to base 10, all we need to do is extend out the
    base into something we can understand...

    4 X 16 + A(10) X 1 = 64 + 10 = 74 (base 10)

    What does all of this have to do with computers in programming.  Let's
    analyze a few things...

    If you've seen $ and # and what do they mean?
    ---------------------------------------------
    $ is a typical designator in computers that a number is in base 16. # is
    a typical designator in computers that a number is in base 10.  You
    probably have seen it by now in the ASCII table studies we have done.
    For example, Z is ASCII character $5A and #90.  These are both one in the
    same.  As we see below:

    5X16^1+A(10)X16^0 = 80 + 10 = 90 (base 10).
    9x10^1+0x10^0 = 5x16^1+10(A)x16^0 (base 16).  {90/16 = 5 rem 10}

    How does my computer store data?
    ================================
    Why do we bother to mess with the different number systems in computing?
    base 10 is human-understandable, so we use it sometimes.  Base 2 is
    obvious, since this is how the computer talks. (hence BI-nary)  All
    computer storage is actually a series of 1's and 0's or ON's and OFF's.
    (2 possible combinations, hence base 2). For example, lets convert #65 to
    base 2 and see how our computers store an A.  Let's start from the highest
    we know we can on the power list for 2's. There are 256 ASCII characters
    because it was decided sometime in the computer stone age that there would be a
    total of 8 bits, the elementary unit of storage in a computer, per byte,
    or next major unit that you all should be familar with in using DOS/
    Windows/whatever.  If we analyze that using a good permutation scheme..
    2 possible orientations, 8 positions...2^8 or 256 total combinations.
    So, we will start from 7, since there is no byte #256.  128 goes into 65
    0 times.  64 goes into 65 one time with 1 left.  32 goes into 1 0 times.
    16 goes into 1 0 times.  8 goes into 1 0 times.  4 goes into 1 0 times.
    2 goes into 1 0 times. 1 goes into 1 one time. (stepping down powers of
    2.). So typically in expressing a list of bits for a byte, we use 0's
    for the filler for all 8 spaces, since we NEED to work for 8 bits with
    a respective byte.  So an ASCII related system (there are others) would
    represent #65 as 0 1 0 0 0 0 0 1.  8 bits, all 0 or 1.  If your computer
    deals with an A, it actually deals with the bit series 01000001.

    In using a computer, we don't need to know about bits, since each
    completely meaningful unit to most of us comprises 8 bits.  We don't
    need to normally think of 01000001, the way the computer does it.  But
    for some things in programming, we do, though.

    Base 16 is also used a lot.  Reason?  It's a real handy way to define
    a byte.  Let's look at the A.  According to the ASCII table, A is $41.
    16 = 2^4 so we can see it's a lot handier way to tag around the implied
    bits of a byte with this... If we look at the bit sequence, a high end
    of 4 bits would have to be multiplied by 16.  So if we look at the meaning
    of 0100 and 0001, we will see why we use base 16... 0100 is 4.  0001 is 1.
    Concenate them together, we get 41...Neat, eh?

    We don't really have all that much concern for base 16 in this tutorial,
    because in pascal, base 10 will work as well.  we do need to be concerned
    about base 2, though, because the computer uses it.

    Reasons for knowing what this stuff is...
    =========================================
    We have reasons that we need to know how to convert between the number
    bases, and be familar with what a bit is...You may be familiar with what
    is called the high and low orders of a byte.  To use the example of the A
    bit series we converted earlier:

    0      1      0       0                     0       0       0       1
          high order                                    low order

    There are pascal commands which use the bits for things.  Also, some
    fixed file formats use these (such as compression -- that's actually whats
    going on -- it works at bit level to compress the number of bytes used) and
    if you wish to if you ever develop something....)  One must have an idea
    of where the bits are coming from, hence all the stuff I was going through
    before.  If you read through your TP programmer's reference and see it
    talking about orders of bytes and what goes on with those particular
    commands, now, you should have the background to know about it and predict
    what would happen on those commands.

    First good commands to know about for working with bits
    =======================================================
            We always want to go for the most efficient code available.  If we
    ever need to work with multiplying or dividing powers of 2, we can do it
    much speedier and easier by having the computer do bit shifts instead of
    doing an actual multiply or divide command when we are dealing with small
    numbers.  Shifting information around is much easier for the CPU than
    actually doing the computation.  Let us demonstrate.

    00000010 (base 2) = 2 (base 10)
    00000010 (shift 1 byte to the left) => 00000100 or 4 (base 10)
    00000010 (shift 1 byte to the right) => 00000001 or 1 (base 10)

    Going one way or the other in shifting bytes have the effect of multiplying
    or dividing by 2^(# of bytes we move).  Play with these commands and you
    will see.  It's a bit shift that it does, and is MUCH faster than an actual
    divide when it comes to any power of 2. Examples:

    writeln(2 shl 1);   {or 2 X 2^1}
    writeln(2 shr 1);   {or 2 / 2^1}

    Those two commands work this way: <number> command <# of bits to shift>
    # of bytes to shift turns out to be the power of 2 we do the work on...

    HI(byte) pulls the high order of the expression.
    LO(byte) pulls the low order of the expression.
    swap(byte) swaps the orders.

    These are all functions.

    Other Math Functions offered by Pascal
    ======================================
    Abs(X)          Takes absolute value of X.
    Arctan(X)       Takes arctangent of X.
    Cos(X)          Takes cosine of X.
    Exp(X)          Takes exponential (base e) of argument.
    Frac(X)         Returns fraction of X.
    Int(X)          Returns integer part of X.
    Ln(x)           Takes base e logarithm of X.
    Pi              Returns value of pi.
    Round(X)        Rounds X(real) to an integer.
    sin(X)          Takes sine of X.
    sqr(x)          Takes square of X.
    sqrt(x)         Takes square root of X.
    trunc(x)        Truncates X w/o rounding.

    It is a good idea to learn basic trigonometry and analytical geometry in
    programming.  For example, I know from my studies that Tan(x) would be
    1/Arctan(x) or sin(X) / cos(X).  This stuff is good to know. The reasons?
    Graphics.  All one needs to draw anything, really, is a good spot placement
    procedure and knowledge of trigonometry, and analytical geometry.  Just
    know and define a resolution and then start drawing knowing your knowledge.
    As a test, to help out....How would one draw a circle given a central point
    and radius?  (Gotoxy in the CRT or WinCRT unit will be of use.  It will
    place the cursor at a specified position so you can write something.).
    If anyone wants a text-based solution on this one if they can't figure
    it out, e-mail ggrotz@2sprint.net.  I will place one in the next part.

    Other stuff that may prove useful
    =================================
    In your programming experience, you may have wondered if there is a way
    to get a string to an integer, or an integer to a string to do things
    with it?  Well, there is.  VAL and STR.

    Val is used like this:  Val(string, integer, errorinteger);
    string is the string you want to try and convert to an integer.
    integer is the integer that holds the successful conversion.
    errorinteger is <> 0 when there is an error in conversion (always check
       this before you move on after using a VAL!!!

    Str is used like this: string := str(integer);
    string is the string you want to hold the conversion in...
    integer is the integer that we want to convert...

    Another good command to know is POS: integer := pos(substr, str);
    It returns 0, if substr isn't there, but it returns a positive value
    corresponding to the start of the substring in the string, if it's there.
    For example, if we want to find the first use of the word AT in a string...

    int := pos('AT', 'THAT');

    int will be 3.

    Conclusion
    ==========
    I know all of this mathematics, and talk of bits is probably confusing.
    If you don't understand it right now, don't worry about it and go back
    at leisure and study it.  It is the basic extent of mathematics behind
    the actual operation of a computer and what we all as programmers need
    to keep in the back of our minds for some things.  You should know what
    a bit is, an order of a byte is, and that there are 8 bits in a byte.
    These things should help out in some matters.  You should know the why
    parts of these things in some cases...You should especially, though,
    understand the parts about the commands SHR, SHL, HI, LO, and SWAP,
    and the concepts of orders based on the storage of a byte, as, though
    we will not see these in this part, we will undoubtedly see them some-
    time before this tutorial is over, more than likely, even we may not
    see them.  But you may need to work at bit level with bytes sometime,
    so this information is present here now for both the novices and the
    experts that may see this.  Also, converting the number bases is a
    needed thing.

    Practice Programming Problem #7
    ===============================
            The concept of a record is relatively straight forward.  So I
    will not try to come up with a problem for basic usage of records.
    But the concept of writing code to do some of the number conversions
    is not.  These functions can be very useful for later use in your
    programming (save them after you write them!).  Write a function for
    your code library that will perform a base 10 to base X number
    conversion (X being a variable we can define in the function header to
    be an integer.) on an integer.  Also write a function that will perform a
    base x to a base 10 conversion.  Inbed these two functions in a program
    that will take a number from the keyboard (you can use whatever prompt
    you deem to be the least confusing as possible) and a base to convert
    the number to.  Put out a prompt as to the answer of what the new base
    number of the input is, as well as a restatement of the original number
    inputted using a reverse conversion of base (Show us that both of the
    functions work and are correct.  If the 2nd computed statement DOES
    not equal what is put in, there is an error.), not a direct reprint of
    the input variable.  To prove we are not writing out the original input
    number that we placed in the keyboard, place a 0 in the keyboard input
    variable after you perform the function to convert it to the user's
    desired base.

    Sample Output
    -------------
    Enter a number: 10
    What base do you want to convert it to? 5

    10 (base 10) is 20 (base 5).
    To check: 20 (base 5) is 10 (base 10).

    Notes:
    1) You will have to build the converted number base as a string,
    because any base beyond 10 requires the use of the alphabet for parts
    of the number.
    2) The best way I see to handle the "What do we use for the number
    in the result?" question is to define a constant string in the function
    to be something like '0123456789ABC...', and address a proper part
    of the constant string when we build the converted number for conversion.
    3) Remember we count from x^0 units on the right!!!!!
    4) You will need to probably make the high end limit to be base 36.
    5) Hint: the base x to base 10 function will need to input a string
    for the input number and output a longint, while the base 10 to base
    x function will need to input a longint and output a string.
    6) A side note for usage.  You can link these two to get from any base
    to any base using base 10 as the intermediary.

    Next Time
    =========
    We will cover the DOS file function commands out of TP.  Hold on to
    your newsreaders because part 8 (right now) is 531 lines, and I'm not
    through yet.  This will be one of our special topics.  If you not
    familiar with the following concepts in DOS, look up in your DOS
    manual and try and pick it up before next time: Read-only file,
    hidden file, system file, volume label, command-line parameter, MD,
    RD, delete (or erase), the use of * and ? as wildcards.  As always,
    any comments, questions, gripes, etc, send them to ggrotz@2sprint.net.

[Back to TUTOR SWAG index] [Back to Main SWAG index] [Original]