diff options
| author | dcw <d.white@imperial.ac.uk> | 2022-09-12 18:50:45 +0100 |
|---|---|---|
| committer | dcw <d.white@imperial.ac.uk> | 2022-09-12 18:50:45 +0100 |
| commit | 740ce546ae046e3502e1593893e3dd76ae76be0a (patch) | |
| tree | 5b7aec215176f7177823f6049caab2947e2551a6 | |
| parent | 7078df7f8a8818000a57279bcef0e9b52452df6a (diff) | |
| download | perlweeklychallenge-club-740ce546ae046e3502e1593893e3dd76ae76be0a.tar.gz perlweeklychallenge-club-740ce546ae046e3502e1593893e3dd76ae76be0a.tar.bz2 perlweeklychallenge-club-740ce546ae046e3502e1593893e3dd76ae76be0a.zip | |
spent a couple of hours translating the challenge 181 tasks to C.. can't have C++ beat C:-)
| -rw-r--r-- | challenge-181/duncan-c-white/C/.cbuild | 4 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/Makefile | 17 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/README | 15 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/args.c | 207 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/args.h | 11 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/ch-1.c | 163 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/ch-2.c | 116 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/input1 | 5 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/readline.c | 80 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/readline.h | 7 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/temperature.txt | 10 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/trim.c | 28 | ||||
| -rw-r--r-- | challenge-181/duncan-c-white/C/trim.h | 1 |
13 files changed, 664 insertions, 0 deletions
diff --git a/challenge-181/duncan-c-white/C/.cbuild b/challenge-181/duncan-c-white/C/.cbuild new file mode 100644 index 0000000000..a14ec76520 --- /dev/null +++ b/challenge-181/duncan-c-white/C/.cbuild @@ -0,0 +1,4 @@ +BUILD = ch-1 ch-2 +CFLAGS = -Wall -g +#LDFLAGS = -lm +#CFLAGS = -g diff --git a/challenge-181/duncan-c-white/C/Makefile b/challenge-181/duncan-c-white/C/Makefile new file mode 100644 index 0000000000..16fbd2e930 --- /dev/null +++ b/challenge-181/duncan-c-white/C/Makefile @@ -0,0 +1,17 @@ +CC = gcc +CFLAGS = -Wall -g +BUILD = ch-1 ch-2 + +all: $(BUILD) + +clean: + /bin/rm -f $(BUILD) *.o core a.out + +args.o: args.c +ch-1: ch-1.o args.o readline.o trim.o +ch-1.o: ch-1.c args.h readline.h trim.h +ch-2: ch-2.o args.o readline.o trim.o +ch-2.o: ch-2.c args.h readline.h trim.h +readline.o: readline.c readline.h +trim.o: trim.c + diff --git a/challenge-181/duncan-c-white/C/README b/challenge-181/duncan-c-white/C/README new file mode 100644 index 0000000000..f584fb29a6 --- /dev/null +++ b/challenge-181/duncan-c-white/C/README @@ -0,0 +1,15 @@ +Thought I'd also have a go at translating ch-1.pl and ch-2.pl into C.. + +ch-1.c and ch-2.c produces identical (non-debugging) output to my Perl +originals. + +Both use the command-line argument processing module args.[ch], and ch-2.c +also uses a pre-written readline() routine and a string trimming routine +(both not used in these Challenges before). + +They were both quite straightforward to translate, it's just that we have to +write a lot more low-level support code that Perl does for us.. however, +in ch-1, I folded all the stages together to avoid having to store all the +input, whether as a single huge string or as a single huge array of words. +Now I'm curious as to what the Perl ch-1 solution might have looked like if +I'd done it that way - perhaps it would be neater. diff --git a/challenge-181/duncan-c-white/C/args.c b/challenge-181/duncan-c-white/C/args.c new file mode 100644 index 0000000000..d4a2d38b9a --- /dev/null +++ b/challenge-181/duncan-c-white/C/args.c @@ -0,0 +1,207 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + + +bool debug = false; + + +// process_flag_noarg( name, argc, argv ); +// Process the -d flag, and check that there are no +// remaining arguments. +void process_flag_noarg( char *name, int argc, char **argv ) +{ + int arg=1; + if( argc>1 && strcmp( argv[arg], "-d" ) == 0 ) + { + debug = true; + arg++; + } + + int left = argc-arg; + if( left != 0 ) + { + fprintf( stderr, "Usage: %s [-d]\n", name ); + exit(1); + } +} + + +// int argno = process_flag_n_args( name, argc, argv, n, argmsg ); +// Process the -d flag, and check that there are exactly +// n remaining arguments, return the index position of the first +// argument. If not, generate a fatal Usage error using the argmsg. +// +int process_flag_n_args( char *name, int argc, char **argv, int n, char *argmsg ) +{ + int arg=1; + if( argc>1 && strcmp( argv[arg], "-d" ) == 0 ) + { + debug = true; + arg++; + } + + int left = argc-arg; + if( left != n ) + { + fprintf( stderr, "Usage: %s [-d] %s\n Exactly %d " + "arguments needed\n", name, argmsg, n ); + exit(1); + } + return arg; +} + + +// int argno = process_flag_n_m_args( name, argc, argv, min, max, argmsg ); +// Process the -d flag, and check that there are between +// min and max remaining arguments, return the index position of the first +// argument. If not, generate a fatal Usage error using the argmsg. +// +int process_flag_n_m_args( char *name, int argc, char **argv, int min, int max, char *argmsg ) +{ + int arg=1; + if( argc>1 && strcmp( argv[arg], "-d" ) == 0 ) + { + debug = true; + arg++; + } + + int left = argc-arg; + if( left < min || left > max ) + { + fprintf( stderr, "Usage: %s [-d] %s\n Between %d and %d " + "arguments needed\n", name, argmsg, min, max ); + exit(1); + } + return arg; +} + + +// process_onenumarg_default( name, argc, argv, defvalue, &n ); +// Process the -d flag, and check that there is a single +// remaining numeric argument (or no arguments, in which case +// we use the defvalue), putting it into n +void process_onenumarg_default( char *name, int argc, char **argv, int defvalue, int *n ) +{ + char argmsg[100]; + sprintf( argmsg, "[int default %d]", defvalue ); + int arg = process_flag_n_m_args( name, argc, argv, 0, 1, argmsg ); + + *n = arg == argc ? defvalue : atoi( argv[arg] ); +} + + +// process_onenumarg( name, argc, argv, &n ); +// Process the -d flag, and check that there is a single +// remaining numeric argument, putting it into n +void process_onenumarg( char *name, int argc, char **argv, int *n ) +{ + int arg = process_flag_n_args( name, argc, argv, 1, "int" ); + + // argument is in argv[arg] + *n = atoi( argv[arg] ); +} + + +// process_twonumargs( name, argc, argv, &m, &n ); +// Process the -d flag, and check that there are 2 +// remaining numeric arguments, putting them into m and n +void process_twonumargs( char *name, int argc, char **argv, int *m, int *n ) +{ + int arg = process_flag_n_args( name, argc, argv, 2, "int" ); + + // arguments are in argv[arg] and argv[arg+1] + *m = atoi( argv[arg++] ); + *n = atoi( argv[arg] ); +} + + +// process_twostrargs() IS DEPRECATED: use process_flag_n_m_args() instead + + +// int arr[100]; +// int nel = process_listnumargs( name, argc, argv, arr, 100 ); +// Process the -d flag, and check that there are >= 2 +// remaining numeric arguments, putting them into arr[0..nel-1] +// and returning nel. +int process_listnumargs( char *name, int argc, char **argv, int *arr, int maxel ) +{ + int arg=1; + if( argc>1 && strcmp( argv[arg], "-d" ) == 0 ) + { + debug = true; + arg++; + } + + int left = argc-arg; + if( left < 2 ) + { + fprintf( stderr, "Usage: %s [-d] list_of_numeric_args\n", name ); + exit(1); + } + if( left > maxel ) + { + fprintf( stderr, "%s: more than %d args\n", name, maxel ); + exit(1); + } + + // elements are in argv[arg], argv[arg+1]... + + if( debug ) + { + printf( "debug: remaining arguments are in arg=%d, " + "firstn=%s, secondn=%s..\n", + arg, argv[arg], argv[arg+1] ); + } + + int nel = 0; + for( int i=arg; i<argc; i++ ) + { + arr[nel++] = atoi( argv[i] ); + } + arr[nel] = -1; + return nel; +} + + +// +// bool isint = check_unsigned_int( char *val, int *n ); +// Given an string val, check that there's an unsigned integer +// in it (after optional whitespace). If there is a valid +// unsigned integer value, store that integer value in *n and +// return true; otherwise return false (and don't alter *n). +bool check_unsigned_int( char *val, int *n ) +{ + // skip whitespace in val + char *p; + for( p=val; isspace(*p); p++ ) + { + /*EMPTY*/ + } + if( ! isdigit(*p) ) return false; + *n = atoi(p); + return true; +} + + +// +// bool ok = check_unsigned_real( char *val, double *n ); +// Given an string val, check that there's an unsigned real +// in it (after optional whitespace). If there is a valid +// unsigned real value, store that value in *n and +// return true; otherwise return false (and don't alter *n). +bool check_unsigned_real( char *val, double *n ) +{ + // skip whitespace in val + char *p; + for( p=val; isspace(*p); p++ ) + { + /*EMPTY*/ + } + if( ! isdigit(*p) ) return false; + *n = atof(p); + return true; +} diff --git a/challenge-181/duncan-c-white/C/args.h b/challenge-181/duncan-c-white/C/args.h new file mode 100644 index 0000000000..8844a8f9c4 --- /dev/null +++ b/challenge-181/duncan-c-white/C/args.h @@ -0,0 +1,11 @@ +extern bool debug; + +extern void process_flag_noarg( char * name, int argc, char ** argv ); +extern int process_flag_n_args( char * name, int argc, char ** argv, int n, char * argmsg ); +extern int process_flag_n_m_args( char * name, int argc, char ** argv, int min, int max, char * argmsg ); +extern void process_onenumarg_default( char * name, int argc, char ** argv, int defvalue, int * n ); +extern void process_onenumarg( char * name, int argc, char ** argv, int * n ); +extern void process_twonumargs( char * name, int argc, char ** argv, int * m, int * n ); +extern int process_listnumargs( char * name, int argc, char ** argv, int * arr, int maxel ); +extern bool check_unsigned_int( char * val, int * n ); +extern bool check_unsigned_real( char * val, double * n ); diff --git a/challenge-181/duncan-c-white/C/ch-1.c b/challenge-181/duncan-c-white/C/ch-1.c new file mode 100644 index 0000000000..981931df4d --- /dev/null +++ b/challenge-181/duncan-c-white/C/ch-1.c @@ -0,0 +1,163 @@ +// +// Task 1: Sentence Order +// +// C version. +// + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "args.h" +#include "readline.h" +#include "trim.h" + + +// a word +#define MAXWORDLEN 100 +typedef char word[MAXWORDLEN]; + +// an incomplete sentence: an array of words. +#define MAXWORDS 1000 +word wd[MAXWORDS]; +int nwords = 0; + +// a line can be this long +#define MAXLINELEN 1000 + +// output stage: lines no more than.. +#define OUTLINELEN 62 +int linewidth; // current length of line printed + +static int comparewords( const void *a, const void *b) +{ + char *wa = (char *) a; + char *wb = (char *) b; + return strcasecmp( wa, wb ); +} + + +// +// deal_with_sentence(); +// deal with the complete sentence in wd[0..nwords-1] +// +static void deal_with_sentence( void ) +{ + assert( nwords>0 ); + + // sort the words + qsort( wd, nwords, MAXWORDLEN, &comparewords ); + + // add the '.' back to the last word + char *last = wd[nwords-1]; + int len = strlen(last); + strcat( last+len, "." ); + + if( debug ) + { + printf( "debug: sorted sentence:" ); + for( int i=0; i<nwords; i++ ) + { + printf( " %s", wd[i] ); + } + putchar( '\n' ); + } + + // print out words, controlling the width as we go + // (recall that linewidth == #chars used on line) + for( int i=0; i<nwords; i++ ) + { + int len = strlen( wd[i] ); + if( linewidth + len > OUTLINELEN ) // start a new line + { + putchar( '\n' ); + linewidth = 0; + } + if( linewidth>0 ) // space-separate words + { + putchar( ' ' ); + linewidth++; + } + printf( "%s", wd[i] ); + linewidth += len; + } +} + + +// +// deal_with_word( wd ); +// we've found a word wd, which may or may not end in +// a '.'. deal with it. +// +static void deal_with_word( char *str ) +{ + if( debug ) + { + printf( "debug: found word %s\n", str ); + } + assert( nwords < MAXWORDS ); + char *w = wd[nwords++]; + strcpy( w, str ); + int len = strlen(w); + char *last = w+len-1; + //printf( "debug: word '%s' last char '%c'\n", w, *last ); + if( *last == '.' ) + { + *last = '\0'; + + // found end of sentence. + deal_with_sentence(); + + nwords = 0; + } +} + + +int main( int argc, char **argv ) +{ + (void)process_flag_n_args( "sentence-order", argc, argv, + 0, "[reads stdin]" ); + + nwords = 0; + linewidth = 0; + + char line[MAXLINELEN]; + + while( readline_discardrest( stdin, line, MAXLINELEN ) ) + { + trim( line ); + if( debug ) + { + printf( "read line '%s'\n", line ); + } + + // now split line into words + char *wordstart = line; + for( char *s = line+1; *s != '\0'; s++ ) + { + if( ! isspace(*s) ) continue; + + *s = '\0'; // nul-terminate + + deal_with_word( wordstart ); + + wordstart = s+1; + } + if( *wordstart != '\0' ) + { + deal_with_word( wordstart ); + } + } + + if( nwords>0 ) + { + deal_with_sentence(); + } + + putchar( '\n' ); + + return 0; +} diff --git a/challenge-181/duncan-c-white/C/ch-2.c b/challenge-181/duncan-c-white/C/ch-2.c new file mode 100644 index 0000000000..8fe614cfea --- /dev/null +++ b/challenge-181/duncan-c-white/C/ch-2.c @@ -0,0 +1,116 @@ +// +// Task 2: Hot Day +// +// C version. +// + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "args.h" +#include "readline.h" +#include "trim.h" + + +#define MAXDATESTRLEN 12 +typedef struct { + char datestr[MAXDATESTRLEN]; // an ISO date str + int temp; // associated temperature +} datetemp; + + +// +// datetemp dt; +// bool ok = split_dt( str, &dt ); +// Split a string (str) into a datestring and temp, +// storing the results in dt, and returning true if +// splitting is successful; or return false if it fails. +// +bool split_dt( char *str, datetemp *dt ) +{ + char *comma = strchr( str, ',' ); + if( comma == NULL ) return false; + *comma = '\0'; + if( strlen(str) > MAXDATESTRLEN ) return false; + strcpy( dt->datestr, str ); + + for( str = comma+1; isspace(*str); str++ ) /*EMPTY*/; + + if( ! isdigit(*str) ) return false; + dt->temp = atoi( str ); + + return true; +} + + +#define MAXLINES 100000 +datetemp dt[MAXLINES]; +int nlines; + + +static int comparedts( const void *a, const void *b) +{ + datetemp *da = (datetemp *) a; + datetemp *db = (datetemp *) b; + return strcmp(da->datestr, db->datestr); +} + +int main( int argc, char **argv ) +{ + (void)process_flag_n_args( "hot-day", argc, argv, + 0, "[reads stdin]" ); + + char line[40]; + + for( nlines = 0; readline_discardrest( stdin, line, 40 ); nlines++ ) + { + assert( nlines < MAXLINES ); + trim( line ); + if( debug ) + { + printf( "read line '%s'\n", line ); + } + if( ! split_dt( line, &dt[nlines] ) ) + { + fprintf( stderr, "hot-day: can't parse '%s' as " + "date, temp\n", line ); + exit(1); + } + if( debug ) + { + printf( "line '%s' split into date %s, temp %d\n", + line, dt[nlines].datestr, dt[nlines].temp ); + } + } + if( debug ) + { + printf( "%d lines\n", nlines ); + } + + // now sort them + qsort( dt, nlines, sizeof(datetemp), &comparedts ); + + // now find all dates D for which the temperature + // on date D is hotter than on D-1. + for( int pos=1; pos<nlines; pos++ ) + { + datetemp *today = &dt[pos]; + datetemp *yest = &dt[pos-1]; + if( debug ) + { + printf( "processing date %s, yest %s, today temp %d, " + "yest temp %d\n", today->datestr, + yest->datestr, today->temp, yest->temp ); + } + if( yest->temp < today->temp ) + { + printf( "%s\n", today->datestr ); + } + } + + return 0; +} diff --git a/challenge-181/duncan-c-white/C/input1 b/challenge-181/duncan-c-white/C/input1 new file mode 100644 index 0000000000..4fe719ed90 --- /dev/null +++ b/challenge-181/duncan-c-white/C/input1 @@ -0,0 +1,5 @@ + All he could think about was how it would all end. There was + still a bit of uncertainty in the equation, but the basics + were there for anyone to see. No matter how much he tried to + see the positive, it wasn't anywhere to be seen. The end was +coming and it wasn't going to be pretty. diff --git a/challenge-181/duncan-c-white/C/readline.c b/challenge-181/duncan-c-white/C/readline.c new file mode 100644 index 0000000000..32fc59fb9a --- /dev/null +++ b/challenge-181/duncan-c-white/C/readline.c @@ -0,0 +1,80 @@ +/* + * readline.c: utility function readline.. + */ + +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <stdlib.h> +#include <stdbool.h> + +#include "readline.h" + + +/* + * bool lineread = readline( in, buf, maxlen ); + * read a single line from in (a FILE * open for input) + * into a character buffer (char *), reading no more than + * maxlen characters, removing a trailing '\n' from the + * buffer (if there is one). + * + * return false if EOF, else true if there's a line. + * + * If the input line is longer than that, the REMAINDER OF + * THE LINE IS LEFT IN the file's input buffer for later reading. + * If readline() is called again immediately, the remainder of + * the line will be treated as a separate line. + * + * In caller-space, buf must be a writable character buffer of + * size >= maxlen, i.e. typically either a char [n] + * or a malloc(n * sizeof(char)) chunk, + * where (in both cases) n >= maxlen + */ +bool readline( FILE *in, char *buf, int maxlen ) +{ + if( fgets( buf, maxlen, in ) == NULL ) + { + return false; + } + int l = strlen(buf); + if( l>0 && buf[l-1]=='\n' ) + { + buf[l-1] = '\0'; + } + return true; +} + + +/* + * bool lineread = readline_discardrest( in, buf, maxlen ); + * read a single line from in (a FILE * open for input) + * into a character buffer (char *), reading no more than + * maxlen characters, removing a trailing '\n' from the + * buffer (if there is one). + * + * return false if EOF, else true if there's a line. + * + * If the input line is longer than that, the REMAINDER OF + * THE LINE IS DISCARDED. + * + * In caller-space, buf must be a writable character buffer of + * size >= maxlen, i.e. typically either a char [n] + * or a malloc(n * sizeof(char)) chunk, + * where (in both cases) n >= maxlen + */ +bool readline_discardrest( FILE *in, char *buf, int maxlen ) +{ + if( fgets( buf, maxlen, in ) == NULL ) + { + return false; + } + int l = strlen(buf); + if( l>0 && buf[l-1]=='\n' ) + { + buf[l-1] = '\0'; + } else + { + while( getchar() != '\n' ) /*EMPTY*/; + } + return true; +} diff --git a/challenge-181/duncan-c-white/C/readline.h b/challenge-181/duncan-c-white/C/readline.h new file mode 100644 index 0000000000..be8e508bd8 --- /dev/null +++ b/challenge-181/duncan-c-white/C/readline.h @@ -0,0 +1,7 @@ +/* + * readline.h: utility functions to readline arbitrarily + * long lines w/o overflow.. + */ + +extern bool readline( FILE * in, char * buf, int maxlen ); +extern bool readline_discardrest( FILE * in, char * buf, int maxlen ); diff --git a/challenge-181/duncan-c-white/C/temperature.txt b/challenge-181/duncan-c-white/C/temperature.txt new file mode 100644 index 0000000000..7b251c9aec --- /dev/null +++ b/challenge-181/duncan-c-white/C/temperature.txt @@ -0,0 +1,10 @@ +2022-08-01, 20 +2022-08-09, 10 +2022-08-03, 19 +2022-08-06, 24 +2022-08-05, 22 +2022-08-10, 28 +2022-08-07, 20 +2022-08-04, 18 +2022-08-08, 21 +2022-08-02, 25 diff --git a/challenge-181/duncan-c-white/C/trim.c b/challenge-181/duncan-c-white/C/trim.c new file mode 100644 index 0000000000..2ac26bfac9 --- /dev/null +++ b/challenge-181/duncan-c-white/C/trim.c @@ -0,0 +1,28 @@ +#include <ctype.h> +#include <stdio.h> +#include <string.h> + + +// +// trim( str ); +// given a writable string (str), modify it to remove any leading +// or trailing whitespace. +// +void trim( char *str ) +{ + int len = strlen(str); + if( isspace( str[len-2] ) ) + { + char *s; + for( s = str+len-2; s>str && isspace(*s); s-- ) /*EMPTY*/ ; + s++; + *s = '\0'; // chop off trailing whitespace + } + + if( isspace(*str) ) + { + char *s; + for( s = str; isspace(*s); s++ ) /*EMPTY*/ ; + for( char *d = str; (*d = *s ) != '\0'; s++, d++ ) /*EMPTY*/ ; + } +} diff --git a/challenge-181/duncan-c-white/C/trim.h b/challenge-181/duncan-c-white/C/trim.h new file mode 100644 index 0000000000..09e5c93ac7 --- /dev/null +++ b/challenge-181/duncan-c-white/C/trim.h @@ -0,0 +1 @@ +extern void trim( char * str ); |
