diff options
| -rw-r--r-- | challenge-180/duncan-c-white/C/.cbuild | 2 | ||||
| -rw-r--r-- | challenge-180/duncan-c-white/C/Makefile | 16 | ||||
| -rw-r--r-- | challenge-180/duncan-c-white/C/README | 14 | ||||
| -rw-r--r-- | challenge-180/duncan-c-white/C/args.c | 207 | ||||
| -rw-r--r-- | challenge-180/duncan-c-white/C/args.h | 11 | ||||
| -rw-r--r-- | challenge-180/duncan-c-white/C/ch-1.c | 52 | ||||
| -rw-r--r-- | challenge-180/duncan-c-white/C/ch-2.c | 61 | ||||
| -rw-r--r-- | challenge-180/duncan-c-white/C/parseints.c | 104 | ||||
| -rw-r--r-- | challenge-180/duncan-c-white/C/parseints.h | 1 | ||||
| -rw-r--r-- | challenge-180/duncan-c-white/C/prarray.c | 39 | ||||
| -rw-r--r-- | challenge-180/duncan-c-white/C/prarray.h | 1 | ||||
| -rw-r--r-- | challenge-180/duncan-c-white/README | 54 | ||||
| -rwxr-xr-x | challenge-180/duncan-c-white/perl/ch-1.pl | 67 | ||||
| -rwxr-xr-x | challenge-180/duncan-c-white/perl/ch-2.pl | 49 |
14 files changed, 655 insertions, 23 deletions
diff --git a/challenge-180/duncan-c-white/C/.cbuild b/challenge-180/duncan-c-white/C/.cbuild index 624a95ebfb..a14ec76520 100644 --- a/challenge-180/duncan-c-white/C/.cbuild +++ b/challenge-180/duncan-c-white/C/.cbuild @@ -1,4 +1,4 @@ -BUILD = ch-1 +BUILD = ch-1 ch-2 CFLAGS = -Wall -g #LDFLAGS = -lm #CFLAGS = -g diff --git a/challenge-180/duncan-c-white/C/Makefile b/challenge-180/duncan-c-white/C/Makefile new file mode 100644 index 0000000000..43bc04f881 --- /dev/null +++ b/challenge-180/duncan-c-white/C/Makefile @@ -0,0 +1,16 @@ +BUILD = ch-1 ch-2 +CC = gcc +CFLAGS = -Wall -g +#LDLIBS = -lm + +all: $(BUILD) + +clean: + /bin/rm -f $(BUILD) *.o core a.out + +ch-1: ch-1.o args.o +ch-1.o: ch-1.c args.h +ch-2: ch-2.o args.o parseints.o prarray.o +ch-2.o: ch-2.c args.h parseints.h prarray.h +parseints.o: parseints.h +args.o: args.h diff --git a/challenge-180/duncan-c-white/C/README b/challenge-180/duncan-c-white/C/README index 12ccf8a6e3..2aeea413e9 100644 --- a/challenge-180/duncan-c-white/C/README +++ b/challenge-180/duncan-c-white/C/README @@ -1,8 +1,12 @@ -Thought I'd also have a go at translating ch-1.pl into C.. +Thought I'd also have a go at translating ch-1.pl and ch-2.pl into C.. -ch-1.c produces identical (non-debugging) output to my Perl originals. +ch-1.c and ch-2.c produces identical (non-debugging) output to my Perl +originals. -It uses the command-line argument processing module args.[ch]. +Both use the command-line argument processing module args.[ch], and ch-2.c +also uses the prarray.[ch] array printing function, and a new int (or comma-sep +lists of int) argument parsing function in parseints.[ch]. -It was very straightforward to translate, it's just that we have to write -a lot more low-level support code that Perl does for us.. +They were both very straightforward to translate, it's just that we have to +write a lot more low-level support code that Perl does for us.. for instance, +in ch-1, the frequency hash over characters became a 256-element int array. diff --git a/challenge-180/duncan-c-white/C/args.c b/challenge-180/duncan-c-white/C/args.c new file mode 100644 index 0000000000..d4a2d38b9a --- /dev/null +++ b/challenge-180/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-180/duncan-c-white/C/args.h b/challenge-180/duncan-c-white/C/args.h new file mode 100644 index 0000000000..8844a8f9c4 --- /dev/null +++ b/challenge-180/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-180/duncan-c-white/C/ch-1.c b/challenge-180/duncan-c-white/C/ch-1.c new file mode 100644 index 0000000000..c2013856b1 --- /dev/null +++ b/challenge-180/duncan-c-white/C/ch-1.c @@ -0,0 +1,52 @@ +// +// Task 1: First Unique Character +// +// C version. +// + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "args.h" + + +// +// int pos = fuc_pos( s ); +// Find the position of the first unique character in the string s, +// or -1 if all characters in s are repeated. +// +int fuc_pos( char *s ) +{ + int freq[256]; // array over char of int + for( int i=0; i<256; i++ ) freq[i] = 0; + for( char *p = s; *p; p++ ) + { + freq[(int)*p]++; + } + + int pos = 0; + for( char *p = s; *p; p++, pos++ ) + { + if( freq[(int)*p] == 1 ) + { + return pos; + } + } + return -1; +} + + +int main( int argc, char **argv ) +{ + int argno = process_flag_n_args( "fuc", argc, argv, + 1, "String" ); + char *str = argv[argno]; + + int pos = fuc_pos( str ); + printf( "%d\n", pos ); + return 0; +} diff --git a/challenge-180/duncan-c-white/C/ch-2.c b/challenge-180/duncan-c-white/C/ch-2.c new file mode 100644 index 0000000000..840c686564 --- /dev/null +++ b/challenge-180/duncan-c-white/C/ch-2.c @@ -0,0 +1,61 @@ +// +// Task 2: Trim List +// +// C version. +// + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "args.h" +#include "prarray.h" +#include "parseints.h" + + +int main( int argc, char **argv ) +{ + int argno = process_flag_n_m_args( "trim-list", argc, argv, + 2, 1024, "I ListofIntegers" ); + char *arg = argv[argno++]; // I + + int x; + if( !check_unsigned_int(arg,&x) ) + { + fprintf( stderr, "trim-list: arg %s must be +int\n", arg ); + exit(1); + } + + // all remaining arguments should be single ints, or + // comma-separated lists of ints. Accumulate them in a single list + // (terminated by a -1). + int nel; + int *list = parse_int_args( argc, argv, argno, &nel ); + + // now trim, i.e. select only those elements >= x. + // could do it in place, but it's easier to copy the elements out. + int trimmed[nel+1]; + int *dst = trimmed; + int ndst = 0; + + for( int *l=list; *l != -1; l++ ) + { + if( *l >= x ) + { + *dst++ = *l; + ndst++; + } + } + + free( list ); + + putchar( '(' ); + print_int_array( 80, ndst, trimmed, stdout ); + putchar( ')' ); + putchar( '\n' ); + + return 0; +} diff --git a/challenge-180/duncan-c-white/C/parseints.c b/challenge-180/duncan-c-white/C/parseints.c new file mode 100644 index 0000000000..0850f13662 --- /dev/null +++ b/challenge-180/duncan-c-white/C/parseints.c @@ -0,0 +1,104 @@ +// Simple routine to parse one or more arguments, +// looking for individual +ints or comma-separated +// lists of +ints. +// + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "args.h" +#include "prarray.h" +#include "parseints.h" + +typedef struct +{ + int nel; // current number of elements + int maxel; // maximum number of elements allocated + int *list; // malloc()d list of integers +} intlist; + + +// +// intlist il.. then initialize il.. then: +// add_one( element, &il ); +// +static void add_one( int x, intlist *p ) +{ + if( p->nel > p->maxel ) + { + p->maxel += 128; + p->list = realloc( p->list, p->maxel ); + assert( p->list ); + } + if( debug ) + { + printf( "PIA: appending %d to result at " + "pos %d\n", x, p->nel ); + } + p->list[p->nel++] = x; +} + + +// +// intlist il.. then initialize il.. then: +// add_one_arg( argstr, &il ); +// +static void add_one_arg( char *argstr, intlist *p ) +{ + int x; + if( !check_unsigned_int(argstr,&x) ) + { + fprintf( stderr, "PIA: arg %s must be +int\n", argstr ); + exit(1); + } + add_one( x, p ); +} + + +// +// int nel; +// int *ilist = parse_int_args( argc, argv, argno, &nel ); +// process all arguments argv[argno..argc-1], extracting either +// single ints or comma-separated lists of ints from those arguments, +// accumulate all integers in a dynarray list, storing the total number +// of elements in nel. This list must be freed by the caller. +int *parse_int_args( int argc, char **argv, int argno, int *nel ) +{ + int *result = malloc( 128 * sizeof(int) ); + assert( result ); + intlist il = { 0, 128, result }; + + if( debug ) + { + printf( "PIA: parsing ints from args %d..%d\n", argno, argc-1 ); + } + for( int i=argno; i<argc; i++ ) + { + assert( strlen(argv[i]) < 1024 ); + char copy[1024]; + strcpy( copy, argv[i] ); + char *com; + char *s; + for( s=copy; (com = strchr(s,',')) != NULL; s=com+1 ) + { + *com = '\0'; + add_one_arg( s, &il ); + } + add_one_arg( s, &il ); + } + + add_one( -1, &il ); + + if( debug ) + { + printf( "PIA: final list is " ); + print_int_array( 80, il.nel, il.list, stdout ); + } + + *nel = il.nel; + return il.list; +} diff --git a/challenge-180/duncan-c-white/C/parseints.h b/challenge-180/duncan-c-white/C/parseints.h new file mode 100644 index 0000000000..da5e145a86 --- /dev/null +++ b/challenge-180/duncan-c-white/C/parseints.h @@ -0,0 +1 @@ +extern int * parse_int_args( int argc, char ** argv, int argno, int * nel ); diff --git a/challenge-180/duncan-c-white/C/prarray.c b/challenge-180/duncan-c-white/C/prarray.c new file mode 100644 index 0000000000..2de253319b --- /dev/null +++ b/challenge-180/duncan-c-white/C/prarray.c @@ -0,0 +1,39 @@ +#include <stdio.h> +#include <string.h> + + +// print_int_array( maxw, nelements, results[], outfile ); +// format results[0..nelements-1] as a comma separated +// list onto outfile with lines <= maxw chars long. +// produces a whole number of lines of output. +void print_int_array( int maxw, int nel, int *results, FILE *out ) +{ + int linelen = 0; + for( int i=0; i<nel; i++ ) + { + char buf[100]; + sprintf( buf, "%d", results[i] ); + int len = strlen(buf); + if( linelen + len + 2 > maxw ) + { + fputc( '\n', out ); + linelen = 0; + } else if( i>0 ) + { + fputc( ' ', out ); + linelen++; + } + + linelen += len; + fprintf( out, "%s", buf ); + if( i<nel-1 ) + { + fputc( ',', out ); + linelen++; + } + } + //if( linelen>0 ) + //{ + // fputc( '\n', out ); + //} +} diff --git a/challenge-180/duncan-c-white/C/prarray.h b/challenge-180/duncan-c-white/C/prarray.h new file mode 100644 index 0000000000..789d6c0d0f --- /dev/null +++ b/challenge-180/duncan-c-white/C/prarray.h @@ -0,0 +1 @@ +extern void print_int_array( int maxw, int nel, int * results, FILE * out ); diff --git a/challenge-180/duncan-c-white/README b/challenge-180/duncan-c-white/README index 635f772de2..3a551d6972 100644 --- a/challenge-180/duncan-c-white/README +++ b/challenge-180/duncan-c-white/README @@ -1,29 +1,49 @@ -Task 1: Ordinal Number Spelling +Task 1: First Unique Character -You are given a positive number, $n. -Write a script to spell the ordinal number. +You are given a string, $s. +Write a script to find out the first unique character in the given string +and print its index (0-based). -For example, +Example 1 -11 => eleventh -62 => sixty-second -99 => ninety-ninth +Input: $s = "Perl Weekly Challenge" +Output: 0 as 'P' is the first unique character -MY NOTES: ok, sounds pretty easy for n>0; let's arbitrarily deal with -n < 1000000 (although millions, billions etc would be pretty easy to add). -Let's do this via formwords(n) which converts (eg) n=17 to "seventeen" and -then use that to generate the final "seventeeth" (nth, ordinal, form) by -mucking about with the ending (and using some special case rules for English, -eg mapping one->first) +Example 2 + +Input: $s = "Long Live Perl" +Output: 1 as 'o' is the first unique character + +MY NOTES: ok, sounds extremely easy, first pass: build a freq hash (a bag), +second pass: sequence through the string, print index of first char +with freq 1. There is one case where the behaviour is undefined: what if +there are no unique characters in the string? I've decided to produce pos -1 +in that case. GUEST LANGUAGE: As a bonus, I also had a go at translating ch-1.pl into C (look in the C directory for that). -Task 2: Unicode Sparkline +Task 2: Trim List + +You are given list of numbers, @n and an integer $i. +Write a script to trim the given list where element is less than or +equal to the given integer. + +Example 1 -You are given a list of positive numbers, @n. +Input: @n = (1,4,2,3,5) and $i = 3 +Output: (4,5) -Write a script to print sparkline in Unicode for the given list of numbers. +Example 2 -MY NOTES: What on earth is "sparkline"? Not a fan of Unicode either, forget it. +Input: @n = (9,0,6,2,3,8,5) and $i = 4 +Output: (9,6,8,5) + +MY NOTES: also looks incredibly easy, basically grep { $_ >= $i }. +Just to bulk the task up a little, I allowed the input list to be +spread across multiple arguments, each of which can either store a +simple +ve int, or a comma-separated list of one or more +ve ints. + +GUEST LANGUAGE: As a bonus, I also had a go at translating ch-2.pl +into C (look in the C directory for that). diff --git a/challenge-180/duncan-c-white/perl/ch-1.pl b/challenge-180/duncan-c-white/perl/ch-1.pl new file mode 100755 index 0000000000..95650a8746 --- /dev/null +++ b/challenge-180/duncan-c-white/perl/ch-1.pl @@ -0,0 +1,67 @@ +#!/usr/bin/perl +# +# Task 1: First Unique Character +# +# You are given a string, $s. +# Write a script to find out the first unique character in the given string +# and print its index (0-based). +# +# Example 1 +# +# Input: $s = "Perl Weekly Challenge" +# Output: 0 as 'P' is the first unique character +# +# Example 2 +# +# Input: $s = "Long Live Perl" +# Output: 1 as 'o' is the first unique character +# +# MY NOTES: ok, sounds extremely easy, first pass: build a freq hash (a bag), +# second pass: sequence through the string, print index of the first char +# with freq 1. There is one case where the behaviour is undefined: what if +# there are no unique characters in the string? I've decided to produce pos -1 +# in that case. +# +# GUEST LANGUAGE: As a bonus, I also had a go at translating ch-1.pl +# into C (look in the C directory for that). +# + +use strict; +use warnings; +use feature 'say'; +use Getopt::Long; +use Data::Dumper; + + +my $debug=0; +die "Usage: fuc [--debug] Str\n" + unless GetOptions( "debug"=>\$debug ) && @ARGV==1; + +my $s = shift; + +# +# my $pos = fuc_pos( $s ); +# Find the position of the first unique character in $s, +# or -1 if all characters in $s are repeated. +# +sub fuc_pos +{ + my( $s ) = @_; + + my %freq; + foreach my $ch (split(//,$s)) + { + $freq{$ch}++; + } + foreach my $pos (0..length($s)-1) + { + if( $freq{substr($s,$pos,1)} == 1 ) + { + return $pos; + } + } + return -1; +} + +my $pos = fuc_pos( $s ); +say $pos; diff --git a/challenge-180/duncan-c-white/perl/ch-2.pl b/challenge-180/duncan-c-white/perl/ch-2.pl new file mode 100755 index 0000000000..ddf8d124ad --- /dev/null +++ b/challenge-180/duncan-c-white/perl/ch-2.pl @@ -0,0 +1,49 @@ +#!/usr/bin/perl +# +# Task 2: Trim List +# +# You are given list of numbers, @n and an integer $i. +# Write a script to trim the given list where element is less than or +# equal to the given integer. +# +# Example 1 +# +# Input: @n = (1,4,2,3,5) and $i = 3 +# Output: (4,5) +# +# Example 2 +# +# Input: @n = (9,0,6,2,3,8,5) and $i = 4 +# Output: (9,6,8,5) +# +# MY NOTES: also looks incredibly easy, basically grep { $_ >= $i }. +# Just to bulk the task up a little, I allowed the input list to be +# spread across multiple arguments, each of which can either store a +# simple +ve int, or a comma-separated list of one or more +ve ints. +# +# GUEST LANGUAGE: As a bonus, I also had a go at translating ch-2.pl +# into C (look in the C directory for that). +# + +use strict; +use warnings; +use feature 'say'; +use Getopt::Long; +use Data::Dumper; + + +my $debug=0; +die "Usage: trim-list [--debug] I List\n" + unless GetOptions( "debug"=>\$debug ) && @ARGV>1; + +my $i = shift; + +# all remaining arguments should be single ints, or +# comma-separated lists of ints. +my $all = join(',',@ARGV); +$all =~ s/\s+/,/g; +my @list = split(/,/, $all); + +# now do the trimming, in-place: +@list = grep { $_ >= $i } @list; +say '('.join(',', @list ).')'; |
