Since train rides can be long and boring, I've decided to make use of that time and fiddle around with some good ol' C.
What I did was to create a way to enumerate words of a certain length of an arbitrary alphabet. Basically, suppose you have an alphabet
{ '0', '1' }
(ooh, binary!) and want to get all words of length 7, you'd call
enumerate("01", 7);
and have a nice array of pointers containing the strings "0000000" to "1111111". What for, you ask? Dunno. Stuff.
I've written it on my phone, so it may be a tad slimmer than the usual 80 characters. It's also not split into files for the very same reason.
#include <math.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *translate(
unsigned long value,
const char *alphabet,
size_t length
) {
size_t base;
char *result;
if (!alphabet || (base = strlen(alphabet)) < 2) {
return NULL;
}
if (length == 0) {
length = 1 + floor(log(value) / log(base));
}
result = malloc(length + 1);
if (!result) {
return NULL;
}
memset(result, alphabet[0], length);
result[length] = 0;
for (int i = length - 1; i >= 0 && value > 0; --i) {
unsigned long mod = value % base;
result[i] = alphabet[mod];
value -= mod;
value /= base;
}
return result;
}
char **enumerate(
const char *alphabet,
const size_t length
) {
size_t base;
if (!alphabet || (base = strlen(alphabet)) < 2) {
return NULL;
}
if (length == 0) {
return NULL;
}
unsigned long end = pow(base, length);
char **array = malloc((end + 1) * sizeof(char*));
if (!array) {
return NULL;
}
array[end] = NULL;
for (int i = 0; i != end; ++i) {
char *storage = malloc(length + 1);
char *translated = translate(i, alphabet, length);
if (!storage || !translated) {
if (storage) {
free(storage);
}
if (translated) {
free(translated);
}
array[i] = NULL;
goto error_cleanup;
}
strcpy(storage, translated);
free(translated);
array[i] = storage;
}
return array;
error_cleanup:
for (int i = 0; array[i] != NULL; ++i) {
free(array[i]);
}
free(array);
return NULL;
}
Example usage; simply concatenate the listings.
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <length> <alphabet>",
argv[0]);
exit(0);
}
size_t length = atoi(argv[1]);
char *alphabet = argv[2];
char **array = enumerate(alphabet, length);
if (!array) {
printf("Error while enumerating.");
exit(1);
}
for (int i = 0; array[i] != NULL; ++i) {
printf("%s\n", array[i]);
free(array[i]);
}
free(array);
exit(0);
}
What I'm most concerned about is idiomaticality (is that even a word?) of the code, since my C is rusty and self-taught, especially concerning the use of that string array. Also, is my goto used well and with good reason?