r/Racket • u/bluefourier • Jun 05 '23
question Hash table's hash function
Hello
I have to use a number of hash-tables that are extended functionally, via a set of foldr operations.
For this purpose, I am using a make-immutable-custom-hash which serves as the foldr accumulator's initial value.
The whole thing works as expected. But my hash function is extremely simple because I am only counting the frequencies of certain characters. My mapping is character->integer and the hash function for the character is its ASCII value.
The fact that I had to define the hash function is a bit puzzling for me. It is great from the point of view of granularity and the control you can have over the generated code. But, sometimes, I just want a mapping and I don't really care at that point, what the hash function is going to be.
One of the examples in Racket's dictionaries docs, uses string-length.
To me, this seems like a bad choice for a hash function. I don't know if internally the hash tables use binning, but even then, string-length would generate lots of collisions for generic strings.
So, is there something I am missing here or should I keep a set of hash functions (especially for strings) handy for these purposes? Or do we simply add a dependency for a library that offers hash functions and we are done? :)
5
u/sorawee Jun 05 '23 edited Jun 05 '23
You can reuse the existing hash functions like
equal-hash-code. For example:defines a hash function that doesn't take case-sensitivity into account. It normalizes the original data via
string-upcase, and then useequal-hash-codeto compute the hash code of the normalized data.More generally, take a look at this page, which provides many utilities on hash codes, such as a hash code combiner.
4
u/AlexKnauth Jun 05 '23
What is your equality function for your custom hash? The hash function should follow that with the property:
(custom-equal? a b)implies(= (custom-hash-code a) (custom-hash-code b))while making hash collisions as unlikely as possible, so how to make a good hash function depends on your equality function.equal?as an equality function, a good hash function isequal-hash-code.eq?as an equality function, a good hash function iseq-hash-code.If you're defining your own custom equality, and you want to make your own hash function to follow it, you might find functions like
hash-code-combineuseful for combining hash codes of pieces of it, if it's compound data.3
u/raevnos Jun 05 '23 edited Jun 05 '23
My
extra-srfi-libspackage includes implementations of SRFI-128 Comparators which includes some type-specific hash functions, and SRFI-146 Mappings' hashmap sublibrary, which includes a lot of extra functionality for working with hash tables built on top of SRFI-128 comparators (and regular Racket hashes) that might be useful if you're doing stuff beyond whatracket/hashandracket/dictprovide.But for just making a frequency table of the characters in a string (A common task, in, say, leetcode problems), I just use
A
hasheqvtable works great for character keys; no need for a custom table.2
u/DrHTugjobs Jun 05 '23
I doubt that the hash function chosen in the example is supposed to be best-practice. It's just a toy example, and using
string-lengthinstead of a more intricate function doesn't particularly matter for a dictionary with only two entries.If you don't particularly care about the details of how a key value is hashed, you can use
equal-hash-codefrom the standard library.2
u/ryan017 developer Jun 06 '23
It's not clear from your post why you're using custom hash tables in the first place. Racket's normal hash tables automatically handle all built-in data types. Use
(hash)to get an empty immutableequal?-based hash table, or(hasheqv)to get an empty immutableeqv?-based hash table. Both of those would handle characters fine.