home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / HASH.ZIP / HASH.PAS
Encoding:
Pascal/Delphi Source File  |  1985-12-28  |  13.9 KB  |  498 lines

  1. program address (input, output, infile, outfile);
  2. const
  3.    DASH          = 45;
  4.    MAXNAME       = 25;
  5.    MAXNUMBER     = 13;
  6.    ENDFILE       = -1;
  7.    ENDSTR        =  0;
  8.    NEWLINE       = 10;
  9.    PRIME_MODULUS = 47;
  10.    MAXBUCKETS    = 50;
  11.    MAXLINELENGTH = 80;
  12.  
  13. type
  14.    character = -1..127;             { NOTE! NOT type "char" }
  15.    name_type = array[1..MAXNAME] of character;
  16.    phonenumbertype = array[1..MAXNUMBER] of character;
  17.    info = record
  18.       name        : name_type;
  19.       phonenumber : phonenumbertype;
  20.       end;
  21.    ptr = ^node;
  22.    node = record                     { typical node in d.s. }
  23.       person : info;                 { entry for a person   }
  24.       next   : ptr;                  { link to next entry   }
  25.       end;
  26.  
  27. var
  28.    BUCKET :   array[1..MAXBUCKETS] of ptr;  { linked list pointers   }
  29.    NLINKS :   integer;                      { to monitor performance }
  30.    infile, outfile : text;                  { input/output files     }
  31.  
  32. { * * * * * end of global declarations * * * * * }
  33.  
  34. procedure filerewrite ( var name_of_file : text);
  35.  
  36. begin
  37. {ssign (name_of_file, 'M:HASH.TXT' );}
  38. rewrite (name_of_file,'hash.out');
  39. end; {rewrite}
  40.  
  41. procedure filereset ( var name_of_file : text);
  42.  
  43. begin {filereset}
  44. {ssign (name_of_file, 'b:phone.dat');}
  45. reset  (name_of_file,'phone_data.2'); 
  46.  
  47. end; {filereset}
  48.  
  49. procedure freecell (cell : ptr);
  50. begin
  51. dispose (cell);
  52.  
  53. end; {freecell}
  54.  
  55. FUNCTION isdigit (c: character): boolean;
  56.    { Returns "true" if "c" is a digit }
  57. begin
  58. isdigit := c in [ord('0')..ord('9')]
  59. end; { isdigit }
  60.  
  61. FUNCTION isletter (c: character): boolean;
  62.    { Returns `true` if "c" is a letter }
  63. begin
  64. isletter := c in [ord('a')..ord('z')] + [ord('A')..ord('Z')]
  65. end; { isletter }
  66.  
  67. FUNCTION getc (var f: text; var c: character): character;
  68.    { get a single character from the file "f" }
  69.    var  ch : char;
  70. begin
  71. if (eof(f)) 
  72.    then c := ENDFILE
  73.    else if (eoln(f)) 
  74.            then begin
  75.                 readln(f);
  76.                 c := NEWLINE
  77.                 end
  78.            else begin
  79.                 read(f, ch);
  80.                 c := ord(ch)
  81.                 end;
  82. getc := c;
  83. end; { getc }
  84.  
  85. { * * * * * * * End of low-level CHARACTER procedures * * * * * * }
  86.  
  87. FUNCTION getname(var f: text; var name: name_type): character;
  88.    { get (read) a "name" from the file "f" }
  89.    var  i : integer;
  90.         done : boolean;
  91.         c : character;
  92. begin
  93. i := 0;
  94. done := false;
  95. while (not done) do
  96.       begin
  97.       c := getc(f, c);
  98.       if (c = ENDFILE)
  99.          then done := true
  100.          else if (isletter(c))
  101.                  then begin
  102.                       i := i + 1;
  103.                       name[i] := c;
  104.                       if (i = (MAXNAME - 1)) 
  105.                          then done := true
  106.                       end
  107.                  else if (i <> 0) 
  108.                       then done := true;
  109.         end; { while }
  110.    i := i + 1;
  111.    name[i] := ENDSTR;
  112.    if ((c = ENDFILE) and (i <= 1)) 
  113.       then getname := ENDFILE
  114.       else getname := 1;
  115. end; { getname }
  116.  
  117. FUNCTION getnumber(var f: text; var number: phonenumbertype): character;
  118.    { Get (read) a phone number from the file "f" }
  119.  
  120.   var
  121.       i   : integer;
  122.       done : boolean;
  123.       c   : character;
  124. begin
  125. i := 0;
  126. done := false;
  127. while (not done) do 
  128.       begin
  129.       c := getc(f, c);
  130.       if ((c = ENDFILE) or (c = NEWLINE)) 
  131.          then done := true
  132.          else if (isdigit(c) or (c = DASH)) 
  133.                  then begin
  134.                       i := i + 1;
  135.                       number[i] := c;
  136.                       if (i = (MAXNUMBER - 1)) 
  137.                          then done := true
  138.                       end
  139.                  else if (i <> 0)        { ignore leading garbage } 
  140.                          then done := true;
  141.       end; { while }
  142. i := i + 1;
  143. number[i] := ENDSTR;
  144. if ((c = ENDFILE) and (i <= 1)) 
  145.    then getnumber := ENDFILE
  146.    else getnumber := 1;
  147. end; { getnumber }
  148.  
  149. FUNCTION getdata (var f: text; var person: info): character;
  150.    { This procedure reads the name and phone number from the text file f.
  151.    { Note: 1. any string of maxstring letters are the name
  152.    {       2. leading blanks are ignored.
  153.    {       3. any none alpabetic character terminates the name
  154.    }
  155. begin
  156. getdata := getname(f, person.name);
  157. getdata := getnumber(f, person.phonenumber);
  158. end; { getdata }
  159.  
  160. PROCEDURE putname(var f: text; var s: name_type);
  161.    { write a "name" on file "f" }
  162.    var  i: integer;
  163. begin
  164. i := 1;
  165. while (s[i] <> ENDSTR) do 
  166.       begin
  167.       write (f, chr(s[i]));
  168.       i := i + 1
  169.       end
  170. end; { putname }
  171.  
  172. PROCEDURE putnumber(var f: text; var number : phonenumbertype);
  173.    { write a phone number on the file "f" }
  174. var
  175.    i: integer;
  176. begin
  177.    i := 1;
  178.    while (number [i] <> ENDSTR) do begin
  179.       write(f, chr(number [i]));
  180.       i := i + 1
  181.       end
  182. end; { putnumber }
  183.  
  184. FUNCTION namlength (name : name_type) : integer;
  185.    { Returns the number of characters in "name".  }
  186.    var  i : integer;
  187. begin
  188. i := 1;
  189. while (name[i] <> ENDSTR) do
  190.         i := i+1;
  191. namlength := i-1;
  192. end;
  193.  
  194. FUNCTION numlength (number : phonenumbertype) : integer;
  195.    { Returns the number of characters in "number".  }
  196.    var  i : integer;
  197. begin
  198. i := 1;
  199. while (number[i] <> ENDSTR) do
  200.         i := i+1;
  201. numlength := i-1;
  202. end;
  203.  
  204. FUNCTION lex_equal (var nm1, nm2: name_type) : boolean;
  205.    { Returns "true" if the two names are lexicographically equal }
  206.    var
  207.      i : integer;
  208.      l_equal : boolean;
  209.  
  210. begin {lex_equal}
  211.  
  212. i := 1;
  213. while ((nm1[i] = nm2[i]) and (nm1[i] <> ENDSTR)) do
  214.     i := i + 1;
  215. l_equal := ((nm1[i] = ENDSTR) and (nm1[i] = nm2[i]));
  216. {
  217. if l_equal
  218.   then writeln ('equal')
  219.   else writeln ('not equal');
  220. }
  221. lex_equal := l_equal;
  222.  
  223. end; { lex_equal  }
  224.  
  225. FUNCTION lex_less(var nm1, nm2: name_type): boolean;
  226.    { Return "true" if "nm1" is lexicographically less than "nm2" }
  227.    var  i: integer;
  228. begin
  229. i := 1;
  230. while ((nm1[i] = nm2[i]) and (nm1[i] <> ENDSTR)) do
  231.       i := i + 1;
  232. lex_less := (nm1[i] < nm2[i])
  233. end; { lex_less }
  234.  
  235. { * * * * * * * * * End of NAME and NUMBER procedures * * * * * * * * }
  236.  
  237. PROCEDURE initlist;
  238.    { Initialize the collection of linked lists comprising the       }
  239.    { buckets---they are all empty.                                  }
  240.    var   i : integer;
  241.          node : ptr;
  242. begin
  243. for i := 1 to MAXBUCKETS do
  244.      begin
  245.      BUCKET[i] := nil;
  246.      end;
  247. end;
  248.  
  249. PROCEDURE dump (var ofile:text);
  250.    { This procedure "dumps" (writes out) the current structure of the }
  251.    { data structure (list in this case) in some appropriate format.   }
  252.    var   item : ptr;
  253.          i,linelength : integer;
  254.  
  255. begin
  256. writeln (ofile);
  257. writeln (ofile, '  ***    dump of buckets    ***');
  258. writeln (ofile);
  259. for i := 1 to MAXBUCKETS do 
  260.      begin                                  { Display i-th bucket }
  261.      write (ofile, 'BUCKET[',i:2,'] : ');
  262.      linelength := 13;
  263.      item := BUCKET[i];
  264.      while (item <> nil) do
  265.           with item^ do 
  266.               begin
  267.               if (linelength + 24) > MAXLINELENGTH 
  268.                  then begin
  269.                       writeln (ofile);
  270.                       write (ofile, '             ');
  271.                       linelength := 13;
  272.                       end;
  273.               write (ofile, '   ');
  274.               putname (ofile, person.name);
  275.               write (ofile, ',');
  276.               putnumber (ofile, person.phonenumber);
  277.               linelength := linelength + namlength(person.name) 
  278.                                        + numlength(person.phonenumber) + 7;
  279.               item := item^.next;
  280.               end;
  281.      writeln (ofile); 
  282.      end;
  283. writeln (ofile);
  284. writeln(ofile, '  ***    end of dump    ***');
  285. end;
  286.  
  287. FUNCTION hash (key : name_type; n : integer) : integer;
  288.    { This function returns an integer in the range [1..n], based on  }
  289.    { the characters in "key".                                        }
  290. var
  291.   sum,
  292.   i,
  293.   j : integer;
  294.  
  295. begin
  296. sum := 0;
  297. j := namlength (key);
  298. for i := 1 to j do
  299.     sum := sum + key[i];
  300. sum := (sum mod n) + 1;
  301. { writeln (output, sum); }
  302. hash := sum;
  303. end; {hash}
  304.  
  305. PROCEDURE enter (var key: info);
  306.    { This routine looks up the given "key" in a phonebook.
  307.    { If the person has already been entered into the phone book,
  308.    { nothing is done, otherwise then a cell denoting this person
  309.    { is added to the phone book.
  310.    {
  311.    {      * * * as a class exercise only * * *
  312.    { "enter" should count the number of links it follows to:
  313.    { 1. find the key, or
  314.    { 2. enter a new key.  (count the newly created link as if
  315.    {                       it had been followed.)
  316.    { and return this value in the global variable "nlinks".
  317.    }
  318.  
  319. var
  320.   add_it  : boolean;
  321.   cell,
  322.   newcell : ptr;
  323.   i       : integer;
  324.  
  325. begin {enter}
  326. nlinks := 1;
  327. i := hash (key.name, PRIME_MODULUS);
  328. {putname (output, key.name); putnumber (output, key.phonenumber); }
  329. { writeln ('enter');}
  330. if bucket[i] = nil
  331.    then begin {put the first link out from the bucket}
  332.         new (cell);
  333.         bucket[i] := cell;
  334.         add_it := false;
  335.         cell^.person := key;
  336.         cell^.next := nil;
  337.         end
  338.    else begin { Go down the chain and see if the name is there}
  339.               { If there, write a duplicate message, else add the }
  340.               { info at the front of the link. }
  341.         cell := bucket[i];
  342.         add_it := true;
  343.         while cell^.next <> nil
  344.             do begin {while}
  345.                if lex_equal (cell^.person.name, key.name)
  346.                   then begin
  347. {---                   writeln ('duplicate found'); }
  348.                        add_it := false;
  349.                        cell^.next := nil;
  350.                        end {then}
  351.                    else begin
  352.                         cell := cell^.next;
  353. {---                    writeln ('down the chain'); }
  354.                         end; {else}
  355.                nlinks := nlinks + 1;
  356.                end; {while moving down the chain}
  357.          end; {else}
  358. if add_it
  359.    then begin
  360. {---    putname (output, key.name);
  361.         writeln (' - add one'); }
  362.         write ('.');
  363.         new (newcell);
  364.         newcell^.next := bucket[i];
  365.         newcell^.person.name := key.name;
  366.         newcell^.person.phonenumber := key.phonenumber;
  367.         bucket[i] := newcell;
  368.         end; {then}
  369.  
  370. end; {enter}
  371.  
  372. FUNCTION lookup (var key: info): boolean;
  373.    { Given key, this function looks up that name in the phone book.
  374.    { It returns the true if the person was in the phone book, and
  375.    { sets the record to the phone number;
  376.    { it returns false, if the person is not in phone book.
  377.    }
  378. var
  379.   i,
  380.   j  : integer;
  381.   cell  : ptr;
  382.   found : boolean;
  383.  
  384. begin
  385. found := false;
  386. i := hash (key.name, PRIME_MODULUS);
  387. cell := bucket[i];
  388. while cell <> nil do
  389.    begin
  390.    if lex_equal (cell^.person.name, key.name)
  391.       then begin
  392.            found := true;
  393.            key := cell^.person;
  394.            cell := nil;
  395.            end {then}
  396.       else begin
  397. {---       writeln;  
  398.            if cell <> nil
  399.               then putname (Output, cell^.person.name);
  400.            writeln; }
  401.            cell := cell^.next;
  402.            end; {else}
  403.    end; {while}
  404.  
  405. lookup := found;
  406.  
  407. end; { lookup }
  408.  
  409.  
  410. PROCEDURE main;
  411.    { This main procedure reads a selection of natural language
  412.    { text, and creates a "phonebook" of every name and number  
  413.    { that occurs in the input file.
  414.    { It then dumps the representing data structure, and prints
  415.    { out a variety of counts describing its performance.
  416.    { finally, it reads in a random selection of names, and
  417.    { looks them up in the phonebook.
  418.    }
  419.    var  done: boolean;
  420.         entry: info;
  421.         query: name_type;
  422.         npeople: integer;               { number of people processed }
  423.         maxlinks, totalinks: integer;   { behavior counts } 
  424.         avg: real;
  425. begin
  426. NLINKS := 0;
  427. npeople := 0;
  428. totalinks := 0;
  429. maxlinks := 0;
  430. initlist;
  431.  
  432.    { main while loop.   echo input words and enter them.            }
  433.  
  434. while (getdata (infile, entry) <> ENDFILE) do
  435.       begin
  436.       enter(entry);
  437.  
  438.                  { collect counts to monitor access performance }
  439.  
  440.       npeople := npeople + 1;
  441.       totalinks := totalinks + NLINKS;
  442.       if NLINKS > maxlinks
  443.          then maxlinks := NLINKS;
  444.       NLINKS := 0;
  445.       end;   { main while loop }
  446.  
  447.                  { dump data structure, and print statistics }
  448.                  { describing the access performance.        }
  449.  
  450. writeln (outfile);
  451. dump(outfile);
  452. writeln (outfile);
  453. writeln (outfile, ' total number of entries = ', npeople:3);
  454. writeln (outfile, ' total number of links followed = ', totalinks:4);
  455. avg := totalinks / npeople;
  456. writeln (outfile, ' average number of links per entry = ', avg:8:4);
  457. writeln (outfile, ' maximum number of links followed = ', maxlinks:4);
  458.  
  459.                 { test the system with a sequence of random queries }
  460.  
  461. writeln (outfile);
  462.  
  463. writeln (output);
  464. writeln (output, '      Process some sample queries');
  465. writeln (output, '      Enter CNTRL D to terminate');
  466. done := false;
  467. while (not done) do
  468.       begin  { look up this query word }
  469.       write (Output, 'name? ');
  470.       if (getname (input, query) = ENDFILE)
  471.          then done := true
  472.          else begin
  473.               writeln;
  474.               entry.name := query;
  475.               if (not lookup (entry))
  476.                  then begin
  477.                       putname (Output, query);
  478.                       writeln (Output, ' is not in the phone book')
  479.                       end {then}
  480.                  else begin
  481.                       writeln;
  482.                       putname (Output, query);
  483.                       write (Output, '''s phone number is ');
  484.                       putnumber (Output, entry.phonenumber);
  485.                       writeln (Output);
  486.                       end;
  487.               end;
  488.       end; { while }
  489. end; { main } 
  490.  
  491. begin
  492. filereset (infile);
  493. filerewrite (outfile);
  494. main;
  495. close (infile);
  496. close (outfile);
  497. end.
  498.