Peter Pentchev's solutions to PWC 286
Overview
This directory contains Peter Pentchev's solutions to the two tasks in the Perl & Raku Weekly Challenge 286.
General remarks
Task 1: Self Spammer
Most programming languages have (different or the same) string-processing functions that can:
- read a file and split it into lines at the same time
- split the whole contents read from a file into lines
- split a string by some commonly-accepted set of whitespace characters
The tricky part here is finding the program source. In most of the so-called "interpreted" languages, e.g. Perl, Python, Ruby et al, there is either a file-global variable or a function that one can call to obtain the path to the file containing this particular source code line (note that I put "interpreted" in quotes, since it is common for these languages to do a precompilation phase to some intermediate representation, e.g. byte-code). For compiled languages such as Rust, Go, C, C++, etc., this part may be a bit more difficult since the end result does not generally include the program source code, so this part may have to depend on some specifics of the build environment and only be capable to run directly from a build directory near to its source code.
Task 2: Order Game
There are several approaches to solving this problem:
- trivial: take a pair of numbers, flip a "do we want the smaller or the larger one" flag, produce the corresponding result, move on to the next pair
- keep this information in an object, feed it numbers one by one or in pairs, let it update its inner state (or, if it is an immutable object, let it produce a new one with the inner state updated)
- keep this information in an object, feed it number by number or the whole sequence, have it work as an iterator that produces numbers for the programming language's constructs to consume
Some languages have built-in constructs for dealing with iterators, others can handle immutable objects easily and efficiently, so the approach depends on the programming language.
Running the test suite
The tests/
subdirectory contains a Perl test suite that outputs TAP, so that
it can be run using prove tests
(or, for the more adventurous, prove -v tests
).
The Python implementation has its own set of static checkers (linters).
Those can be run from the python/
directory using Tox 4.x - either directly
(tox run-parallel
) or using the tox-stages tool
(tox-stages run
).
Task 1: Self Spammer
This task does not involve any input data (except for the program source itself), so the programs are executed without any parameters.
The PWCTest::Ch286
module in tests/lib/
defines a test_self_spammer
function that
runs the specified program, checks whether it output a single line containing a single word, and
then looks for that word in the specified source file (the program itself by default).
Of course, for the Perl implementation there is a bit of a chicken-and-egg problem, since
the test itself must implement the same algorithm to check whether the word that
the program output is actually present as a separate word in the source file.
I played around a bit with the idea of using an external grep
program to look for
the word, but it turns out that at least GNU grep has some... idionsyncrasies regarding
character sequences like \<
, \>
, '')
, etc, so the output of Perl's quotemeta
function was not suitable for feeding to grep
directly.
Eh, let's hope my solution is correct anyway :)
The tests/03-python-ch-1.t
test first looks for a suitable Python 3 interpreter.
If the PYTHON3
environment variable is defined, it is used as the name or path of
the Python 3 interpreter; oterhwise, python3
is used.
The test tries to run the Python 3 interpreter for a pass
command; if that fails,
the tests are skipped.
Task 2: Order Game
This task takes some input, so there are two ways of running the program:
- if the
PWC_FROM_STDIN
environment variable is not set to the exact value1
, the program runs the three sequences given as examples, and produces three numbers on its standard output stream, each one on a line by itself. In other words, the program must output1\n0\n2\n
exactly. - if the
PWC_FROM_STDIN
environment variable is set, the program reads a single line of text from its standard input, treats it as a sequence of decimal integers separated by one or more whitespace characters, runs the order game on that sequence, and produces a single number on a line by itself on its standard output stream.
The PWCTest::Ch286
module in tests/lib/
defines a test_order_game_default
function that
runs a program with PWC_FROM_STDIN
unset and expects the exact output, and also
a test_order_game
function that runs a series of tests with different sequences,
each time running the program with PWC_FROM_STDIN
set to 1 and feeding it the sequence.
If the implementation in any language should provide more than one method, then
the program should honor the PWC_METHOD
environment variable.
The value "0" indicates the use of the most natural method for the language,
the value "1" indicates the use of an alternative method, and if there are more than two,
then the values "2", "3", etc, are used to select them.
If PWC_METHOD
is set to a non-numeric value or to a value that is higher than
the index of the last supported methods, it is ignored and the program proceeds as if
PWC_METHOD
was set to "0".
The tests/02-perl-ch-2.t
test runs these functions on the Perl implementation and
produces TAP output suitable for the prove
tool.
The tests/03-python-ch-2.t
test first looks for a suitable Python 3 interpreter.
If the PYTHON3
environment variable is defined, it is used as the name or path of
the Python 3 interpreter; oterhwise, python3
is used.
The test tries to run the Python 3 interpreter for a pass
command; if that fails,
the tests are skipped.
Implementation details
Task 1: Self Spammer
Perl
We use the FindBin core module and its $Bin
and $Script
variables to
figure out where the program source is.
Python
We use Python's built-in __file__
pseudoglobal variable to find the path to our source file.
Then we use random.choice()
to select a random word directly without bothering with
an index into the words array.
Task 2: Order Game
Perl
The Perl solution has three major functions:
round_trivial
- run a single round on a list, producing a list with half the number of elements using the trivial approach: take numbers in pairs, keep a flag, produce the smaller or the larger one as the flag says, flip it.run_order_game
- run the whole order game on the specified sequence of numbers. This function currently only uses a single "run a round" implementation,round_trivial
. ThePWC_METHOD
environment variable is ignored even if set.parse_stdin
- read a line of numbers from the standard input, break it down into a list of integers.
Python
The Python solution defines an OrderIter
class and an OrderIterState
enumeration.
The class serves as an iterator, stashing a value and then performing a min()
or
max()
operation on the next value fetched from the supplied input.
The OrderState
enumeration is used to keep track of the stash/yield state.
Contact
These solutions were written by Peter Pentchev. They are developed in the common PWC-club GitHub repository. This documentation is hosted at Ringlet.