diff options
| author | Jörg Sommrey <28217714+jo-37@users.noreply.github.com> | 2023-04-14 09:23:55 +0200 |
|---|---|---|
| committer | Jörg Sommrey <28217714+jo-37@users.noreply.github.com> | 2023-04-19 18:48:49 +0200 |
| commit | be29b0279e69aa1e5343a5da7a3676ba02e61567 (patch) | |
| tree | f893743615355bf140db4ee4da97d5c045986c8c /challenge-053 | |
| parent | 0c582246b5be6d8005c2a7638531964abc729263 (diff) | |
| download | perlweeklychallenge-club-be29b0279e69aa1e5343a5da7a3676ba02e61567.tar.gz perlweeklychallenge-club-be29b0279e69aa1e5343a5da7a3676ba02e61567.tar.bz2 perlweeklychallenge-club-be29b0279e69aa1e5343a5da7a3676ba02e61567.zip | |
Challenge 053 task 2
Diffstat (limited to 'challenge-053')
| -rwxr-xr-x | challenge-053/jo-37/perl/ch-2.pl | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/challenge-053/jo-37/perl/ch-2.pl b/challenge-053/jo-37/perl/ch-2.pl new file mode 100755 index 0000000000..1aba9c29b6 --- /dev/null +++ b/challenge-053/jo-37/perl/ch-2.pl @@ -0,0 +1,148 @@ +#!/usr/bin/perl -s + +use v5.24; +use Test2::V0; +use experimental qw(signatures); + +our ($tests, $examples); + +run_tests() if $tests || $examples; # does not return + +die <<EOS unless @ARGV; +usage: $0 [-examples] [-tests] [N] + +-examples + run the examples from the challenge + +-tests + run some tests + +N + print vowel strings of length N + +EOS + + +### Input and Output + +# Create N counters, chain them, initialize the first counter with all +# vowels and step the last counter while there are values. +main: { + my @val; + my @counter; + my $n = shift; + for (1 .. $n) { + push @val, undef; + push @counter, Counter->new(\$val[-1]); + if (@counter > 1) { + $counter[-1]->parent($counter[-2]); + $counter[-2]->child($counter[-1]); + } + } + $counter[0]->set([qw(a e i o u)]); + while ($counter[0]) { + say @val; + $counter[-1]->next; + } +} + + +### Implementation + +# Using "Counter" objects to solve this task. +# +# A counter has: +# - a reference to an (external) scalar that is updated on every state +# change. +# - an array of possible values +# - a "next" method that steps to the next value +# - a "set" method that initializes the array of possible values. +# Counters are chained as parent / child. On every state change, a +# child counter is initialized with the new possible values. If the +# values are exhausted, the parent's "next" method is called. + +package Counter; + +our %successors; + +BEGIN { + %successors = ( + a => [qw(e i)], + e => ['i'], + i => [qw(a e o u)], + o => [qw(a u)], + u => [qw(o e)] + ); +} +use overload + bool => sub ($self, @) {!!$self->{vals}->@*}; + +sub new ($class, $ref) { + bless {ref => $ref}, $class; +} + +sub parent ($self, $parent) { + $self->{parent} = $parent; +} + +sub child ($self, $child) { + $self->{child} = $child; +} + +sub set ($self, $vals) { + $self->{vals} = $vals; + $self->{cur} = 0; + _upd($self); +} + +sub _upd ($self) { + my $val = $self->{vals}[$self->{cur}]; + $self->{ref}->$* = defined $val ? $val : ''; + $self->{child}->set($successors{$val}) + if defined $val && defined $self->{child}; +} + +sub next ($self) { + if ($self->{cur}++ < $self->{vals}->$#*) { + _upd($self); + } else { + $self->{vals} = []; + $self->{cur} = -1; + _upd($self); + $self->{parent}->next if defined $self->{parent}; + } +} + + +### Examples and tests + +package main; + +sub run_tests { + SKIP: { + skip "examples" unless $examples; + + my @vstrings; + my @tmp; + my $counter0 = Counter->new(\$tmp[0]); + my $counter1 = Counter->new(\$tmp[1]); + $counter0->child($counter1); + $counter1->parent($counter0); + $counter0->set([qw(a e i o u)]); + while ($counter0) { + push @vstrings, join '', @tmp; + $counter1->next; + } + is [@vstrings], bag {item 'ae'; item 'ai'; item 'ei'; + item 'ia'; item 'io'; item 'iu'; item 'ie'; item 'oa'; + item 'ou'; item 'uo'; item 'ue'; end;}, + 'example'; + } + + SKIP: { + skip "tests" unless $tests; + } + + done_testing; + exit; +} |
