PreviousUpNext

15.4.893  src/lib/src/hashtable.pkg

## hashtable.pkg
#
# Typeagnostic hashtables.
#
# See also:
#
#     src/lib/src/int-hashtable.pkg
#     src/lib/src/unt-hashtable.pkg
#     src/lib/src/quickstring-hashtable.pkg
#     src/lib/src/typelocked-hashtable-g.pkg
#     src/lib/src/typelocked-double-keyed-hashtable-g.pkg

# Compiled by:
#     src/lib/std/standard.lib



###               "The sciences do not try to explain, they hardly
###                even try to interpret, they mainly make models.
###
###                By a model is meant a mathematical construct which,
###                with the addition of certain verbal interpretations,
###                describes observed phenomena.
###
###                The justification of such a mathematical construct
###                is solely and precisely that it is expected to work."
###
###                                         -- Johnny von Neuman



stipulate
    package hr  =  hashtable_representation;                    # hashtable_representation      is from   src/lib/src/hashtable-rep.pkg
    package rwv =  rw_vector;                                   # rw_vector                     is from   src/lib/std/src/rw-vector.pkg
herein

    package   hashtable
    : (weak)  Hashtable                                         # Hashtable                     is from   src/lib/src/hashtable.api
    {
        Hashtable (X, Y)
            =
            HASHTABLE
              {
                hash_g:                 X -> Unt,
                eq_pred:                (X, X) -> Bool,
                #
                not_found_exception:    Exception,
                #
                table:                  Ref( hr::Table( X, Y ) ),
                n_items:                Ref( Int )
              };

        fun index (i, size)
            =
            unt::to_int_x (unt::bitwise_and (i, unt::from_int size - 0u1));


        # Find smallest power of 2 (>= 32) that is >= n 
        #
        fun round_up n
            =
            f 32
            where

              fun f i
                  =
                  if   (i >= n)   i;
                  else            f (i * 2);   fi;
            end;

        # Create a new table; the
        # int is a size hint and the
        # exception is to be raised by find.
        #
        fun make_hashtable (hash, eq) { size_hint, not_found_exception }
            =
            HASHTABLE {
                hash_g => hash,
                eq_pred => eq,
                not_found_exception,
                table => REF (hr::allot size_hint),
                n_items => REF 0
            };


        # Remove all elements from the table 
        #
        fun clear (HASHTABLE { table, n_items, ... } )
            =
            {   hr::clear *table;
                n_items := 0;
            };

        # Insert an item.  If the key already has an item associated with it,
        # then the old item is discarded.
        #
        fun set (my_table as HASHTABLE { hash_g, eq_pred, table, n_items, ... } ) (key, item)
            =
            {   vector = *table;
                size = rwv::length vector;
                hash = hash_g key;
                index = index (hash, size);

                fun get hr::NIL
                        =>
                        {   rwv::set (vector, index, hr::BUCKET (hash, key, item, rwv::get (vector, index)));
                            n_items := *n_items + 1;
                            hr::grow_table_if_needed (table, *n_items);
                            hr::NIL;
                        };

                    get (hr::BUCKET (h, k, v, r))
                        =>
                        if (hash == h  and  eq_pred (key, k))
                            #
                            hr::BUCKET (hash, key, item, r);
                        else
                            case (get r)
                                #
                                hr::NIL =>  hr::NIL;
                                rest     =>  hr::BUCKET (h, k, v, rest);
                            esac;
                        fi;
                end;

                case (get (rwv::get (vector, index)))
                    #
                    hr::NIL =>  ();
                    b        =>  rwv::set (vector, index, b);
                esac;
            };



        # Return TRUE iff the key is in the domain of the table:
        #
        fun contains_key (HASHTABLE { hash_g, eq_pred, table, ... } ) key
            =
            get (rwv::get (vector, index))
            where
                vector   =  *table;
                hash  =  hash_g key;
                index =  index (hash, rwv::length vector);


                fun get (hr::BUCKET (h, k, v, r))
                        => 
                        (hash == h  and  eq_pred (key, k))    or
                        get r;

                    get hr::NIL
                        =>
                        FALSE;
                end;
            end;



        # Find an item;  if it is missing raise the table's not-found exception.
        #
        fun look_up (HASHTABLE { hash_g, eq_pred, table, not_found_exception, ... } ) key
            =
            get (rwv::get (vector, index))
            where
                vector   =  *table;
                size  =  rwv::length vector;

                hash  =  hash_g key;
                index =  index (hash, size);


                fun get (hr::BUCKET (h, k, v, r))
                        =>
                        if   (hash == h   and   eq_pred (key, k))   v;
                        else                                        get r;
                        fi;
                    
                    get hr::NIL
                        =>
                        raise exception not_found_exception;
                end;
            end;



        # Find an item;  if it is missing return NULL.
        #
        fun find (HASHTABLE { hash_g, eq_pred, table, ... } ) key
            =
            get (rwv::get (vector, index))
            where
                vector =  *table;
                size  =  rwv::length vector;

                hash  =  hash_g key;
                index =  index (hash, size);

                fun get (hr::BUCKET (h, k, v, r))
                        =>
                        if   (hash == h  and  eq_pred (key, k))   THE v;
                        else                                      get r;
                        fi;
                        #
                    get hr::NIL
                        =>
                        NULL;
                end;
            end;

        # Remove an item;  if is it missing raise the table's not-found exception.
        #
        fun remove (HASHTABLE { hash_g, eq_pred, not_found_exception, table, n_items } ) key
            =
            item
            where
                vector =  *table;
                size  =  rwv::length vector;

                hash  =  hash_g key;
                index =  index (hash, size);

                fun get (hr::BUCKET (h, k, v, r))
                        =>
                        if (hash == h  and  eq_pred (key, k))
                            #
                            (v, r);
                        else
                            (get r) ->   (item, r');
                            #   
                            (item,  hr::BUCKET (h, k, v, r'));
                        fi;
                        #
                    get hr::NIL
                        =>
                        raise exception not_found_exception;
                end;

                (get (rwv::get (vector, index)))
                    ->
                    (item, bucket);

                rwv::set (vector, index, bucket);

                n_items := *n_items - 1;
            end;


        # Return the number of items in the table:
        #
        fun vals_count (HASHTABLE { n_items, ... } )
            =
            *n_items;


        # Return a list of the items in the table.
        #
        fun vals_list (HASHTABLE { table => REF vector, n_items, ... } )
            =
            hr::vals_list (vector, n_items);

        fun keyvals_list (HASHTABLE { table => REF vector, n_items, ... } )
            =
            hr::keyvals_list (vector, n_items);


        # Apply a function to the entries of the table:
        #
        fun keyed_apply f (HASHTABLE { table, ... } ) =  hr::keyed_apply f *table;
        fun       apply f (HASHTABLE { table, ... } ) =  hr::apply       f *table;

        # Map a table to a new table that has the same keys and exception:
        #
        fun keyed_map f (HASHTABLE { hash_g, eq_pred, table, n_items, not_found_exception } )
            =
            HASHTABLE
              {
                hash_g,
                eq_pred,
                table   =>  REF (hr::keyed_map f *table),
                n_items =>  REF *n_items,
                not_found_exception
              };

        # Map a table to a new table that has the same keys and exception:
        #
        fun map f (HASHTABLE { hash_g, eq_pred, table, n_items, not_found_exception } )
            =
            HASHTABLE
              {
                hash_g,
                eq_pred,
                table   =>  REF (hr::map f *table),
                n_items =>  REF *n_items,
                not_found_exception
              };


        # Fold a function over the entries of the table:
        #
        fun foldi f init (HASHTABLE { table, ... } ) =  hr::foldi f init *table;
        fun fold  f init (HASHTABLE { table, ... } ) =  hr::fold  f init *table;


        # Modify the hashtable items in place: 
        #
        fun keyed_map_in_place f (HASHTABLE { table, ... } ) =  hr::keyed_map_in_place f *table;
        fun map_in_place   f (HASHTABLE { table, ... } ) =  hr::map_in_place   f *table;


        # Remove any hashtable items that do
        # not satisfy the given predicate.
        #
        fun keyed_filter prior (HASHTABLE { table, n_items, ... } )
            =
            n_items := hr::keyed_filter prior *table;

        fun filter prior (HASHTABLE { table, n_items, ... } )
            = 
            n_items := hr::filter prior *table;


        #  Create a copy of a hashtable 
        #
        fun copy (HASHTABLE { hash_g, eq_pred, table, n_items, not_found_exception } )
            =
            HASHTABLE
              {
                hash_g,
                eq_pred,
                table   =>  REF (hr::copy *table),
                n_items =>  REF *n_items,
                not_found_exception
              };

        # Return a list of the sizes of the various buckets.
        # This is to allow users to gauge the quality of
        # their hashing function:
        #
        fun bucket_sizes (HASHTABLE { table, ... } )
            =
            hr::bucket_sizes *table;
    };                                                                  # package hashtable 
end;


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext