Problem Set 5: Speller
Evidence of a 5-point Submission
checkfunction calculates the hash index once and uses a loop with short-circuiting to inspect that index, usesstrcasecmpto compare words- Implements a hash function that is more complex than sorting by the first
nletters of a word or summing the ASCII values of a word, calculates an appropriate value ofN loadfunction error handles aftermallocandfopencalls, usesfscanfto store the new word directly in the table (without using a buffer)sizefunction returns size in O(1) (handled within theloadfunction)unloadfunction uses recursion or a loop to free the entire table, only declares one or two new variables or functions
Evidence of a 4-point Submission
checkfunction calculates the hash index once and uses a loop with short-circuiting to inspect that index, usesstrcasecmpto compare words- Student attempts to implement a hash function that is more complex than sorting by the first
nletters of a word loadfunction function error handles aftermallocandfopencalls, may use a buffer to transfer a word into the tablesizefunction returns size in O(1) (handled within theloadfunction)unloadfunction uses recursion or a loop to free the entire table, may declare three or four new variables or functions
Evidence of a 3-point Submission
checkfunction calculates the hash index once and uses a loop with short-circuiting to inspect that index- Student attempts to implement a hash function that is more complex than sorting by first letter
loadfunction uses a buffer to transfer a word into the tablesizefunction returns size in O(1) (handled within the load function)unloadfunction iterates through each linked list in each element of the table at most once
Evidence of a 2-point Submission
- Defines
Nto be a number much larger than required by the given hash function checkfunction calculates the hash index more than once, does not short-circuit, usesstrcmpto compare strings- hash function is less complex (sorts into fewer indices) than sorting by first letter
loadfunction does not check thatfopenormallochave executed successfully, may notfscanfdirectly into the node, may unnecessarily initialize the table toNULL, may use other extraneous variables and conditionals within the loopsizefunction returns size in O(n)unloadfunction iterates through the table using extraneous variables or computation for each element
Evidence of a 1-point Submission
- Defines
Nto be a number much smaller than required by the given hash function checkfunction does not usestrcmporstrcasecmpto compare words, maymallocor do other unnecessary computation for each word in the list- hash function remains unchanged or may produce an index that is outside of the range of [0, N-1].
loadfunction iterates through the dictionary in a way that is confusing or needlessly complicatedsizefunction returns size in O(n)unloadfunction uses a confusing method to iterate through the table, may visit elements in the table more than once or skip over elements of the table
Example Implementations (Worse vs. Better)
check Function
Worse Implementation
The example below uses an unnecessary conditional statement
unsigned int hcode = hash(word);
node *trav = table[hcode];
while (trav != NULL)
{
if (strcasecmp(trav->word, word) == 0)
{
return true;
}
else
{
trav = trav->next;
}
}
return false;
Better Implementation
The example below uses no extraneous variables or conditionals
node *trav = table[hash(word)];
while (trav != NULL)
{
if (strcasecmp(trav->word, word) == 0)
{
return true;
}
trav = trav->next;
}
return false;
Hash Function
Worse Implementation
The example below uses a value of N that will leave empty slots at both the beginning and end of the hash table. It also uses an unnecessary conditional statement
const unsigned int N = 1000000;
unsigned int hash(const char *word)
{
int sum = 0;
for (int i = 0; i < 3; i++)
{
if (strlen(word) > i)
{
sum += toupper(word[i]);
}
else
{
sum += 0;
}
}
return (sum % N);
}
Better Implementation
Although the hash function itself is not better, the example below picks a more appropriate value for N and uses no unnecessary conditionals or variables. Note that it still wouldn’t be most efficient to call strlen twice.
const unsigned int N = 72;
unsigned int hash(const char *word)
{
int sum = 0;
int iterations = strlen(word) < 3 ? strlen(word) : 3;
for (int i = 0; i < iterations; i++)
{
sum += toupper(word[i]) - 'A';
}
return sum % N;
}
load Function
Worse Implementation
The example below uses a buffer and an unnecessary conditional statement (there is no need to check if table[bucket] != NULL)
char new_word[LENGTH + 1];
while (fscanf(dict, "%s", new_word) != EOF)
{
n = malloc(sizeof(node));
if (n == NULL)
{
return false;
}
strcpy(n->word, new_word);
n->next = NULL;
int bucket = hash(n->word);
if (table[bucket] != NULL)
{
n->next = table[bucket];
}
table[bucket] = n;
size_of_dict += 1;
}
// clean up...
Better Implementation
The example below avoids using a buffer by freading directly into the node
// load dictionary...
while (true)
{
node *newnode = malloc(sizeof(node));
if (newnode == NULL)
{
return false;
}
if (fscanf(source, "%s", newnode->word) == EOF)
{
free(newnode);
break;
}
unsigned int hcode = hash(newnode->word);
newnode->next = table[hcode];
table[hcode] = newnode;
dsize++;
}
// clean up...
size Function
Worse Implementation
The example below includes an unnecessary conditional
if (counter > 0)
{
return counter;
}
return 0;
Better Implementation
The example below includes no unnecessary conditionals
// calculated in load
return dsize;
unload Function
Worse Implementation
The example below includes an unnecessary conditional and initializes many variables
for (int x = 0; x < N; x++)
{
node *checker = table[x];
while (checker != NULL)
{
node *tmp = checker;
checker = checker->next;
free(tmp);
}
if (x == N - 1 && checker == NULL)
{
return true;
}
}
return false;
Better Implementation
The example below has no unnecessary conditionals and uses recursion to avoid the need for extraneous variables
void vacate_list(node *head)
{
if (head == NULL)
{
return;
}
vacate_list(head->next);
free(head);
}
bool unload(void)
{
for (int i = 0; i < N; i++)
{
vacate_list(table[i]);
}
return true;
}