So I've been taking Harvard's CS50 class and currently am working on it's Problem Set 5 called speller (https://cs50.harvard.edu/x/2020/psets/5/speller/)
Basically I think that I have filled everything correctly, however, when trying to compile this error message appears :
In function `check':/home/ubuntu/pset5/speller/dictionary.c:33: undefined reference to `hash' dictionary.o: In function `load': /home/ubuntu/pset5/speller/dictionary.c:90: undefined reference to `hash' clang-7: error: linker command failed with exit code 1 (use -v to see invocation)
I am not sure what this means, and tried to figure it out for quite some time, but I'm reaching out for an explanation...
My code is :
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include "dictionary.h"
int word_count = 0;
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Number of buckets in hash table
const unsigned int N = 26;
// Hash table
node *table[N];
// Returns true if word is in dictionary else false
bool check(const char *word)
{
unsigned int h = hash(word);
node *cursor = table[h];
while(cursor != NULL)
{
if(strcasecmp(word, cursor -> word) ==0)
{
return true;
}
else
{
cursor = cursor -> next;
}
}
return false;
}
// Hashes word to a number
// This hash function was adapted by Neel Mehta from
// http://stackoverflow.com/questions/2571683/djb2-hash-function.
unsigned int hash_word(const char* word)
{
unsigned long hash = 5381;
for (const char* ptr = word; *ptr != '\0'; ptr++)
{
hash = ((hash << 5) + hash) + tolower(*ptr);
}
return hash %26;
}
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
FILE *dic = fopen(dictionary, "r");
char word[LENGTH + 1];
if(dic == NULL)
{
unload();
return false;
}
while (fscanf(dic,"%s",word) != EOF)
{
node *sllnode = malloc(sizeof(node));
if( sllnode == NULL)
{
return false;
}
strcpy(sllnode -> word, word);
word_count++;
int dicindex = hash(word);
if(table[dicindex] == NULL)
{
sllnode -> next = NULL;
}
else
{
sllnode -> next = table[dicindex];
}
table[dicindex]= sllnode;
}
fclose(dic);
// check here whethet to free memory space or not (maybe needs to be freed at very end)
return true;
}
// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned int size(void)
{
return word_count;
return 0;
}
// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
for(int i = 0; i < N ; i++)
{
node *cursor = table[i];
while(cursor)
{
node *tmp = cursor;
cursor = cursor -> next;
free(tmp);
}
}
return true;
}
If you would like to know what dictionary.h looks like, here is the code for that:
// Declares a dictionary's functionality
#ifndef DICTIONARY_H
#define DICTIONARY_H
#include <stdbool.h>
// Maximum length for a word
// (e.g., pneumonoultramicroscopicsilicovolcanoconiosis)
#define LENGTH 45
// Prototypes
bool check(const char *word);
unsigned int hash(const char *word);
bool load(const char *dictionary);
unsigned int size(void);
bool unload(void);
#endif // DICTIONARY_H
Hoping someone could help me with this..
Any help would be appreciated!
If you look in your load() function, you have this line: int dicindex = hash(word);.
The issue with this is that you changed hash() into hash_word() which is why you are having the error because there is no hash() function in the code. Easiest thing to do is to change unsigned int hash_word(const char* word) back to normal which was unsigned int hash(const char* word) as you weren't suppose to change any function name in this program.
Related
I've been stuck with this pset for more then a week now and can't seems to get any closer to getting something out of my code. I keep getting all the words in the text misspelled and I really don't understand how to create a hash function myself (I'm guessing the issue mostly comes from this but I'm not sure either...). I wanted to create a hash table with the first two letters of each words (it is inspired from various options I saw online).
Can someone help me understand what I'm doing wrong...
Thanks in advance !
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Choose number of buckets in hash table
const unsigned int N = 676;
// Hash table
node *table[N];
// Initialize new variables used in the program
unsigned int numberofwords;
unsigned int hashvalue;
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
// Hash the word to determine its hash value
hashvalue = hash(word);
// Set cursor to the head of the linked list of the word
node* cursor = table[hashvalue];
// Traverse into the linked list comparing the word to find a correspondance while the cursor isn't pointing to null
while (cursor != NULL)
{
if (strcasecmp(cursor->word, word) == 0)
{
return true;
}
cursor = cursor->next;
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// Inittialize a variable to keep track of the total of ASCII values
unsigned int sum = 0;
// Loop in all the letters in the word
for (int i = 0 ; i < strlen(word) ; i++)
{
// Ignore digits
if (word[i] != isalpha(word[i]))
{
continue;
}
else
{
// Check the first two characters in the word while setting them to lower letters in order to get they ASCII value
sum += 26 * (tolower(word[0]) - 'a') + (tolower(word[1]) - 'a');
}
// Divide the total of the three letters by the number of buckets
hashvalue = sum % N;
}
return hashvalue;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// Open the file
FILE* file = fopen(dictionary, "r");
char word[LENGTH + 1];
if (file == NULL)
{
fclose(file);
perror("Loading error");
return false;
}
else
{
// While dictionary doesn't return EOF, read strings from the file, one at the time
while (fscanf(file, "%s", word) != EOF)
{
// Create a new node for the word and copy it in the node
node *n = malloc(sizeof(node));
if (n == NULL)
{
return false;
}
strcpy (n->word, "%s");
// Hash word to obtain a hash value
hashvalue = hash(word);
// Insert word into hash table depending if it's the first word or not
if (table[hashvalue] != NULL)
{
n->next = table[hashvalue];
}
else
{
n->next = NULL;
}
table[hashvalue] = n;
// Add one to the counter
numberofwords++;
}
fclose(file);
// Return true
return true;
}
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
// Return the number of words we're keeping track of while loading the dictionary
return numberofwords;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
// Check all the head of the hash table
for(int i = 0; i < N; i++)
{
// Create a temporary node to not lose the rest of the linked list
node* cursor = table[i];
// Set the cursor to the next node while the temporary value remains at the inital location, then free it before to move to cursor
while(cursor != NULL)
{
node* tmp = cursor;
cursor = cursor->next;
free(tmp);
}
}
return true;
}
Here is the dictonary.h file :
// Declares a dictionary's functionality
#ifndef DICTIONARY_H
#define DICTIONARY_H
#include <stdbool.h>
// Maximum length for a word
// (e.g., pneumonoultramicroscopicsilicovolcanoconiosis)
#define LENGTH 45
// Prototypes
bool check(const char *word);
unsigned int hash(const char *word);
bool load(const char *dictionary);
unsigned int size(void);
bool unload(void);
#endif // DICTIONARY_H
When I run my program i get these 2 errors and not sure why
The error
dictionary.c:87:9: error: use of undeclared identifier 'words_counter'
words_counter++;
^
dictionary.c:97:12: error: use of undeclared identifier 'words_counter'
return words_counter;
^
2 errors generated.
make: *** [Makefile:3: speller] Error 1
The code
// Implements a dictionary's functionality
#include <stdbool.h>
#include "dictionary.h"
#include <stdlib.h>
#include <strings.h>
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Number of buckets in hash table
const unsigned int N = 1500;
// Hash table
node *table[N];
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
// TODO
int index = hash(word);
node *cursor = table[index];
while (cursor != NULL)
{
if (strcasecmp(word, cursor -> word) ==0 )
{
return true;
}
//Move cursor to next node
cursor = cursor -> next;
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// TODO
// Ref of hash function: https://cs50.stackexchange.com/questions/38753/cs50-good-hash-function-for-pset5-speller/38755
int hash = 600;
while (*word != '\0')
{
hash = ((hash << 4 ) + (int) (*word)) % N;
word++;
}
return hash % N;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// TODO
FILE *file = fopen(dictionary, "r");
if (file == NULL)
{
return false;
}
// Read strings from file
// define word
char word [LENGTH + 1];
int index;
while(fscanf(file, "%s", word) != EOF)
{
// Creating new node
node *n = malloc(sizeof(node));
if (n == NULL)
{
return false;
}
// Copies word to the node
strcpy (n->word, word);
// Obtaining index to hash word
index = hash(word);
n -> next = table[index];
table[index] = n;
words_counter++;
}
fclose(file);
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
// TODO
return words_counter;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
// TODO
//Free nodes in each linked list
for (int i = 0; i<N; i++)
{
node *cursor = table[i];
node *tmp = cursor;
while (cursor != NULL)
{
cursor = cursor-> next;
free (tmp);
tmp = cursor;
}
}
return true;
}
I defined words_counter and it compiled but it seems I did not pass check50 by 2 mistakes:
the code
// Implements a dictionary's functionality
#include <stdbool.h>
#include "dictionary.h"
#include <stdlib.h>
#include <strings.h>
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Number of buckets in hash table
const unsigned int N = 1500;
unsigned words_counter;
// Hash table
node *table[N];
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
// TODO
int index = hash(word);
node *cursor = table[index];
while (cursor != NULL)
{
if (strcasecmp(word, cursor -> word) == 0 )
{
return true;
}
//Move cursor to next node
cursor = cursor -> next;
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// TODO
// Ref of hash function: https://cs50.stackexchange.com/questions/38753/cs50-good-hash-function-for-pset5-speller/38755
int hash = 600;
while (*word != '\0')
{
hash = ((hash << 4 ) + (int) (*word)) % N;
word++;
}
return hash % N;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// TODO
FILE *file = fopen(dictionary, "r");
if (file == NULL)
{
return false;
}
// Read strings from file
// define word
char word [LENGTH + 1];
int index;
while(fscanf(file, "%s", word) != EOF)
{
// Creating new node
node *n = malloc(sizeof(node));
if (n == NULL)
{
return false;
}
// Copies word to the node
strcpy (n->word, word);
// Obtaining index to hash word
index = hash(word);
n -> next = table[index];
table[index] = n;
words_counter++;
}
fclose(file);
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
// TODO
return words_counter;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
// TODO
//Free nodes in each linked list
for (int i = 0; i<N; i++)
{
node *cursor = table[i];
node *tmp = cursor;
while (cursor != NULL)
{
cursor = cursor-> next;
free (tmp);
tmp = cursor;
}
}
return true;
}
the mistakes:
:) dictionary.c exists
:) speller compiles
:( handles most basic words properly
expected "MISSPELLED WOR...", not "MISSPELLED WOR..."
:) handles min length (1-char) words
:) handles max length (45-char) words
:) handles words with apostrophes properly
:( spell-checking is case-insensitive
expected "MISSPELLED WOR...", not "MISSPELLED WOR..."
:) handles substrings properly
:) program is free of memory errors
I am not sure how to handle these 2
Your words_counter is used as the global variable in your code, so define it in the same place as other global objects:
const unsigned int N = 1500;
unsigned words_counter;
I am working on the CS50 pset5 Speller, and I keep getting a segmentation fault error. Debug50 suggests the problem is the line n->next = table[index]; in the implementation of the load function, line 110. I tried to revise but I canĀ“t figure out why it would give error. Here below my code, can anyone please help me?
// Implements a dictionary's functionality
#include <stdbool.h>
#include <strings.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node {
char word[LENGTH + 1];
struct node *next;
} node;
// Number of buckets in hash table
const unsigned int N = 150000;
// Nodes counter
int nodes_counter = 0;
// Hash table
node *table[N];
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
// TODO
int hash_value = hash(word);
node *cursor = malloc(sizeof(node));
if (cursor != NULL)
{
cursor = table[hash_value];
}
if (strcasecmp(cursor->word, word) == 0) // If word is first item in linked list
{
return 0;
}
else // Iterate over the list by moving the cursor
{
while (cursor->next != NULL)
{
if (strcasecmp(cursor->word, word) == 0) // If word is found
{
return 0;
}
else
{
cursor = cursor->next;
}
}
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// Adaptation of FNV function, source https://www.programmingalgorithms.com/algorithm/fnv-hash/c/
const unsigned int fnv_prime = 0x811C9DC5;
unsigned int hash = 0;
unsigned int i = 0;
for (i = 0; i < strlen(word); i++)
{
hash *= fnv_prime;
hash ^= (*word);
}
return hash;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// Open Dictionary File (argv[1] or dictionary?)
FILE *file = fopen(dictionary, "r");
if (file == NULL)
{
printf("Could not open file\n");
return 1;
}
// Read until end of file word by word (store word to read in word = (part of node)?)
char word[LENGTH + 1];
while(fscanf(file, "%s", word) != EOF)
{
// For each word, create a new node
node *n = malloc(sizeof(node));
if (n != NULL)
{
strcpy(n->word, word);
//Omitted to avoid segmentation fault n->next = NULL;
nodes_counter++;
}
else
{
return 2;
}
// Call hash function (input: word --> output: int)
int index = hash(word);
// Insert Node into Hash Table
n->next = table[index];
table[index] = n;
}
return false;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
// Return number of nodes created in Load
if (nodes_counter > 0)
{
return nodes_counter;
}
return 0;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
// TODO
for (int i = 0; i < N; i++)
{
node *cursor = table[i];
while (cursor->next != NULL)
{
node *tmp = cursor;
cursor = cursor->next;
free(tmp);
}
}
return false;
}
There are multiple problems in your code:
node *table[N]; cannot be only be defined as a global object if N is a constant expression. N is defined as a const unsigned int, but N is not a constant expression in C (albeit it is in C++). Your program compiles only because the compiler accepts this as a non portable extension. Either use a macro or an enum.
you overwrite cursor as soon as it is allocated in check(). There is no need to allocate a node in this function.
the hash() function should produce the same hash for words that differ only in case.
the hash() function only uses the first letter in word.
the hash() function can return a hash value >= N.
fscanf(file, "%s", word) should be protected agains buffer overflow.
you do not check if cursor is non null before dereferencing it in unload()
Here is a modified version:
// Implements a dictionary's functionality
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node {
char word[LENGTH + 1];
struct node *next;
} node;
// Number of buckets in hash table
enum { N = 150000 };
// Nodes counter
int nodes_counter = 0;
// Hash table
node *table[N];
// Returns true if word is in dictionary, else false
bool check(const char *word) {
int hash_value = hash(word);
// Iterate over the list by moving the cursor
for (node *cursor = table[hash_value]; cursor; cursor = cursor->next) {
if (strcasecmp(cursor->word, word) == 0) {
// If word is found
return true;
}
}
// If word is not found
return false;
}
// Hashes word to a number
unsigned int hash(const char *word) {
// Adaptation of FNV function, source https://www.programmingalgorithms.com/algorithm/fnv-hash/c/
unsigned int fnv_prime = 0x811C9DC5;
unsigned int hash = 0;
for (unsigned int i = 0; word[i] != '\0'; i++) {
hash *= fnv_prime;
hash ^= toupper((unsigned char)word[i]);
}
return hash % N;
}
// Loads dictionary into memory, returning true if successful, else a negative error number
int load(const char *dictionary) {
// Open Dictionary File (argv[1] or dictionary?)
FILE *file = fopen(dictionary, "r");
if (file == NULL) {
printf("Could not open file\n");
return -1;
}
// Read until end of file word by word (store word to read in word = (part of node)?)
char word[LENGTH + 1];
char format[10];
// construct the conversion specifier to limit the word size
// read by fscanf()
snprintf(format, sizeof format, "%%%ds", LENGTH);
while (fscanf(file, format, word) == 1) {
// For each word, create a new node
node *n = malloc(sizeof(node));
if (n == NULL) {
fclose(file);
return -2;
}
strcpy(n->word, word);
n->next = NULL;
nodes_counter++;
// Call hash function (input: word --> output: int)
int index = hash(word);
// Insert Node into Hash Table
n->next = table[index];
table[index] = n;
}
fclose(file);
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void) {
// Return number of nodes created in Load
return nodes_counter;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void) {
for (int i = 0; i < N; i++) {
node *cursor = table[i];
table[i] = NULL;
while (cursor != NULL) {
node *tmp = cursor;
cursor = cursor->next;
free(tmp);
}
}
return true;
}
I was trying to make a spell checker program where I first have to load a dictionary into memory. To do so, I tried using a hash table. The program shows a segmentation fault when I use the hash function for the hash table.
// Implements a dictionary's functionality
#include <stdbool.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Number of buckets in hash table
const unsigned int N = 6536;
// Hash table
node *table[N];
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
//TODO
return false;
}
// Hashes word to a number
unsigned int hash(const char *word) //Hashed using djb2
{
unsigned long hash = 5381;
int c = 0;
while (c == *word++)
{
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
}
return hash;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
char *str = NULL;
FILE *dict = fopen(dictionary,"r");
node *temp = NULL;
if(dict == NULL)
{
//printf("Error: \n", strerror(errno));
return false;
}
for(int i = 0; i < N; i++)
{
table[i] = NULL;
}
while(fscanf(dict, "%s", str) != 1)
{
unsigned int key = hash(str);
temp = malloc(sizeof(node));
if(temp == NULL)
{
return false;
}
if(table[key] != NULL)
{
temp->next = table[key]->next;
strcpy(temp->word, str);
table[key]->next = temp;
free(temp);
}
else
{
table[key] = malloc(sizeof(node));
table[key]->next = NULL;
strcpy(table[key]->word, str);
}
}
fclose(dict);
printf("SUCCESS\n");
return true;
}
The debugger shows the seg. fault occuring at unsigned int key = hash(str);. I'd like to know how this can be fixed.
Try
char str[MAX_LEN];
instead of
char *str = NULL;
(after defining MAX_LEN as appopriate for your application).
As M Oehm pointed out in a comment, I think you might be interpreting the return value of fscanf() incorrectly also.
I just finish writing a simple code for Pset5 speller just to check if my code is able to compile. The code is written into the header file. However, when I try to compile the file the error clang-7: error: linker command failed with exit code 1 shows up. Based on my shallow understanding of what have read online, it says that this error would occur if I declare and call a function that does not have any definition. Any help would be much appreciated. Here is my code:
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Number of buckets in hash table
const unsigned int N = 26;
// Hash table
node *table[N];
// Returns true if word is in dictionary else false
//uses the hash table to check
bool check(const char *word)
{
int hashindex = hash(word);
node *tmp = table[hashindex];
while (tmp->next != NULL)
{
if (strcmp(word, tmp->word) == 0)
{
return true;
}
tmp = tmp->next;
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
//hashing the first letter of the string
unsigned int value1 = (int) *(word);
unsigned int value2;
if (isupper(value1))
{
value2 = tolower(value1);
}
else
{
value2 = value1;
}
int hashvalue = value2 % 97;
return hashvalue;
}
// Loads dictionary into memory, returning true if successful else false
//hashes the words in the dictionary file
bool load(const char *dictionary)
{
// open dictionary file to load
FILE *file = fopen(dictionary, "r");
if (file == NULL)
{
printf("Could not open %s.\n", dictionary);
return false;
}
//making an array that can store scanned words from the dictionary
char dictionaryword[LENGTH];
//initializing all the buckets in the table
for (int i = 0; i < N; i++)
{
table[i] = NULL;
}
//loop to read the word one-by-one
while(fscanf(file, "%s", dictionaryword) != EOF)
{
//creating a new node to store the word
node *n = malloc(sizeof(node));
if (n == NULL)
{
return false;
}
strcpy(n->word, dictionaryword);
n->next = NULL;
//hashing the word to determine which bucket to store it in
int hashindex = hash(n->word);
//checking for any collisions
if (table[hashindex] == NULL)
{
table[hashindex] = n;
}
else
{
//adding new node from the end
node *tmp = table[hashindex];
while (tmp->next != NULL)
{
tmp = tmp->next;
}
tmp->next = n;
}
memset (dictionaryword, 0, LENGTH);
}
return true;
}
// Returns number of words in dictionary if loaded else 0 if not yet loaded
//the biggest number in the hash table
unsigned int size(void)
{
//iterate over each bucket, assuming all the buckets are filled
unsigned int counter = 0;
for (int i = 0; i < N; i++)
{
node* tmp = table[i];
while(tmp->next != NULL)
{
tmp = tmp->next;
counter++;
}
}
return counter;
}
// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
for (int i = 0; i < N; i++)
{
//if the bucket is not filled, memory is leaked somewhere
if (table[i] == NULL)
{
return false;
}
while (table[i] != NULL)
{
node *tmp = table[i]->next;
free(table[i]);
table[i] = tmp;
}
}
return true;
}
EDIT: This is the header file
#ifndef DICTIONARY_H
#define DICTIONARY_H
#include <stdbool.h>
// Maximum length for a word
// (e.g., pneumonoultramicroscopicsilicovolcanoconiosis)
#define LENGTH 45
// Prototypes
bool check(const char *word);
unsigned int hash(const char *word);
bool load(const char *dictionary);
unsigned int size(void);
bool unload(void);
#endif // DICTIONARY_H