diff options
| author | dcw <d.white@imperial.ac.uk> | 2023-05-15 00:14:02 +0100 |
|---|---|---|
| committer | dcw <d.white@imperial.ac.uk> | 2023-05-15 00:14:02 +0100 |
| commit | b682ef222c56e28ed69b8266fa94c0382539cbcd (patch) | |
| tree | 8bf2d08829da989d94f95fee8b511f4c366be996 | |
| parent | a3e809db1e8a1e2168d9ceaaa2e8a24d25419bdb (diff) | |
| download | perlweeklychallenge-club-b682ef222c56e28ed69b8266fa94c0382539cbcd.tar.gz perlweeklychallenge-club-b682ef222c56e28ed69b8266fa94c0382539cbcd.tar.bz2 perlweeklychallenge-club-b682ef222c56e28ed69b8266fa94c0382539cbcd.zip | |
imported my solutions to this week's challenge - both tasks in Perl, task 1 in C as well - task 2 in C to follow tomorrow
| -rw-r--r-- | challenge-216/duncan-c-white/C/.cbuild | 2 | ||||
| -rw-r--r-- | challenge-216/duncan-c-white/C/Makefile | 16 | ||||
| -rw-r--r-- | challenge-216/duncan-c-white/C/README | 9 | ||||
| -rw-r--r-- | challenge-216/duncan-c-white/C/args.c | 234 | ||||
| -rw-r--r-- | challenge-216/duncan-c-white/C/args.h | 12 | ||||
| -rw-r--r-- | challenge-216/duncan-c-white/C/ch-1.c | 83 | ||||
| -rw-r--r-- | challenge-216/duncan-c-white/C/parseints.c | 114 | ||||
| -rw-r--r-- | challenge-216/duncan-c-white/C/parseints.h | 1 | ||||
| -rw-r--r-- | challenge-216/duncan-c-white/C/printarray.c | 39 | ||||
| -rw-r--r-- | challenge-216/duncan-c-white/C/printarray.h | 1 | ||||
| -rw-r--r-- | challenge-216/duncan-c-white/README | 96 | ||||
| -rwxr-xr-x | challenge-216/duncan-c-white/perl/ch-1.pl | 80 | ||||
| -rwxr-xr-x | challenge-216/duncan-c-white/perl/ch-2.pl | 203 |
13 files changed, 844 insertions, 46 deletions
diff --git a/challenge-216/duncan-c-white/C/.cbuild b/challenge-216/duncan-c-white/C/.cbuild index c168e34403..835981f6f1 100644 --- a/challenge-216/duncan-c-white/C/.cbuild +++ b/challenge-216/duncan-c-white/C/.cbuild @@ -1,5 +1,5 @@ BUILD = ch-1 ch-2 -#BUILD = ch-1 +BUILD = ch-1 CFLAGS = -Wall -g #LDFLAGS = -lm #CFLAGS = -g diff --git a/challenge-216/duncan-c-white/C/Makefile b/challenge-216/duncan-c-white/C/Makefile new file mode 100644 index 0000000000..d193658c72 --- /dev/null +++ b/challenge-216/duncan-c-white/C/Makefile @@ -0,0 +1,16 @@ +# Makefile rules generated by CB +CC = gcc +CFLAGS = -Wall -g +BUILD = ch-1 + +all: $(BUILD) + +clean: + /bin/rm -f $(BUILD) *.o core a.out + +args.o: args.c +ch-1: ch-1.o args.o parseints.o printarray.o +ch-1.o: ch-1.c args.h parseints.h printarray.h +parseints.o: parseints.c args.h parseints.h printarray.h +printarray.o: printarray.c + diff --git a/challenge-216/duncan-c-white/C/README b/challenge-216/duncan-c-white/C/README new file mode 100644 index 0000000000..f73271428e --- /dev/null +++ b/challenge-216/duncan-c-white/C/README @@ -0,0 +1,9 @@ +Thought I'd also have a go at translating ch-1.pl and (shortly) ch-2.pl into C.. + +Both C versions produce very similar (non-debugging and debugging) +output to the Perl originals. + +These C versions use some of my regular support modules: +- my command-line argument processing module args.[ch], +- my csvlist-of-int parsing module parseints.[ch], and +- my int-array printing module printarray.[ch]. diff --git a/challenge-216/duncan-c-white/C/args.c b/challenge-216/duncan-c-white/C/args.c new file mode 100644 index 0000000000..20c21e6c30 --- /dev/null +++ b/challenge-216/duncan-c-white/C/args.c @@ -0,0 +1,234 @@ +#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 isint = check_int( char *val, int *n ); +// Given an string val, check that there's an integer +// in it (after optional whitespace). If there is a valid +// integer value, store that integer value in *n and +// return true; otherwise return false (and don't alter *n). +bool check_int( char *val, int *n ) +{ + // skip whitespace in val + char *p; + for( p=val; isspace(*p); p++ ) + { + /*EMPTY*/ + } + int sign = 1; + if( *p == '+' ) p++; + else if( *p == '-' ) + { + sign = -1; + p++; + } + if( ! isdigit(*p) ) return false; + *n = atoi(p) * sign; + 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-216/duncan-c-white/C/args.h b/challenge-216/duncan-c-white/C/args.h new file mode 100644 index 0000000000..df765fa21e --- /dev/null +++ b/challenge-216/duncan-c-white/C/args.h @@ -0,0 +1,12 @@ +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_int( char * val, int * n ); +extern bool check_unsigned_real( char * val, double * n ); diff --git a/challenge-216/duncan-c-white/C/ch-1.c b/challenge-216/duncan-c-white/C/ch-1.c new file mode 100644 index 0000000000..ba8185dba8 --- /dev/null +++ b/challenge-216/duncan-c-white/C/ch-1.c @@ -0,0 +1,83 @@ +// Task 1: Registration Number +// +// C translation + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "args.h" +#include "parseints.h" +#include "printarray.h" + + +// +// bool containsall = wordcontainsall( word, set ); +// Return true iff word contains every letter in set. +// +bool wordcontainsall( char *word, char *set ) +{ + for( ; *set; set++ ) + { + char letter = *set; + if( isupper(letter) ) letter = tolower(letter); + if( strchr( word, letter ) == NULL ) return false; + } + return true; +} + + +int main( int argc, char **argv ) +{ + int argno = process_flag_n_m_args( "reg-nos", argc, argv, + 2, 1000, "reg wordlist" ); + + char *reg = argv[argno++]; + + if( debug ) + { + printf( "debug: reg: %s, list: ", reg ); + for( int i=argno; i<argc; i++ ) + { + if( i>argno ) putchar( ',' ); + printf( "%s", argv[i] ); + } + putchar( '\n' ); + } + + char regset[argc+1]; + char *d = regset; + for( char *s = reg; *s; s++ ) + { + if( ! isalpha(*s) ) continue; + if( strchr(regset, *s) == NULL ) + { + *d++ = *s; + *d = '\0'; + } + } + + if( debug ) + { + printf( "debug: regset=%s\n", regset ); + //exit(1); + } + + int n = 0; + for( int i=argno; i<argc; i++ ) + { + char *word = argv[i]; + if( wordcontainsall( word, regset ) ) + { + if( n>0 ) putchar( ',' ); + n++; + printf( "%s", word ); + } + } + putchar( '\n' ); + + return 0; +} diff --git a/challenge-216/duncan-c-white/C/parseints.c b/challenge-216/duncan-c-white/C/parseints.c new file mode 100644 index 0000000000..3e820eb334 --- /dev/null +++ b/challenge-216/duncan-c-white/C/parseints.c @@ -0,0 +1,114 @@ +// 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 "printarray.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 != NULL ); + } + #if 0 + if( debug ) + { + printf( "PIA: appending %d to result at " + "pos %d\n", x, p->nel ); + } + #endif + 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_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. +// Note that the list of elements used to be terminated by a -1 value, +// but I've commented this out from now on. +// +int *parse_int_args( int argc, char **argv, int argno, int *nel ) +{ + int *result = malloc( 128 * sizeof(int) ); + assert( result != NULL ); + intlist il = { 0, 128, result }; + + #if 0 + if( debug ) + { + printf( "PIA: parsing ints from args %d..%d\n", argno, argc-1 ); + } + #endif + 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 0 + if( debug ) + { + printf( "PIA: final list is " ); + print_int_array( 80, il.nel, il.list, ',', stdout ); + putchar( '\n' ); + } + #endif + + *nel = il.nel; + return il.list; +} diff --git a/challenge-216/duncan-c-white/C/parseints.h b/challenge-216/duncan-c-white/C/parseints.h new file mode 100644 index 0000000000..da5e145a86 --- /dev/null +++ b/challenge-216/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-216/duncan-c-white/C/printarray.c b/challenge-216/duncan-c-white/C/printarray.c new file mode 100644 index 0000000000..ddee597df3 --- /dev/null +++ b/challenge-216/duncan-c-white/C/printarray.c @@ -0,0 +1,39 @@ +#include <stdio.h> +#include <string.h> + + +// print_int_array( maxw, nelements, results[], sep, outfile ); +// format results[0..nelements-1] as a <sep> separated +// list onto outfile with lines <= maxw chars long. +// produces a whole number of lines of output - without the trailing '\n' +void print_int_array( int maxw, int nel, int *results, char sep, 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( sep, out ); + linelen++; + } + } + //if( linelen>0 ) + //{ + // fputc( '\n', out ); + //} +} diff --git a/challenge-216/duncan-c-white/C/printarray.h b/challenge-216/duncan-c-white/C/printarray.h new file mode 100644 index 0000000000..40efb83277 --- /dev/null +++ b/challenge-216/duncan-c-white/C/printarray.h @@ -0,0 +1 @@ +extern void print_int_array( int maxw, int nel, int * results, char sep, FILE * out ); diff --git a/challenge-216/duncan-c-white/README b/challenge-216/duncan-c-white/README index b035eae705..a64621c29b 100644 --- a/challenge-216/duncan-c-white/README +++ b/challenge-216/duncan-c-white/README @@ -1,76 +1,82 @@ -Task 1: Odd one Out +Task 1: Registration Number -You are given a list of words (alphabetic characters only) of same size. -Write a script to remove all words not sorted alphabetically and print -the number of words in the list that are not alphabetically sorted. +You are given a list of words and a random registration number. +Write a script to find all the words in the given list that has every +letter in the given registration number. Example 1 - Input: @words = ('abc', 'xyz', 'tsu') - Output: 1 + Input: @words = ('abc', 'abcd', 'bcd'), $reg = 'AB1 2CD' + Output: ('abcd') - The words 'abc' and 'xyz' are sorted and can't be removed. - The word 'tsu' is not sorted and hence can be removed. + The only word that matches every alphabets in the given registration number is 'abcd'. Example 2 - Input: @words = ('rat', 'cab', 'dad') - Output: 3 - - None of the words in the given list are sorted. - Therefore all three needs to be removed. + Input: @words = ('job', 'james', 'bjorg'), $reg = '007 JB' + Output: ('job', 'bjorg') Example 3 - Input: @words = ('x', 'y', 'z') - Output: 0 + Input: @words = ('crack', 'road', 'rac'), $reg = 'C7 RA2' + Output: ('crack', 'rac') -MY NOTES: strangely worded, all the stuff about removing words you don't want -and then counting what's left (but not displaying what's left) can be -simplified to: count what you want (the non-sorted words), and that's easy. -But on second thought, let's remove the words anyway, even if we only print -them out in debug mode. +MY NOTES: I think the meaning is: form a set of all letters from the +registration. Select all words that contain every member of the set. +That's easy. GUEST LANGUAGE: As a bonus, I've had a go at translating ch-1.pl into C, look in the C/ directory for that. -Task 2: Number Placement - -You are given a list of numbers having just 0 and 1. You are also given -placement count (>=1). +Task 2: Word Stickers -Write a script to find out if it is possible to replace 0 with 1 in the -given list. The only condition is that you can only replace when there -is no 1 on either side. Print 1 if it is possible otherwise 0. +You are given a list of word stickers and a target word. +Write a script to find out how many word stickers is needed to make up +the given target word. Example 1: - Input: @numbers = (1,0,0,0,1), $count = 1 - Output: 1 + Input: @stickers = ('perl','raku','python'), $word = 'peon' + Output: 2 - You are asked to replace only one 0 as given count is 1. - We can easily replace middle 0 in the list i.e. (1,0,1,0,1). + We just need 2 stickers i.e. 'perl' and 'python'. + 'pe' from 'perl' and + 'on' from 'python' to get the target word. Example 2: - Input: @numbers = (1,0,0,0,1), $count = 2 - Output: 0 + Input: @stickers = ('love','hate','angry'), $word = 'goat' + Output: 3 - You are asked to replace two 0's as given count is 2. - It is impossible to replace two 0's. + We need 3 stickers i.e. 'angry', 'love' and 'hate'. + 'g' from 'angry' + 'o' from 'love' and + 'at' from 'hate' to get the target word. Example 3: - Input: @numbers = (1,0,0,0,0,0,0,0,1), $count = 3 - Output: 1 + Input: @stickers = ('come','nation','delta'), $word = 'accommodation' + Output: 4 -MY NOTES: ok, so it means "can you replace COUNT consecutive -zeros with ones, where neither the preceding or following number is 1". -i.e. where there is either NO preceding or following number (the COUNT -consecutive zeros start or end of the sequence), or a 0 precedes and -follows our COUNT consecutive zeros. -So I guess we just run a start-point along the sequence and check. + We just need 2 stickers of 'come' and one each of 'nation' & 'delta'. + 'a' from 'delta' + 'ccommo' from 2 stickers 'come' + 'd' from the same sticker 'delta' and + 'ation' from 'nation' to get the target word. -GUEST LANGUAGE: As a bonus, I had a go at translating ch-2.pl into C, -look in the C/ directory for that. +Example 4: + + Input: @stickers = ('come','country','delta'), $word = 'accommodation' + Output: 0 + +as there's no "i" in the inputs. + + +MY NOTES: ok, so the only tricky part is that each sticker may be used any +number of times (if any of it's letters remain in the word). It will be +worth checking that every letter in the word is present in one or more stickers +before starting the main search process. + +GUEST LANGUAGE: I will have a go at translating ch-2.pl into C tomorrow; +look in the C/ directory when that's done. diff --git a/challenge-216/duncan-c-white/perl/ch-1.pl b/challenge-216/duncan-c-white/perl/ch-1.pl new file mode 100755 index 0000000000..0962893e57 --- /dev/null +++ b/challenge-216/duncan-c-white/perl/ch-1.pl @@ -0,0 +1,80 @@ +#!/usr/bin/perl +# +# Task 1: Registration Number +# +# You are given a list of words and a random registration number. +# Write a script to find all the words in the given list that has every +# letter in the given registration number. +# +# Example 1 +# +# Input: @words = ('abc', 'abcd', 'bcd'), $reg = 'AB1 2CD' +# Output: ('abcd') +# +# The only word that matches every alphabets in the given registration number is 'abcd'. +# +# Example 2 +# +# Input: @words = ('job', 'james', 'bjorg'), $reg = '007 JB' +# Output: ('job', 'bjorg') +# +# Example 3 +# +# Input: @words = ('crack', 'road', 'rac'), $reg = 'C7 RA2' +# Output: ('crack', 'rac') +# +# MY NOTES: I think the meaning is: form a set of all letters from the +# registration. Select all words that contain every member of the set. +# That's easy. +# +# GUEST LANGUAGE: As a bonus, I've 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: reg-no [--debug] reg wordlist\n" + unless GetOptions( "debug"=>\$debug ) && @ARGV>1; + +my $reg = shift; +my @list = split( /,/, join(',',@ARGV) ); + +say "debug: reg: $reg, list: ", join(',',@list) if $debug; + +# +# my $containsall = wordcontainsall( $word, %set ); +# Return true iff $word contains every letter in %set. +# +sub wordcontainsall +{ + my( $word, %set ) = @_; + foreach my $letter (keys %set) + { + return 0 unless $word =~ /$letter/i; + } + return 1; +} + + +my %regset; +$regset{$_}++ for grep { /[A-Z]/i } split(//,$reg); + +#die Dumper(\%regset); + +my @results; +foreach my $word (@list) +{ + if( wordcontainsall( $word, %regset ) ) + { + push @results, $word; + say "debug: found word $word that contains all reg letters" + if $debug; + } +} + +say join( ',', map { qq('$_') } @results ); diff --git a/challenge-216/duncan-c-white/perl/ch-2.pl b/challenge-216/duncan-c-white/perl/ch-2.pl new file mode 100755 index 0000000000..ef9597efa9 --- /dev/null +++ b/challenge-216/duncan-c-white/perl/ch-2.pl @@ -0,0 +1,203 @@ +#!/usr/bin/perl +# +# Task 2: Word Stickers +# +# You are given a list of word stickers and a target word. +# Write a script to find out how many word stickers is needed to make up +# the given target word. +# +# Example 1: +# +# Input: @stickers = ('perl','raku','python'), $word = 'peon' +# Output: 2 +# +# We just need 2 stickers i.e. 'perl' and 'python'. +# 'pe' from 'perl' and +# 'on' from 'python' to get the target word. +# +# Example 2: +# +# Input: @stickers = ('love','hate','angry'), $word = 'goat' +# Output: 3 +# +# We need 3 stickers i.e. 'angry', 'love' and 'hate'. +# 'g' from 'angry' +# 'o' from 'love' and +# 'at' from 'hate' to get the target word. +# +# Example 3: +# +# Input: @stickers = ('come','nation','delta'), $word = 'accommodation' +# Output: 4 +# +# We just need 2 stickers of 'come' and one each of 'nation' & 'delta'. +# 'a' from 'delta' +# 'ccommo' from 2 stickers 'come' +# 'd' from the same sticker 'delta' and +# 'ation' from 'nation' to get the target word. +# +# Example 4: +# +# Input: @stickers = ('come','country','delta'), $word = 'accommodation' +# Output: 0 +# +# as there's no "i" in the inputs. +# +# MY NOTES: ok, so the only tricky part is that each sticker may be used any +# number of times (if any of it's letters remain in the word). It will be +# worth checking that every letter in the word is present in one or more +# stickers before starting the main search process. +# +# GUEST LANGUAGE: I will have a go at translating ch-2.pl into C tomorrow; +# look in the C/ directory when that's done. +# + +use strict; +use warnings; +use feature 'say'; +use Getopt::Long; +use Data::Dumper; +use Function::Parameters; + +my $debug=0; +die "Usage: word-stickers [--debug] word stickerwordlist\n" + unless GetOptions( "debug"=>\$debug ) && @ARGV > 1; +my $word = lc(shift); +my @sticker = split( /,/, lc(join(',',@ARGV)) ); + +say "debug: word: $word, sticker: ", join(',',@sticker) if $debug; + + +# +# my $ismissing = lettermissing( $letter, @sticker ); +# Return 1 iff $letter is missing from all the stickers in @sticker. +# Return 0 otherwise. +# +fun lettermissing( $letter, @sticker ) +{ + foreach my $s (@sticker) + { + return 0 if $s =~ /$letter/i; + } + return 1; +} + + +# +# my $missing = findmissing( $word, @sticker ); +# Find and return a string of all the letters that are in $word +# but which are missing from all stickers. +# +fun findmissing( $word, @sticker ) +{ + my $missing = ""; + foreach my $letter (split(//,$word)) + { + if( lettermissing($letter,@sticker) ) + { + $missing .= $letter; + say "debug: found missing letter $letter" if $debug; + } + } + return $missing; +} + + +# +# my $lettersincommon = lettersincommon( $word, $sticker ); +# Find and return a string containing all the letters that +# $word has in common with $sticker (using each letter in +# $sticker only once) +# +fun lettersincommon( $word, $sticker ) +{ + my $common = ""; + while( $sticker ) + { + $sticker =~ s/^(.)//; + my $letter = $1; + if( $word =~ s/$letter// ) + { + $common .= $letter; + } + } + return $common; +} + + + +my $minstickers=1000000; +my @minstickers_used; + + +# +# findall( $word, $stickersused, @allsticker ); +# We have already used @$stickersused; +# Find all combinations of all stickers @allsticker that +# contain all letters in $word, and find the best (minimum) +# number of stickers in $minstickers, and stickers themselves +# in @minstickers_used. +# How? well, stickers can be used (or not) whenever it contains +# any letters in common with the word. +# +fun findall( $word, $stickersused, @allsticker ) +{ + if( $word eq '' ) + { + say "found solution, stickersused = ", join(',',@$stickersused) if $debug; + my $nwords = @$stickersused; + if( $nwords < $minstickers ) + { + $minstickers = $nwords; + @minstickers_used = @$stickersused; + say "found new best solution: min stickers ". + "$minstickers, stickersused = ", + join(',',@minstickers_used) if $debug; + } + return; + } + foreach my $sticker (@allsticker) + { + my $common = lettersincommon( $word, $sticker ); + next if $common eq ''; + say "lettersincommon( $word, sticker $sticker ) = $common" if $debug; + + # there are two possibilities: use this sticker or don't; + # try both.. + + # try using the sticker + say "USE sticker $sticker, against $word, letters in common $common" if $debug; + my @newsu = @$stickersused; + push @newsu, $sticker; + my $newword = $word; + $newword =~ s/$_// for split(//,$common); + say " - new word is <$newword>" if $debug; + findall( $newword, \@newsu, @allsticker ); + + # or try without the sticker + } +} + + +my $missingletters = findmissing( $word, @sticker ); +if( $missingletters ne '' ) +{ + say "0"; + if( $debug ) + { + say "debug: There are no solutions as letters $missingletters ". + "are missing from all stickers"; + } + exit(0); +} + +$minstickers=1000000; +@minstickers_used = (); + +findall( $word, [], @sticker ); + +say $minstickers; +if( $debug ) +{ + say "debug: stickers used: ", join(',',@minstickers_used); +} |
