Published by /u/briandfoy on Saturday 21 December 2024 06:00
Published by /u/briandfoy on Saturday 21 December 2024 05:57
Published by /u/petdance on Saturday 21 December 2024 04:55
grep-like code search tool ack has been updated to v3.8.0. It's available on CPAN as App::Ack, or at https://beyondgrep.com/
The big new feature is that you can have boolean matches on the line.
bash ack this --and that --and other and this --or that --or other ack this --not that --not other
Published by oodler on Saturday 21 December 2024 00:00
It was Christmas Eve, and Santa was facing a nightmare. The sleigh’s new GPS system, upgraded for the first time in centuries from following the Star of Bethlehem, was malfunctioning. As Santa checked the screen, the map was a chaotic mess — locations were wrong, some coordinates did not make sense, and the sleigh was heading straight into the mountains instead of over the City of New Orleans!
Santa's CURRENT position read-out indicated he was over the Appalachians at the following coordinates:
Sleigh Latitude Sleigh Longitude Sleigh Altitude (ft)
38.0000° N -81.500° W 300
When he should be nearly exactly at,
Sleigh Latitude Sleigh Longitude Sleigh Altitude (ft)
29.9500° N -90.070° W 300
"We can't afford this!" Santa shouted. "We’ve got billions of presents to deliver, and the system's down. And I want some gumbo!"
"Santa, we can fix it," Jingles, the lead elf, said with a frantic smile. "We just need more computing power. If we use OpenMP, we can parallelize the calculations and solve this quickly."
Santa stared. “What are you talking about, Jingles? I'm a giant elf, not a Scottish engineer on a television show for nerds!”
“We’ll solve the GPS problem by recalculating the distances to each delivery location. The sleigh’s GPS relies on triangulating data from multiple satellites. Right now, it's too slow to process the data for each location one by one. We can use OpenMP to divide the problem into smaller parts, calculate distances in parallel, and get this fixed fast!”
The data format of the positional satellites consisted of their positions in latitude, longitude, with an altitude; so the computations are necessarily in 3D space, and could contain any number of lines:
The distance formula was computationally expensive, involving square roots and trigonometry. But by using OpenMP, the task could be split into multiple OS threads, each calculating the distance to one satellite at the same time. The results would then be aggregated, and the sleigh's GPS would know exactly where to direct its heading.
The code would parallelize the work by dividing the list of satellites among a number of pthreads using OpenMP's #pragma omp for
construct! OpenMP::Simple
handles the required #include <omp.h>
and makes it easy to query the environment for information, such as OMP_NUM_THREADS
.
Jingles quickly typed up a solution in Perl, his favorite programming language. The solution was simple but critically offloaded the triangulation computations to Inline::C
code containing OpenMP directives, using the OpenMP
module on CPAN.
use strict;
use warnings;
use OpenMP;
use Inline (
C => 'DATA',
with => qw/OpenMP::Simple/,
);
my $omp = OpenMP->new;
$omp->env->omp_num_threads($ENV{OMP_NUM_THREADS} // 8); # accept number of threads via commandline
# Sleigh's CURRENT position (latitude, longitude, altitude)
my $sleigh_lat = 38.0000;
my $sleigh_lon = -81.500;
my $sleigh_alt = 300.0; # in meters
# Satellite positions in tab-delimited format
my @satellites = ();
open my $FH, "<", "./satellite-data.txt" || die $!;
foreach my $line (<$FH>) {
chomp $line;
push @satellites, [split(/[\s\t]+/, $line)];
}
# Function to calculate distance from sleigh to each satellite using OpenMP::Simple,
# a subclass of Inline::C!
my $distances = calculate_distances($sleigh_lat, $sleigh_lon, $sleigh_alt, \@satellites);
# Print the calculated distances
foreach my $distance (@$distances) {
print "Distance: $distance meters\n";
}
Santa watched as Jingles split up the computational load. Each satellite’s data was handled in parallel by different threads, and within seconds, the recalculations were done. The GPS latency issues were fixed. Jingles already had some thoughts of improvements he could make using OpenMP's ability to target GPUs directly, but was quite happy with the current resolution.
Just as the final calculations finished, a gentle voice spoke, “When Faith sanctifies morally neutral technology, Good things are possible at Internet scale.”
Santa turned to see the Holy Family - Baby Jesus in the arms of His Virgin Mother accompanied by His foster father, Joseph; by the sleigh, smiling serenely. “Thank you,” Santa whispered. “Merry Christmas.”
With the sleigh back on track, Santa soared into the night sky.
When things settled down, Santa was able to look at Jingles' full code, and it was something to behold!
use strict;
use warnings;
use OpenMP;
use Inline (
C => 'DATA',
with => qw/OpenMP::Simple/,
);
my $omp = OpenMP->new;
$omp->env->omp_num_threads($ARGV[0] // 8); # accept number of threads via commandline
# Sleigh's CURRENT position (latitude, longitude, altitude)
my $sleigh_lat = 38.0000;
my $sleigh_lon = -81.500;
my $sleigh_alt = 300.0; # in meters
# Satellite positions in tab-delimited format
my @satellites = ();
open my $FH, "<", "./satellite-data.txt" || die $!;
foreach my $line (<$FH>) {
chomp $line;
push @satellites, [split(/[\s\t]+/, $line)];
}
# Function to calculate distance from sleigh to each satellite using Inline::C
my $distances = calculate_distances($sleigh_lat, $sleigh_lon, $sleigh_alt, \@satellites);
# Print the calculated distances
foreach my $distance (@$distances) {
print "Distance: $distance meters\n";
}
__DATA__
__C__
#include <math.h>
// Function to convert geographic coordinates to Cartesian (x, y, z)
void geo_to_cartesian(double lat, double lon, double alt, double *x, double *y, double *z) {
double R = 6371000; // Earth's radius in meters
double lat_rad = lat * M_PI / 180.0;
double lon_rad = lon * M_PI / 180.0;
*x = (R + alt) * cos(lat_rad) * cos(lon_rad);
*y = (R + alt) * cos(lat_rad) * sin(lon_rad);
*z = (R + alt) * sin(lat_rad);
}
// Function to calculate Euclidean distance between two points (x1, y1, z1) and (x2, y2, z2)
double calculate_distance(double x1, double y1, double z1, double x2, double y2, double z2) {
return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2) + pow(z2 - z1, 2));
}
/* C function parallelized with OpenMP */
// Main function to calculate the distances from the sleigh to each satellite
AV* calculate_distances(double sleigh_lat, double sleigh_lon, double sleigh_alt, AV *satellites) {
int satellite_count = av_len(satellites) + 1; // The number of satellites
AV* distances = newAV(); // Create a new Perl array (AV) to hold the distances
double sleigh_x, sleigh_y, sleigh_z;
// Convert sleigh's geographic coordinates to Cartesian coordinates
geo_to_cartesian(sleigh_lat, sleigh_lon, sleigh_alt, &sleigh_x, &sleigh_y, &sleigh_z);
// Fetch satellite data into local arrays
double *sat_latitudes = malloc(satellite_count * sizeof(double));
double *sat_longitudes = malloc(satellite_count * sizeof(double));
double *sat_altitudes = malloc(satellite_count * sizeof(double));
// Populate satellite data into local arrays (from the Perl array)
for (int i = 0; i < satellite_count; i++) {
AV *satellite = (AV*) SvRV(*av_fetch(satellites, i, 0)); // Fetch the satellite at index i
sat_latitudes[i] = ((double) SvNV(*av_fetch(satellite, 0, 0))); // Latitude
sat_longitudes[i] = ((double) SvNV(*av_fetch(satellite, 1, 0))); // Longitude
sat_altitudes[i] = ((double) SvNV(*av_fetch(satellite, 2, 0))); // Altitude
}
// Declare a temporary array to hold distances for each thread
double *_distances = malloc(satellite_count * sizeof(double));
PerlOMP_GETENV_BASIC // read common environmental variables, provided by OpenMP::Simple
// Start parallel region
#pragma omp parallel shared(_distances)
{
// Parallel for loop to compute distances in parallel
#pragma omp for
for (int i = 0; i < satellite_count; i++) {
_distances[i] = 0.0; // Initialize
double sat_x, sat_y, sat_z;
// Convert satellite's geographic coordinates to Cartesian coordinates
geo_to_cartesian(sat_latitudes[i], sat_longitudes[i], sat_altitudes[i], &sat_x, &sat_y, &sat_z);
// Calculate the distance from the sleigh to this satellite
_distances[i] = calculate_distance(sleigh_x, sleigh_y, sleigh_z, sat_x, sat_y, sat_z);
}
}
// Combine the results from all threads into the main distances array inside the parallel region
for (int i = 0; i < satellite_count; i++) {
av_push(distances, newSVnv(_distances[i]));
}
// Free the private distance arrays and satellite data arrays
free(_distances);
free(sat_latitudes);
free(sat_longitudes);
free(sat_altitudes);
// Return the AV containing the distances to Perl
return distances;
}
__END__
See More:
Published on Saturday 21 December 2024 00:00
If you are manipulating images, you need PDL!
For years, I've had this urge to use PDL to take a bitmap image and trace the outlines to make an SVG file that I could scale up to A0 poster size without the resulting pixelation. Yes, there are already tools that do that, but where's the fun in that? It's Christmas, the time for Fun and Games!
Published by Max Maischein on Friday 20 December 2024 21:22
We are happy to announce that CosmoShop supports the German Perl/Raku-Workshop.
CosmoShop is the largest pure Perl based shop system.
Since 1997, we have been implementing sophisticated and individual eCommerce projects in the B2B sector with our specially developed store software. We are the central point of contact for the entire spectrum.
CosmoShop ist das weltweit größte rein perlbasierte Shopsystem.
Mit unserer eigens entwickelten Shopsoftware realisieren wir seit 1997 anspruchsvolle und individuelle eCommerce Projekte im B2B Umfeld. Dabei sind wir zentraler Ansprechpartner des gesamten Spektrums.
Published by The Perl and Raku Conference - Greenville, SC 2025 on Friday 20 December 2024 16:58
Published by amphibole on Friday 20 December 2024 16:25
I am trying to install the CPAN module DBD::SQLite and I am getting a strange failure. The compilation of several C files into object code goes fine, and then when we hit sqlite3.c the compilation just hangs. The abbreviated command is:
i686-linux-gnu-gcc -c [...] sqlite3.c
I'd like confirmation if anyone else could try it; maybe someone using perlbrew?
Here is some version information:
Published on Friday 20 December 2024 15:09
Update Module::CoreList for 5.41.8
Published on Friday 20 December 2024 15:06
Bump the perl version in various places for 5.41.8
New perldelta for 5.41.7
Published on Friday 20 December 2024 14:46
Perl 5.41.7 was released, update release schedule
Update epigraphs.pod for 5.41.7
Published by user12090504 on Friday 20 December 2024 11:56
I have a statement in Perl like
if (($hh, $mm) = ($info{$item}{$p_expectedtime} =~ /(\d+):(\d+)/))
but when compiling, I get the message:
Use of uninitialized value in pattern match (m//)
Tried to add a define()
like
if (define($hh, $mm) && ($hh, $mm) = ($info{$item}{$p_expectedtime} =~ /(\d+):(\d+)/))
which is incorrect.
Published by /u/davorg on Friday 20 December 2024 09:34
We are delighted to announce the new release, which includes 57 significant bug fixes compared to the previous 2.1.8 version. This update addresses a range of important issues and enhances the overall stability and performance.
More details at https://blog.foswiki.org/Blog/Foswiki219IsReleased
Published by /u/briandfoy on Friday 20 December 2024 03:45
Published by Brett Estrade on Friday 20 December 2024 03:26
Next PCC: July 3-4, 2025 in Austin, Texas USA
See entire the post to learn about our future plans, in perpetuity.
The very first Perl Community Conference was a tremendous success thanks to everyone of you authors and speakers. Many thanks to PCC Co-Organizer Will "The Chill" Braswell, our friends at the Diogenes Hackerspace (in Austin, Texas), and all the participants both online and in person! We'll be following up soon about posting the videos. The next stage will be editing and publishing Issue #2 of the Science Perl Journal. The schedule from the Winter'24 PCC should be a clue about some of its contents. We have discussed offering a "Letters to the Editor" section to address feedback from friends and foes alike. More on this will be announced in future posts.
Future Plans in Perpetuity
Please mark your calendars for both July 3rd/4th (USA's birthday) and December 17th/18th (Perl's birthday), starting in 2025 and going forward every year from now on. Our current plan is to continue meeting in Austin until further notice, as it is a central location and we already have all of the conference equipment there.
As you can see from the dates above, we are expanding to a 2-day format starting with our upcoming PCC Summer 2025, which will begin on July 3rd and run through July 4th, finishing in time for people to get home for fireworks if they so choose. Likewise, we will start our PCC Winter 2025 one day early on December 17th and run through December 18th, giving people enough time to get home for their Christmas holiday travel plans etc.
The 2-day format will give us an opportunity to have more talks, as well as more extra-curricular activities. We want to offer things like Perl job fairs, Perl code hackathons, Perl training courses, and also official group activities on the evenings of July 3rd and December 17th, such as a lake cruise or live theater or dinner-and-a-show etc.
So, please set a permanent annual reminder in your calendar now, repeating every July 3-4 and Dec 17-18 - forever. It is safe to assume the location will be Austin, Tx indefinitely. Together we will put Perl back on top!
Sincerely, Brett Estrade (OODLER) Chairman, Science Perl Committee Co-Editor, Science Perl Journal Co-Organizer, Perl Community Conference
Published by Marc Perry on Friday 20 December 2024 00:00
Christmas eve was quickly approaching, and Santa was trying to decide whether to purchase masks for all of the reindeer again this year (he was aware that there were documented cases of SARS-CoV-2 infecting White-Tail Deer (O.virginianaus), and he knew that Reindeer (R.tarandus) were closely related), when the Chief Elf interrupted his train of thought to inform him that two of the elves in workshop were in the infirmary with respiratory distress. Testing had revealed that they were both infected with Respiratory Syncitial Virus (RSV), specifically with the RSVA subtype.
Santa asked "Do we know where they were exposed, or if one of them transmitted it to the other? We may need to require everyone at the North Pole to mask again until after Christmas eve . . . "
"The diagnostic lab says the two viruses are similar but not identical. Also they are not confident of the lineage calls and the clade assignments because we are using a new set of PCR primers designed in Edinburgh, which are based on a newer RSVA reference sequence (RefSeq). The problem is that we are using the RSVA phylogenetic tree available at usher.bio and that tree was built using the NCBI RefSeq as the root. You can see where the problem arises . . . "
Santa thought for a moment and said "Oh, blast! We aligned our fastq reads to the other RefSeq, and at the primer trimming step we are using the genomic coordinates from that different virus strain. So if we first align against the NCBI RefSeq genome (to get more accurate lineage calls and clade assignments) then the primer trimming step will not work as intended. Huh! Hmmm, do you think we could align those two different RSVA RefSeqs to each other and then modify the coordinates in the primer bed file so it uses the NCBI RefSeq's genomic coordinates instead?!?"
The Chief Elf sketched this out on the iceboard and said, "I think this could work. I will ask our bioinformatic software developers to tackle the problem."
And so gentle reader that is how we arrived at a simple, but tedious task of converting the genomic coordinates in this bed file:
RS20000581 44 66 RSVA_1_LEFT 1 +
RS20000581 434 464 RSVA_1_RIGHT 1 -
RS20000581 359 385 RSVA_2_LEFT 2 +
RS20000581 749 773 RSVA_2_RIGHT 2 -
RS20000581 669 699 RSVA_3_LEFT 1 +
RS20000581 1057 1083 RSVA_3_RIGHT 1 -
RS20000581 990 1016 RSVA_4_LEFT 2 +
RS20000581 1366 1389 RSVA_4_RIGHT 2 -
So that it would contain the correct coordinates from a different RSVA subtype RefSeq. It quickly became apparent that this problem could be solved much more quickly using the Awesome Power of Perl, unfortunately there was no out-of-the-box solution available in the existing base of Perl code. But how to proceed?
Each of the two genome files are simple text files consisting of an alphabet of five letters (A, C, G, and T (occasionally an N)) (technically RSV, similar to SARS-CoV-2 is an RNA virus, but life is simpler if we use 'T' instead of 'U'), all we need is a so-called "Global Alignment" of the two full-length sequences (they are each just over 15,000 bases in length). What we really want to know is where all of the longest stretches of identical sequences are located in the viral chromosomes. Fortunately an algorithm to calculate this was published by Needleman and Wunsch back in 1970; it is considered a classic example of dynamic programming (which builds up the answer without consuming vast amounts of memory). Furthermore, the National Center of Biotechnology Information (NCBI) maintains a server we can use to upload any two closely related nucleotide sequences.
The output from the N-W Global Alignment looks like this:
Query 2177 GTGTGATTAACTACAGTGTATTAGATTTGACAGCAGAAGAACTAGAGGCTATCAAACATC 2236
|||||||||||||||||||| |||| ||||||||||||||||||||||||||||||||||
Sbjct 2221 GTGTGATTAACTACAGTGTACTAGACTTGACAGCAGAAGAACTAGAGGCTATCAAACATC 2280
Query 2237 AGCTTAATCCAAAAGATAATGATGTAGAGCTTTGAGTTAATAAAAAGGTGGGGCAAATAA 2296
|||||||||||||||||||||||||||||||||||||||||||||| ||||||||||||
Sbjct 2281 AGCTTAATCCAAAAGATAATGATGTAGAGCTTTGAGTTAATAAAAAA-TGGGGCAAATAA 2339
Where the the top sequence in each pair is a stretch of 60 bases from the Edinburgh RefSeq (Labeled 'Query') and the bottom sequence in each pair is the matching stretch from the NCBI RefSeq (Labeled 'Sbjct'). The unix pipe symbols denote a perfect match between the sequences at that base.
Notice that the numbering systems are slightly offset, AND that there is a gap introduced with a hyphen in-between the A at 2327, and the T at 2328 in the Sbjct, directly across from the G at position 2384 in the Query. That can be interpreted as a single nucleotide insertion in the Query genome. So creating the mapping table between the two coordinate systems is not trivial. The sequences only share 94 percent identity over about 15,200 nucleotides, which means they diverge 6 percent of the time.
To simplify the construction of our lookup table we can use grep to filter the starting alignment into two different subsets,
grep ^Query nw_alignment.txt > just_rows_from_edinburgh_refseq.txt
grep ^Sbjct nw_alignment.txt > just_rows_from_ncbi_refseq.txt
N.B. you will have to go in and manually remove the first extraneous Query line from the first file. These become the first two input files to our script, the third file is the primer bed file we are starting with.
#!/usr/bin/env perl
use strict;
use warnings;
my $query_input = $ARGV[0];
my $subject_input = $ARGV[1];
my $input_bed = $ARGV[2];
open my ($FH1), '<', $query_input or die "Could not open $query_input for reading\n";
open my ($FH2), '<', $subject_input or die "Could not open $subject_input for reading\n";
my @global_array = ();
$global_array[0] = { query => undef,
sbjct => undef,
};
# This global counter serves as the index for the first data structure
# that captures the information at each place in the aligned sequences
my $counter = 0;
# Parse the rows of the first file
while ( <$FH1> ) {
chomp;
# Each of these filtered rows has the same structure, in addition to
# A, C, G, and T, the N-W output can also contain hyphens (dashes), so
# our regex has to include that in the string we are capturing
my ($seq) = $_ =~ m/Query\s+\d+\s+([\w\-]+)\s+\d+/;
# Parse the 60 nucleotide string into individual characters
my @qchars = split(//, $seq);
foreach my $q ( @qchars ) {
$counter++;
# Load up the array of hashrefs with the symbols
$global_array[$counter]{query} = $q;
}
}
close $FH1;
$counter = 0;
while ( <$FH2> ) {
chomp;
my ($seq) = $_ =~ m/Sbjct\s+\d+\s+([\w\-]+)\s+\d+/;
my @schars = split(//, $seq);
foreach my $s ( @schars ) {
$counter++;
$global_array[$counter]{sbjct} = $s;
}
}
close $FH2;
# Now we process the information in each element of the
# @global_array, and store that in this global hash
# the keys of this hash are genomic coordinates for the
# Edinburgh RSV-A RefSeq and the values are the genomic
# coordinates of the NCBI RSV-A RefSeq
my %ncbi_coordinates_of = ();
my $qcounter = 0;
my $scounter = 0;
# There is nothing useful in the [0] element
foreach my $i ( 1.. ) {
my $key = undef;
my $value = undef;
# If the string from the N-W output contains a '-' then we
# need to handle those differently. We only increment the
# $qcounter when the value in 'query' matches a letter
if ( $global_array[$i]{query} =~ m/\w/ ) {
$qcounter++;
$key = $qcounter;
if ( $global_array[$i]{sbjct} =~ m/\w/ ) {
$scounter++;
$value = $scounter;
}
$ncbi_coordinates_of{$key} = $value;
}
else {
$scounter++;
next;
}
}
# After all of that manipulation and data wrangling, the problem now becomes
# very simple. We read in the starting primer bed file and iterate over each
# TSV record. We use the information in columns 2 and 3 to query the
# %ncbi_coordinates_of hash, and insert the coordinates we want from the hash
# values. The modified record is immediately printed to STDOUT
open my ($FH3), '<', $input_bed or die "Could not open $input_bed for reading\n";
while ( <$FH3> ) {
my @fields = split(/\t/, $_);
# Change the sequence name in column 1
$fields[0] = 'NC_038235.1';
# It is formally possible that either the 'start' or the 'end' of the
# Query sequence is NOT in the %ncbi_coordinates_of hash, so check for this
unless ( exists $ncbi_coordinates_of{$fields[1]} and exists $ncbi_coordinates_of{$fields[2]} ) {
$fields[1] = $fields[2] = 'undef';
print join("\t", @fields);
next;
}
# Sometimes the 'start' and 'end' coordinates from the Query sequence ARE keys in the
# %ncbi_coordinates_of hash, but the corresponding values from the Sbjct sequence are undef
# so continue processing gracefully when this occurs
$fields[1] = $ncbi_coordinates_of{$fields[1]} //= 'undef';
$fields[2] = $ncbi_coordinates_of{$fields[2]} //= 'undef';
print join("\t", @fields);
}
close $FH3;
exit;
When you run the script, save the output to file, and then use grep undef
to see if there were any coordinates which could NOT be successfully mapped. If there are any then you can use grep -v undef
to save a copy without those lines.
What did we learn? After running this script to create the new primer bed file for the NCBI RSV-A RefSeq, and then rerunning their viral sequencing pipeline to align the amplified reads to the NCBI RefSeq, the bioinformaticians working with the infirmary could see that the two isolates from the workshop elves were more closely related than they had previously thought, but since the viruses still had many mutations separating them, it seemed like these were two separate exposures (or introductions) of RSV-A to the North Pole compound.
You can see that for yourself in this image:
N.B.: In accordance with the laws of Canada, Denmark, and Russia, the clinical metadata and any other personal health information (PHI) for these samples has been de-identified.
These are two versions of the RSV-A Global phylogenentic tree, showing a subtree of the samples that the most highly related to the two sequences from Santa's workshop. The trees are shown rotated 90 degrees, so the "root" of the tree (which is not shown), is on the LEFT. You can see the "branches" as horizontal lines; and at the tips of each branch is a "leaf" (or a node). The leaves represent the sequences from different individual viruses in the database. Each tip is labeled with the name of the sample, and the two samples from the North Pole are colored black. The Y-axis has no units, and is arbitrary. The data depicting similarity is contained in the pattern of the branches (which are technically internal nodes), and in the LENGTH of the branches. The X-axis conveys information about the number of mutations in one sample, which is the number of nucleotide sequence changes that it takes to "get back" to the root of the tree.
Panel A. shows where the UShER program placed the two samples from the North Pole, using the Edinburgh RefSeq genome, whereas Panel B. shows where they were placed after the same samples were aligned to the NCBI RefSeq genome, and the new Primer bed file created with our script was used to remove (or trim) the primers from the end of the reads. You can see that the placement of the Elf_0001 sample is almost the same in the two trees, whereas the placement of the Elf_0002 sample has moved. It is now forking off at a different branch of the tree and is closer (and therefore more similar, or more related) to Elf_0001.
In principal, the flow and the steps used here could work with any paired RefSeqs for a pathogenic virus where you have created DNA sequencing amplicons with PCR primers from one coordinate system, but you want to be able to use your regular pipelines to align the fastq reads to a different RefSeq.
If you have some paired-end fastq files from RSV (or another RNA virus) you can get an idea of the processing steps to use to generate consensus.fasta files here.
During the COVID19 Global Pandemic these consensus.fasta files were essential for constructing the phylogenetic trees used by Epidemiologists to identify outbreaks of new strains.
THM: Best to wear masks at the North Pole until after DEC-24.
Published on Friday 20 December 2024 00:00
PDL is powerful and has many features. But as with any complex software system, bugs can happen. Let's learn about two!
Tags: internals implementation dataflow
Published by Mayur Koshti on Thursday 19 December 2024 17:33
Published by Makoto Nozaki on Thursday 19 December 2024 17:30
We are pleased to announce Olaf Alders as the White Camel Award 2024 recipient.
Most likely people recognize Olaf as the guy behind MetaCPAN. He co-founded the project in late 2010 as a means to aggregate data for an iOS application. The phone app fizzled, but MetaCPAN took on a life of its own. Olaf has been part of the team working on it ever since. MetaCPAN is a user-friendly search and discovery platform for Perl modules and distributions, based on CPAN (Comprehensive Perl Archive Network).
Olaf is also the person responsible for perl.com’s revival. After the site became inactive for several years, Olaf brought the site back to life. He’s always looking for new contributions. Anyone can submit an article via GitHub.
In 2023, Olaf also took over the Perl Advent Calendar, a long-running tradition previously managed by Mark Fowler, whom we would like to acknowledge for his 20+ years of dedication.
When Olaf is not working on websites, he is spending time with his family and his dog. He enjoys open-water swimming, biking, and running. He is in the pool regularly with his local Masters Swim Club. He is currently recording guitar parts for a post-punk song cycle about Ragnarök.
In his technical work, Olaf also helps to maintain the modules in the libwww-perl organization. He is the author of “perlimports” and several other modules. Olaf is a regular attendee of the Perl Toolchain Summit.
For the award details, see https://whitecamel.org/.
Published by AskQuestionsP on Thursday 19 December 2024 17:18
Below is the code which does the processing of lines written in new.txt
file.
my $bankNameTest;
my $bankName;
my @lines;
my $document = do {
local $/ = undef;
open my $fh, "<", "new.txt"
or die "could not open $file: $!";
<$fh>;
};
chomp ($document);
print "$document is doc\n";
@lines = split (/\n/,$document);
foreach my $test (@lines) {
$bankNameTest=glob ("${test}/wint_nightly_nfarm_*.main.20241216.4262507") ;
chomp ($bankNameTest);
print "$bankNameTest is testbankname\n";
$bankName=`ls -d $bankNameTest | sed -r "s?${var}/??g" | sed -r "s/wint_nightly_nfarm_//g" | sed -r "s/.main.20241216.4262507//g"`;
chomp ($bankName);
print "$bankName is the bankName\n";
}
Content of new.txt
is :
_qa/SERVFARM/CAPTURE_DB/CaptureViewer/2487964_brd_launch/cap_allegro_view
_qa/SERVFARM/CAPTURE_DB/CaptureViewer/FIND/enh_find/DRC_MARKERS/hierdsn_incomp_entry
_qa/SERVFARM/CAPTURE_DB/CaptureViewer/crossprobe/03_between_cap_allegroviewer/dsn_schm_page
Output coming is:
_qa/SERVFARM/CAPTURE_DB/CaptureViewer/2487964_brd_launch/cap_allegro_view/wint_nightly_nfarm_capture_viewer.main.20241216.4262507 is testbankname
capture_viewer is the bankName
is testbankname
is the bankName
_qa/SERVFARM/CAPTURE_DB/CaptureViewer/crossprobe/03_between_cap_allegroviewer/dsn_schm_page_and_part_name_with_special_chars/wint_nightly_nfarm_capture_viewer.main.20241216.4262507 is testbankname
capture_viewer is the bankName
Here, 2nd line of new.txt is not getting processed correctly.
Expected Output:
_qa/SERVFARM/CAPTURE_DB/CaptureViewer/2487964_brd_launch/cap_allegro_view/wint_nightly_nfarm_capture_viewer.main.20241216.4262507 is testbankname
capture_viewer is the bankName
_qa/SERVFARM/CAPTURE_DB/CaptureViewer/FIND/enh_find/DRC_MARKERS/hierdsn_incomp_entry/wint_nightly_nfarm_capture_viewer.main.20241216.4262507 is testbankname
capture_viewer is the bankName
_qa/SERVFARM/CAPTURE_DB/CaptureViewer/crossprobe/03_between_cap_allegroviewer/dsn_schm_page_and_part_name_with_special_chars/wint_nightly_nfarm_capture_viewer.main.20241216.4262507 is testbankname
capture_viewer is the bankName
Not sure what's the issue? Seems to be some character related issue in new.txt. Need to have the output as mentioned in "Expected Output".
Published by Chris on Thursday 19 December 2024 16:54
I've noticed that my Catalyst app doesn't seem to be handling non-standard characters correctly - I first noticed this when the RTF text editor app I was using replaced " characters with the “angled” kind at the start / end of a string. I've agonised over this, and have thrown up a new blank Catalyst app with the following index page:
sub index :Path :Args(0) {
my ( $self, $c ) = @_;
# Hello World
#$c->response->body( $c->welcome_message );
$c->response->body("<h1>Hello World, where's the nearest café</h1>");
$c->log->debug(sprintf("encoding: %s", $c->encoding->mime_name));
}
This gives the following in my browser:
However my Catalyst is version 5.90131, which should be encoding UTF-8 by default, in fact the debug message in the routine gives me: [debug] encoding: UTF-8
Browser shows UTF-8 too in the headers:
Started pulling my hair out a bit - I'm sure I'm missing something totally obvious, please could someone point me in the right direction! Thanks so much.
I have added use utf8
to the controller, which fixed the page text:
Published by Randal Schwartz on Thursday 19 December 2024 00:00
I had never met Randal Schwartz before, but when I reached out to him about contributing to year 25 of the Perl Advent Calendar, he immediately agreed. This year I wanted to try some new things and Randal suggested giving a Perl-specific talk which he had given once before, but which was lacking in recording quality. This gave Randal a chance to re-record his talk and it gave the Perl communities a chance to watch in real time. We've never before had a video recording as a Perl Advent article, but I believe quite strongly that this is in the spirit of the Advent Calendar project, which is about giving something back. Also, when I mentioned it to Mark Fowler back in October, he didn't object, so I'll take that as a seal of approval.
-- Olaf Alders
Randal's advance summary of this talk:
Having been there, at the beginning with Perl, I will recount the early
days through the modern era (or as much as I can cover in the time
provided). I’ll deliver first-hand experience of the creation of the Camel
Book, the Llama book, and the way I invaded comp.unix.questions with Perl 2
answers so often that people would post “no Perl please”. Oh, and my
version of the story of the Schwartzian Transform.
If you enjoyed the video below, you may also enjoy the next in the series, which will be presented by Dave Cross: Still Munging Data with Perl. If you are at all interested, please register now, even if you cannot attend, as that will allow us to share the recording URL with you after the fact.
Now, please enjoy Randal Schwartz's "Half My Life with Perl":
Published on Thursday 19 December 2024 00:00
If you're tracing rays or transforming coordinates, you could be using PDL!
A little knowledge is a dangerous thing. Having seen how to do matrix multiplication on 16 December, the Elf R&D department, headed by Rudolf and Dancer, decided to calculate the sparkle from their baubles. In optics, tracing light rays through bits of glass that act as mirrors and lenses can be done by working through a series of equations or by multiplying matrices and vectors!
Let me start off by asking the folk on this platform one question. Imagine a scenario that you had lost something important with multiple potential negative consequences. For instance losing a bunch of keys including your car keys, your house keys, your changing room locker keys and a USB stick. What would be the greatest cause for alarm? I suspect that while there may be many possible answers aligned with each individual’s life priorities, the real men in this group know that the most feared is the reaction following the revelation to the wife. For while any calamitous occurrence may be approached objectively, with rationality, reflection and hopefully recovery, this particularly troublesome phase involves heightened emotions, reactivating Mrs Saif’s indelible memories of my many past failings. Objectivity, while desirable in principle, has to deal with such a tainted history.
I won’t elaborate further on my survival. I have survived. Barely. Indeed, over the years I must have acquired as poor an ability to remember traumas, as Mrs Saif is good at recollecting them. I haven’t figured out whether it is a natural self defence mechanism shielding me from PTSD, or whether it is a blessing that with age comes a little deafness, poorer eyesight, slower reactions and a reduced residual capacity of my intra-cranial hard-drive. But either way my poor wife’s vain attempts at reforming me, making me a better me, is met with the resistance caused by my irritatingly chilled attitude, constantly in perpetual denial of any need to change, and being so easily distracted.
This experience may help understand the general antipathy or indeed apathy towards any radical change from that which had been established over years. Objectively, a modern object orientated infrastructure within the core appears desirable. But such a goal is not a well defined endpoint. While there are a number of excellent bolted-on frameworks in CPAN, there are differences in priorities and performances. Entry of this form of modernisation into the core without upsetting core values is always going to difficult.
But this work is even more difficult for the poor core developer working on this task. He relies on feedback, without which he finds himself providing a valuable community service without any indication of what the value of his efforts is to that community.
So I decided, while watching a rubbish, inane but ludicrously popular game show, that for once I will make an effort. Not a personal resolution to be more responsible (apologies to Mrs Saif). I am too old for that malarkey. I will use Object::Pad
. I will try and replicate this game in an attempt to learn this new way of programming. I will try two things…Firstly by emulating this game using easily identifiable objects, secondly converting a module I have been using into one using Object::Pad
. Then I will report back my personal experience of this style of coding. As a disclaimer, I have only ever used classical packages in Perl so am unable to compare with Moose
, Moo
, Venus
etc. I am also as messy at coding as I am in my head.
A simple game where the player has to only answer one question to win money, while captivating one half of the audience, and mystifying the other half at how this could possibly be called entertainment. It seems simple enough to code. We have a “Game Board” object, containing a series of “Box” objects. The boxes each contain a random “Money” Object. A “Banker” object and the game board interact with the player via Objects representing the “Display” and the keyboard “UI” derived from old packages I have lying around. These objects may each have methods for drawing or undrawing themselves, or sending a message to the player, and responding to the players choices.
My effort has been so far remarkably smooth. Using signatures, auto-magically generated setters and getters has made this a remarkably straight forward and efficient process. The game is not yet complete…the biggest attraction of this game is not the challenges it poses but the “banter” and the suggestion that there is any skill or strategy involved. A reasonable algorithm for identifying what the banker should offer would also be interesting. My next post, once this game is completed, is a little review of how the whole experience has been for me, the visible benefits and drawbacks if any.
“`
Published by José Joaquín Atria on Wednesday 18 December 2024 00:00
Another busy Christmas period loomed, and the North Pole was tirelessly working to get the final preparations ready. Inside a brightly lit meeting room Santa cleared his throat.
"This is all very interesting, very... *yawn* ...stimulating", he said, picking at some cookie crumbs that fell from his beard to the table. "But how is that going to help us? How does ... absorbability help us deliver gifts?"
"Observability", corrected Ada Slashdóttir, the team's senior elf.
"Yeah, that's what I said", said Santa. "What does it do for us?"
As their platform had started to grow, the elves had struggled to keep track on where the bottlenecks were. And now that they had migrated parts of it to microservices and their platform was becoming more distributed, even the tools they were familiar were starting to become unwieldy.
"We need to have a way to see from the outside how our system behaves internally, Santa", said Ada. "That's observability. So it doesn't directly help us send gifts, but it helps us keep things running smoothly, and that helps us send gifts".
"Isn't that what logs do?", asked Duende Juniorsson, the team's junior elf, who was trying to catch up. "We already have logs in our services. Can't we use those?"
Santa liked logs. Yule logs in particular.
"We can, for sure", said Gnomo Knullpointer, who had recently joined Santa's workshop as a coding elf. "Logs are useful because they have been around forever, so they are well supported. But they have their limitations, specially now that a single request can touch several separate microservices before we are done with it. We'd have to correlate logs across services, which is not easy."
"Correlating logs is difficult, but there are other kinds of telemetry we can use", said Ada. "In particular, we can uses traces to track a request across services. This is called 'distributed tracing'. Let me show you how it works."
Ada then opened the code for the Naught-or-Not service, a Mojolicious application that checked whether a specific child has been naughty or nice:
# Naught-or-Not: the 'naughty or nice' service
use Mojolicious::Lite -signatures;
use Mojo::SQLite;
helper sql => sub { state $sql = Mojo::SQLite->new };
app->sql->migrations->name('santa')->from_string(<<'EOF')->migrate;
-- 1 up
create table naughty (id integer unique, naughty bool);
-- 1 down
drop table naughty;
EOF
get '/is-naughty/:id' => sub ($c) {
my $id = $c->param('id');
my $db = app->sql->db;
# If we have a value for this ID, return it
my $row = $db
->select( naughty => ['naughty'] => { id => $id } )
->hashes
->first;
return $c->render( json => { naughty => $row->{naughty} } ) if $row;
# Otherwise, generate one and store it before returning
my $naughty = !!( int rand 2 ); # Randomised naughtiness!
$db->insert( naughty => { id => $id, naughty => $naughty } );
$c->render( json => { naughty => $naughty } );
};
"The first thing we'll need to do", said Ada, "is get this code to start generating telemetry data. Traces in particular."
"Wait a second", said Gnomo. "If we start touching the code to generate this telemetry, we run the risk of introducing bugs. Not to mention that we have a lot of code, we'll never be able to do it all by hand..."
Santa was starting to sweat.
"We won't have to. We can use OpenTelemetry to do what is called 'zero code instrumentation', which will get us most of the way there. The first thing we'll need to do is load up the OpenTelemetry::SDK in our application. This will read the configuration from the environment and set things up internally so any telemetry we generate gets exported correctly. And since we are using Mojolicious we can use Mojolicious::Plugin::OpenTelemetry to actually generate telemetry data from our routes, all without actually touching any of the controller code."
use Mojolicious::Lite -signatures;
use OpenTelemetry::SDK;
use Mojo::SQLite;
plugin 'OpenTelemetry';
...
"And that's it?", asked Duende a little disappointed. He was already looking forward to touching a lot of code.
"Pretty much. The rest is just configuration. You can already see this in motion by running the code and telling it to export the traces to the console", said Ada, feeling pedagogic.
$ OTEL_TRACES_EXPORTER=console ./server get '/is-naughty/123'
[2024-12-16 23:24:29.37268] [821150] [trace] [nLc70JabTxup] GET "/is-naughty/123"
[2024-12-16 23:24:29.37288] [821150] [trace] [nLc70JabTxup] Routing to a callback
{
'attributes' => {
'client.address' => '127.0.0.1',
'client.port' => '47534',
'http.request.method' => 'GET',
'http.response.status_code' => 200,
'http.route' => '/is-naughty/:id',
'network.protocol.version' => '1.1',
'server.address' => '127.0.0.1',
'server.port' => '36703',
'url.path' => '/is-naughty/123',
'user_agent.original' => 'Mojolicious (Perl)'
},
'dropped_attributes' => 0,
'dropped_events' => 0,
'dropped_links' => 0,
'end_timestamp' => '1734391469.37503',
'events' => [],
'instrumentation_scope' => {
'name' => 'server',
'version' => ''
},
'kind' => 2,
'links' => [],
'name' => 'GET /is-naughty/:id',
'parent_span_id' => '0000000000000000',
'resource' => {
'process.command' => './server',
'process.command_args' => [
'get',
'/is-naughty/123'
],
'process.executable.name' => 'perl',
'process.executable.path' => '/home/user/.perl/perls/perl-5.40.0/bin/perl',
'process.pid' => 821150,
'process.runtime.name' => 'perl',
'process.runtime.version' => 'v5.40.0',
'telemetry.sdk.language' => 'perl',
'telemetry.sdk.name' => 'opentelemetry',
'telemetry.sdk.version' => '0.024'
},
'span_id' => '3823100b9ca26a91',
'start_timestamp' => '1734391469.3732',
'status' => {
'code' => 0,
'description' => ''
},
'trace_flags' => 1,
'trace_id' => '795c45630d3615ba1ebf524661bf01ac',
'trace_state' => ''
}
A wall of text (pretty printed here for clarity) appeared in front of the elves. Santa seized the opportunity to look like he knew what was going on by looking at the text in silence while thinking about what to have for lunch.
After a couple of seconds Gnomo and Duende had pieced it together. It was all there! Data about the request itself, how long it took, where it came from, and even details about what code had generated it... and all of that without having to touch any of the core logic!
"This is very cool", said Gnomo, who had been reading through OpenTelemetry::Guides::Quickstart and was already making sense of things. "And it seems we can get even more data if we load some of the instrumentation libraries that are already out there. This route uses a database, so we could use the OpenTelemetry::Instrumentation::DBI to get DBI to also generate telemetry".
use Mojolicious::Lite -signatures;
use OpenTelemetry::SDK;
use OpenTelemetry::Instrumentation 'DBI';
use Mojo::SQLite;
plugin 'OpenTelemetry';
...
Now running the test command printed even more output, this time including traces for all the DB operations, including the ones that executed when the service initially came up and ran the database migrations, etc.
"What about our other services? Not all of them are using Mojolicious", said Duende, thinking that maybe then he'd have the chance to touch some code.
Ada loaded up the code for the Gift Allocation service, which was implemented in Dancer2. This service made a call to Naught-or-Not to check whether this child had been naughty, and allocated either a lump of coal or one of the gifts from the child's wishlist.
package Gift::Assignment;
use Dancer2;
use HTTP::Tiny;
set serializer => 'JSON';
my $ua = HTTP::Tiny->new;
post '/gift/assign' => sub {
my $id = body_parameters->get('id');
my $res = $ua->get("$ENV{NAUGHT_OR_NOT_HOST}/is-naughty/$id");
unless ( $res->{success} ) {
status $res->{status};
return { error => $res->{reason} };
}
my $body = decode_json $res->{content};
return { gift => 'coal' } if $body->{naughty};
my @wants = body_parameters->get_all('wants');
{ gift => $wants[ int rand @wants ] };
};
"Like with the Mojolicious service, integrating this with OpenTelemetry requires no code changes", said Ada. Duende's heart sank. Was he going to get to touch any code? "All we need to do is load the Dancer2::Plugin::OpenTelemetry plugin".
"And like with the DBI instrumentation, since this route is using an HTTP client, we can load OpenTelemetry::Instrumentation::HTTP::Tiny to get telemetry for the requests it makes!", chimed in Gnomo, who was neck-deep in the documentation for instrumentation libraries and getting very excited.
The import list now looked like this:
package Gift::Assignment;
use Dancer2;
use Dancer2::Plugin::OpenTelemetry;
use OpenTelemetry::Instrumentation 'HTTP::Tiny';
set serializer => 'JSON';
...
"Hold on a second... what happened to the HTTP::Tiny import?", asked Duende, who was trying to follow along to find ways to contribute.
"Oh, well that's done automatically when we load the instrumentation library, so we don't need to write that twice", said Ada".
"And what about the OpenTelemetry::SDK import? Don't we need it here as well?", continued Duende.
"Ah, well spotted, Duende!", replied Ada. "We do need it, but we only need to load it once. Since this service will have more controllers once it's finished, loading it in each one would be a hassle, so it's better to load it once at the entry point. In this case, since we are using Plack to mount it, we can load that in the top-level PSGI file".
#!/usr/bin/env perl
use strict;
use warnings;
use lib 'lib';
use Gift::Assignment;
use OpenTelemetry::SDK;
use Plack::Builder;
builder {
Gift::Assignment->psgi_app;
}
Once again they tested it. They brought up the server configured to send traces to the terminal, and made a request:
$ curl -X POST \
-H 'Content-Type: application/json' \
-d '{"id":123, "wants":["teddy_bear", "crochet"]}' \
http://localhost:5000/gift/assign
As the traces appeared in the logs now for both services, Gnomo spotted something. "Hey look! Ignore everything in the traces except for those span and trace IDs". Pretty printed for your convenience, this is what he was looking at:
naught-or-not | {
'name' => 'SELECT "naughty" FROM "naughty" WHERE "id" = ?',
'parent_span_id' => '88b066b24f372f39',
'span_id' => '932cf74c171b705e',
'trace_id' => '16073bf43d281a0693ec7d5a9f4860a4',
}
naught-or-not | {
'name' => 'GET /is-naughty/:id',
'parent_span_id' => '7bd198e5a6ddd5fd',
'span_id' => '88b066b24f372f39',
'trace_id' => '16073bf43d281a0693ec7d5a9f4860a4',
}
gifts | {
'name' => 'GET',
'parent_span_id' => 'd9718758cefd39d4',
'span_id' => '7bd198e5a6ddd5fd',
'trace_id' => '16073bf43d281a0693ec7d5a9f4860a4',
}
gifts | {
'name' => 'POST /gift/assign',
'parent_span_id' => '0000000000000000',
'span_id' => 'd9718758cefd39d4',
'trace_id' => '16073bf43d281a0693ec7d5a9f4860a4',
}
Santa wondered if this is what it felt like to see the woman in the red dress.
"What are we looking at?", said Duende.
"They are all linked!", replied Gnomo excitedly. "They have the same trace_id
, and the parent_span_id
in each one is the same as the span_id
of the one that came before it, which appears lower in the logs".
"Well, except for that one at the bottom. That one just has a bunch of zeroes as the parent_span_id
", commented Duende.
"Yes, that is called the root span", said Ada. "The fact that it has a null parent_span_id
tells us that this is the span that initiated the trace".
"We could write something that would aggregate these data and present them in a neater way", said Gnomo. "Maybe something using a kind of flamegraph like the ones from Devel::NYTProf, which are great".
"We don't have to!", said Ada. "The OpenTelemetry project has already done that for us. We can use the OpenTelemetry Collector to aggregate all sorts of telemetry data generated via OpenTelemetry and then configure it to send that data to whatever external service or platform we want".
"Oh, so it's vendor agnostic too?", said Gnomo. "That's very good".
Santa's eye twitched at the mention of "agnostic". This was a touchy subject in the North Pole. He looked at Helga Hakrniüs, the Public Relations elf in the corner of the room who quietly shook their head. Santa let his breath out and wiped his brow.
"Yes, that's one of OpenTelemetry's key selling points", said Ada. "Vendor agnostic on the consumer side, and platform agnostic on the producer side, so you can use it in services written not only in all sorts of frameworks like we just saw, but also in all sorts of languages. They even have a demo application where you can see this in motion".
"Is this true for Perl as well?", said Gnomo. "We can obviously use it in Mojolicious and Dancer2, but what about other frameworks, like Catalyst? I heard that's what the Milk-and-Cookies team was using for their service. Or plain CGI? We still have a bunch of those in the older parts of the code".
"Yes!", said Ada. "The Perl implementation is relatively new, so there's still work to do, but we do have Plack::Middleware::OpenTelemetry that we can use for any server that uses Plack. The integration might not be as close, but it does work. And it's only a matter of time before more instrumentation libraries get released".
"So how do we use this Collector?", asked Duende.
"I'm glad you asked", replied Ada.
"The OpenTelemetry distribution on CPAN has an example collector stack that we can use to see how things go together", continued Ada. "There is also a similar example managed by the developers of the collector itself, and a more realistic example in the OpenTelemetry demo I mentioned before. We can use the one from CPAN to illustrate things for now".
git clone https://github.com/jjatria/perl-opentelemetry
cd perl-opentelemetry/examples/collector
docker compose up --build
"And then we tell the services to talk to it?", asked Gnomo.
"Yes", said Ada. "We need to set a couple of environment variables for each deployment so that they can talk to the collector. It's probably easier to show this with a docker compose file".
services:
naught-or-not:
build:
context: mojo
container_name: naught-or-not
volumes:
- ./mojo:/app
command:
- hypnotoad
- ./server
- --foreground
environment:
IO_ASYNC_LOOP: Mojo
OTEL_BSP_MAX_EXPORT_BATCH_SIZE: 1
OTEL_EXPORTER_OTLP_ENDPOINT: http://localhost
OTEL_SERVICE_NAME: naught-or-not
OTEL_TRACES_EXPORTER: otlp
network_mode: host
gifts:
build:
context: dancer
container_name: gifts
volumes:
- ./dancer:/app
command:
- plackup
- --server
- Net::Async::HTTP::Server
- ./server.psgi
environment:
NAUGHT_OR_NOT_HOST: http://localhost:8080
OTEL_BSP_MAX_EXPORT_BATCH_SIZE: 1
OTEL_EXPORTER_OTLP_ENDPOINT: http://localhost
OTEL_SERVICE_NAME: gifts
OTEL_TRACES_EXPORTER: otlp
network_mode: host
"In both cases we need to set OTEL_SERVICE_NAME
to something meaningful so we can identify where the traces come from. We also need to set OTEL_TRACES_EXPORTER
to otlp
, which is the protocol used by the collector, and OTEL_EXPORTER_OTLP_ENDPOINT
to the host that we are sending the telemetry to. In this case, both of these are set to their default values, but it's good for now to be explicit".
"What about that OTEL_BSP_MAX_EXPORT_BATCH_SIZE
?", asked Duende. He had already learned the hard way that a batch of one was a little pointless.
"Oh, that's only for testing. In a realistic scenario we can increase that to limit the number of requests that we make to the collector", said Ada.
"So the communication with the collector happens off-band?", asked Gnomo.
"Yes. When exporting to the console we got each span as it was produced, but in a production environment you'd expect many times more spans to be produced, and it would be very expensive to export each one as it came. It's more efficient to batch them and send them all together when enough of them are ready. The Perl OpenTelemetry implementation uses a batch span processor written on top of IO::Async to do this".
Santa wondered if his elves lived in the same planet as he did. He was glad there were people in the room who knew about this so he didn't have to.
"Is that why we need the IO_ASYNC_LOOP
variable in the Mojolicious environment?", asked Gnomo.
"Yes", said Ada. "We need that because Mojolicious has its own event loop, so we need to tell IO::Async to use it".
"I see. And that's why in the Dancer2 environment we use Net::Async::HTTP::Server, because Dancer2 doesn't have an event loop, and this gives it one", said Gnomo.
"Exactly", said Ada. "And now, if we run this so it talks to the collector stack, and make some queries, we can see the traces in our browser. That stack connects the collector to a Jaeger instance, which we should be able to see at http://localhost:16686".
"Incidentally", said Ada, "I've put all the files for what we've been talking about in a repository you can check out if you want to run things yourself".
Santa was happy to finally be able to see some pretty pictures. And as he looked at his elves, all excitedly looking around and exploring the data, he felt a warm sense of pride and joy to see them learning and growing and getting things done.
With this out of the way, he could finally get back to what mattered most: having those milk and cookies.
Published on Wednesday 18 December 2024 00:00
The Mandelbrot set is a popular fractal plot that makes for great visualizations and animations, besides its scientific uses.
I will not delve into it deeply, but the link above points to a computer algorithm that is written in Python on Wikipedia. However, if you want to make that algorithm actually intuitive and more identical to the mathematical equations, you want to use PDL for it.
PDL allows for n-dimensional arrays to be created out of the box and manipulated on, as you would do in more mathematical but slower tools like MATLAB or Mathematica.
In this post, I demonstrate how to go about implementing a Mandelbrot visualization, including multibrot ones.
Tags: mandelbrot visualization
Published by gatorreina on Tuesday 17 December 2024 21:23
I have set up a Mariadb Maxscale (1 Primary and 2 Replicas) replication cluster for experimentation purposes. As I understand it the main purpose for using Maxscale is that it decides what server to query. Accordingly, I am unsure how to direct my DBI query to Maxscale instead of a specific server.
I usually use Perl DBI to connect to databases in the following manner;
my $dbh = DBI->connect("DBI:mysql:database=$db_name};$server",$user,$passwd);
Can someone tell me how to do this with Maxscale?
maxctrl list servers
┌─────────┬─────────────┬──────┬─────────────┬─────────────────┬──────────┬─────────────────┐
│ Server │ Address │ Port │ Connections │ State │ GTID │ Monitor │
├─────────┼─────────────┼──────┼─────────────┼─────────────────┼──────────┼─────────────────┤
│ server1 │ 192.168.0.2 │ 3306 │ 0 │ Master, Running │ 0-2-1438 │ MariaDB-Monitor │
├─────────┼─────────────┼──────┼─────────────┼─────────────────┼──────────┼─────────────────┤
│ server2 │ 192.168.0.3 │ 3306 │ 0 │ Slave, Running │ 0-2-1438 │ MariaDB-Monitor │
├─────────┼─────────────┼──────┼─────────────┼─────────────────┼──────────┼─────────────────┤
│ server3 │ 192.168.0.4 │ 3306 │ 0 │ Slave, Running │ 0-2-1438 │ MariaDB-Monitor │
└─────────┴─────────────┴──────┴─────────────┴─────────────────┴──────────┴─────────────────┘
I seem to be able to connect as I can do:
$ mysql -h 192.168.0.1 -umaxscale -ppasswd -P4006 -e 'SHOW DATABASES;'
+--------------------+
| Database |
+--------------------+
| admin |
| information_schema |
| mysql |
| performance_schema |
| sys |
| test |
+--------------------+
I just can't figure out how to successfully query via Perl DBI.
Published by Robert McIntosh on Tuesday 17 December 2024 20:53
The Weekly Challenge, organized by Mohammad S. Anwar, is a friendly competition in which developers compete by solving a pair of tasks. It encourages participation from developers of all languages and levels through learning, sharing, and having fun.
Task 2: Nested Array from The Weekly Challenge 300 prompts developers to find the longest nested array length.
The Weekly Challenge 300 deadline is Sunday, December 23, 2024 at 23:59 (UK Time). To avoid bias, please consider reading this post after competing.
You are given an array of integers,
@ints
of lengthn
containing permutation of the numbers in the range[0, n-1]
.Write a script to build a set,
set[i] = ints[i], ints[ints[i]], ints[ints[ints[i]]], ...
subjected to the following rules:
- The first element in
set[i]
starts with the selection of elementsints[i]
.- The next element in
set[i]
should beints[ints[i]]
, and thenints[ints[ints[i]]]
, and so on.- We stop adding right before a duplicate element occurs in
set[i]
.Return the longest length of a set
set[i]
.
Example 1 and Example 2 present the expected outputs for given inputs.
Input: @ints = (5, 4, 0, 3, 1, 6, 2)
Output: 4
Here one of the longest sets is set[0]
:
set[0] = {ints[0], ints[5], ints[6], ints[2]} = {5, 6, 2, 0}
Input: @ints = (0, 1, 2)
Output: 1
def build_set_from_index(ints, starting_index):
iset = [
ints[starting_index],
]
for ints_index in range(1, len(ints)):
pindex = iset[ints_index - 1]
value = ints[pindex]
if value in iset:
break
iset.append(value)
return iset
def return_longest_length(ints):
max_length = 0
for i in range(0, len(ints)):
iset = build_set_from_index(ints, i)
iset_length = len(iset)
if iset_length > max_length:
max_length = iset_length
return max_length
My solution utilizes two functions build_set_from_index
and return_longest_length
.
build_set_from_index
build_set_from_index
returns the set[starting_index]
constructed from the parameters ints
and starting_index
. I used an iterative approach to construct set[]
.
My approach emerged from an early morning and a subsequent paraphrasing of the set[]
construction rules. Initially, these rules seemed complex. But, after re-reviewing Example 1 after a decent breakfast and caffeine, I better understood these rules. I was also able to formulate the following paraphrase.
set[i]
is a set with k
elements.
k
.set[i]
.set[i]
at k = 0
is equal to ints[i]
.k > 0
, the k-th
element of set[i]
is equal to value of ints[]
indexed using the (k-1)-th
element of set[i]
.Using the paraphrased approach, interatively constructing set[0]
from @ints
, as in Example 1, became even more straightforward!
k = 0
, the value of set[0]
is equal to ints[0] = 5
. set[0]
contains {5}
.k = 1
, the value of set[0]
is equal to ints[5] = 6
. set[0]
contains {5, 6}
.k = 2
, the value of set[0]
is equal to ints[6] = 2
. set[0]
contains {5, 6, 2}
.k = 3
, the value of set[0]
is equal to ints[2] = 0
. set[0]
contains {5, 6, 2, 0}
k = 4
, STOP because set[0]
contains ints[0] = 2
.return_longest_length
return_longest_length
finds the maximum length of all set[]
constructed from ints
. It utilizes build_set_from_index
to generate set[k]
for each 0 <= k < len(ints)
and then indentifies the set[k]
with the longest length.
In this post I discussed Task 1: Nested Array and I presented my solution. My solution is straightforward, was largely informed by how I paraphrased the original task, and highlights the importance of a good breakfast.
Published by Robert McIntosh on Tuesday 17 December 2024 01:35
The Weekly Challenge, organized by Mohammad S. Anwar, is a friendly competition in which developers compete by solving a pair of tasks. It encourages participation from developers of all languages and levels through learning, sharing, and having fun.
Task 1: Beautiful Arrangement from The Weekly Challenge invites developers to find the number of beautifully arranged permutations from among all permutations generated from a positive integer.
In this post I discuss, and present my solution to, Task 1: Beautiful Arrangement, and end a brief conclusion.
The Weekly Challenge 300 deadline is Sunday, December 23, 2024 at 23:59 (UK Time). To avoid bias, consider reading this post after competing.
You are given a positive integer,
$int
.Write a script to return the number of beautiful arrangements that you can construct from
$int
.A permutation of
n
integers, 1-indexed, is considered abeautiful arrangement
if for everyi
(1 <= i <= n)
either of the following is true:
permutation[i]
is divisible byi
i
is divisible bypermutation[i]
Examples 1 and 2 present the expected outputs from given inputs.
Input: $n = 2
Output: 2
For n = 2
and with i
integers such that (1 <= i <= n)
there are two permutations (1, 2)
and (2, 1)
. Output: 2
because both meet the beautiful arrangement
requirements.
The permutation (1, 2)
is a beautiful arrangement because all of its elements match the first condition:
i = 1
, permutation[1] = 1
satisfies the first condition, since one is divisible by one.i = 2
, permutation[2] = 2
satisfies the first conditions, since two is divisible by two.
The permutation(2, 1)
is also a beautiful arrangement because all of its elements match either the first or second condition:
i = 1
, permutation[1] = 2
satisfies the first condition, since two is divisible by one.i = 2
, permutation[2] = 1
satisfies the second condition, since two is divisible by one.Input: $n = 1
Output: 1
Input: $n = 10
Output: 700
from itertools import permutations
def generate_permutations(n)
iterable = list(range(1, n + 1))
return permutations(iterable)
def count_beautiful_arrangements(perms):
num_beautiful_arr = 0
for perm in perms:
is_beautiful_arr = True
for value_index, value in enumerate(perm):
if value % (value_index + 1) == 0:
continue
elif (value_index + 1) % value == 0:
continue
else:
is_beautiful_arr = False
break
if is_beautiful_arr == True:
num_beautiful_arr += 1
return num_beautiful_arr
My inelegant and unsophisticated solution utilizes two functions generate_permutations
and count_beautiful_arrangements
.
generate_permutations
returns, for the parameter n
, all permutations for the set where 1 <= i <= n
.
iterable = list(range(1, n + 1))
generates a list of integers where 1 <= i <= n
.permutations(iterable)
, imported from the itertools
module, generates all permutations of iterable
. count_beautiful_permutations
returns, for the permutations iterable perms
parameter, the total number of permutations in perms
that match the beautiful arrangement conditions.
for perm in...
iterates through each permutation. perm
is a beautiful arrangement (is_beautiful_arr = True
).
for value_index, value in...
checks if each element of perm
matches either condition 1 or condition 2.
perm
is counted as a beautiful arrangement. is_beautiful_arr
is set to False
, the loop breaks early and perm
is not counted as a beautiful arrangement.
In this post I discussed Task 1: Beautiful Arrangement and I presented my solution. My 'inelegant and unsophisticated' solution works, but it has considerable room for improvement.
Published by E. Choroba on Tuesday 17 December 2024 00:00
Everybody knows that the North Pole™ produces and delivers Christmas gifts, but it’s not the sole business they’re into. In fact, they’ve recently engaged in production of Christmas ornaments and Christmas cards, too.
As is common, starting a new business brings new problems. The elves needed a way to design decorated Christmas trees to evaluate their ornament proposals and their combinations. They started by planting small trees and decorating them by hand, but quickly found out this approach didn’t scale as more and more (and bigger and bigger) trees were needed.
At a C-level meeting, Santa listened to laments of the managing elf responsible for the ornaments and narrowed his eyes at another young elf at the other corner of the table.
“You had something for arranging trees, right?” asked Santa.
“Yes, but…” spluttered the elf.
“Let’s meet after lunch and see how we can share the knowledge,” commanded Santa.
In the afternoon (by the way, it was already dark, since it was the North Pole and summer was over) Santa met with the COO and CCO (where the second letter stands for Ornaments or Cards, respectively).The CBO (Chief Baubles Officer) was missing as his department was merged with the Ornaments in the last workforce shaping to trim the fat.
“Can you show us what your department uses to visualise trees?” asked Santa, turning to the CCO.
“Our developers found this open source tool called TrEd, which stands for ‘Tree Editor’,” replied the elf. “And they’re still discovering new features it has. You can do much more than view the trees: you can easily change their structure, add attributes to nodes and edges, add secondary relations that turn the trees into full graphs, and there are also tools for searching large treebanks.”
“I hate the jargon,” muttered Santa and turned to the COO, “but I guess you’re following.”
“Actually, not really,” replied the COO, “we need to arrange the ornaments, but we don’t want to change the structure of the trees. How is such a thing needed to produce a Christmas card, anyway?”
“We hear similar questions often,” said the CCO keeping a stiff upper lip. “At the beginning, we only produced English Christmas cards, so we didn’t need anything like that. But several years ago we started printing the cards in other languages, too, and we needed a way to translate all the greetings and wishes. We started with elvish translators, but we found ourselves in your boots, so to say: the approach didn’t scale.
“We needed an automated process. We reached for statistical machine translation, but for that, we needed large aligned corpora in both the source and target languages.”
“Corpora?” asked Santa raising an eyebrow.
“Large collections of texts. And we quickly found out aligning the individual words wasn’t enough, as the grammar in various languages can change the words in different roles. The sentence structure stays usually much more similar across languages than individual words and their order. That’s why we started annotating the trees.”
“Decorating,” said Santa and nodded to the COO hopefully.
“No, annotating,” explained the CCO. “I’m talking about trees in the graph-theory sense. We arrange the words in a sentence to a tree and annotate the relations between them with their syntactic roles: this word is a subject of this verb, that word is an adverbial of that word,” and he started to gesticulate wildly.
“Wait, wait,” the COO interrupted him, “can you show us what you’re talking about? I still have no idea.”
“Ho ho ho,” said Santa, “a picture is worth a thousand words!”
The CCO opened his ChristmasPad and typed something into a terminal. “See? This is Ukrainian, by the way.”
“That’s impressive,” admitted Santa, “but I fear there’s some kind of confusion.”
“Let’s have a look at a simpler example in English,” replied the CCO and quickly typed on the keyboard. “The annotated sentence is Is that Microwave that you gave Dan really expensive?”
“You can see the pronoun that references the word microwave, and the word microwave is an object of the verb give in a semantic sense which we can also capture.”
“No, no,” tried Santa to stop him, “linguistics is not our concern.”
“That”s great!” rejoiced the CCO. “I’ve always wondered whether TrEd can be used outside of linguistics. There already is one such use: The tree editor serves as a client to a search engine. You assemble a tree and the engine searches your tree data to find where the tree would fit. The trick is you can specify different relations than the ordinary parent–child one.”
“Ho ho ho,” nodded Santa, “Christmas is a family time!”.
“I mean this,” explained the CCO and again showed them his screen.
“Normally, the parent would be at the top, but here, we’re using the reversed relation, so the query will search for all nominal subjects whose parent is not a verb.”
“How can something that’s not a verb have a subject?” wondered Santa.
“Let me show you the English example with the microwave again. The Universal Dependencies style uses adjectives in copula constructions as parents of the subject and the auxiliary verb. The word expensive is not a verb, but the microwave is its nominal subject.”
“I fear this whole thing is of no use for us,” sighed the COO. “What programming language is the tool written in?”
“Perl,” replied the CCO. “It uses Tk::Canvas to edit the trees, which makes it rather easy to extend if you need more features.”
“At least something our team would understand. And the search engine is also written in Perl?” asked the COO again.
“There are in fact two implementations,” replied the CCO. “One uses SQL on Postgres to store and query the data, but it’s only suitable for data that don’t change, as updating the database is quite slow. The second implementation uses Perl and is great for querying frequently changing data. If the data are large, you need some kind of parallelism to compensate its less favourable speed, we run it over slurm. But you can also write your queries directly in Perl. This will show you exactly the same trees as the query I showed you before.” And he again used the terminal.
btred -N -T -e '
FPosition()
if $this->{deprel} eq "nsubj"
&& $this->parent->{upostag} ne "VERB"
' data/*.conllu | tred -l-
“Also, if you need to process the data without all the power TrEd offers, you can just use Treex::PML, the library that TrEd is based on. It implements the Prague Markup Language used as TrEd’s native data format. The previous five-liner turns almost into a screenful,” and he opened Elven Mate at Creating Scripts (EMaCS) and started to type, interrupted several times by squinting into the documentation.
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
my $ud_path;
BEGIN { $ud_path = $ENV{UD_DIR} }
use lib "$ud_path/libs";
use Treex::PML qw{ ImportBackends AddResourcePath };
my @backends = ImportBackends('UD');
AddResourcePath("$ud_path/resources");
my $schema = 'Treex::PML::Factory'->createPMLSchema({
use_resources => 1,
filename => "ud_schema.xml"});
for my $file (@ARGV) {
my $doc = 'Treex::PML::Factory'->createDocumentFromFile(
$file, {backends => \@backends});
my $tree_no = 1;
for my $tree ($doc->trees) {
my $node_no = 1;
for my $node ($tree->descendants) {
say "$file##$tree_no.$node_no"
if $node->{deprel} eq "nsubj"
&& $node->parent->{upostag} ne "VERB";
++$node_no;
}
++$tree_no;
}
}
“It’s interesting, but I don’t see how our department could benefit from it,” shrugged the COO.
Santa seemed lost in thought. “Maybe your department can’t,” murmured he, “but we have many other departments that need solutions…”
He dismissed the meeting by pointing at the door and strode towards his office. Can you think of a way how you could benefit from the tool?
Published on Tuesday 17 December 2024 00:00
If you want to create new PDL operations, it's easy! You can make an ad-hoc one with Inline::Pdlpp, and expand it (or just start) by using pptemplate.
Published by London Perl Workshop on Monday 16 December 2024 14:10
Do you want LPW to happen again in 2025? Then you need to make it happen. You need to start thinking about this now. After Lee's closing talk, which detailed how organisation of the 2024 workshop worked and effectively put out a call for organisers for the future, a small number of attendees hinted they would be able to help out in one way or another. For that we are grateful.
However there is no core organising team yet for 2025. Someone, ideally two or three people, need to step up and explicitly say "we are going to organise LPW 2025". If you need help around any of this then we (the 2024 organisers) can guide you. The TPRF have also said they would like to explore how to support LPW 2025 and welcome potential organisers to join the monthly community meeting to discuss this.
Failing that LPW will be going on an indefinite hiatus again.
Responding to the 2024 feedback
Roughly one fifth of the attendees completed the feedback form, I'm showing the results here and will respond to (paraphrased-to-reatain-anonymity) feedback below.
I think it's clear that a variety of subjects is beneficial, with Perl still having the core focus. I should also say that I hope it was relatively clear that if you didn't attend certain topics then you pick the "did not attend" option not the "not useful" option (you don't know if something is useful or not unless you attend it). I don't know if that was the case though and there was some gaming of the form, which was a bit... well, whatever.
I think this is fair - as I explained in my talk, the venue is always a set of compromises and here I think we found the correct ones. Catering is a bonus, we could have spent more but decided not to. The size issue was probably the difference between the two rooms, which I cover below.
I think it's clear here as well that most people are satisfied with the current setup. Maybe there's some demand or appetite to have a longer workshop (2 days) but not enough for demand to change this. Most comments about the above essentially confirmed what we suspected (or Lee asked/questioned in his talk).
Well that's nice to know, thanks. And we also got positive feedback and thanks in the form as comments, so thanks for that as well. Below I'll respond to some suggestions, comments, and so on.
Acoustics in ballroom could be better / Some sound issues - yes, it was maybe down to the nature of the space.
The Library was too small for some talks. Mayby make it clear the size of the room will be selected based on signups so people pay more attention? / Second track was sometimes too full - When announcing the schedule I made it clear that attendees need to mark talks they plan to see in advance so we can choose the right rooms. In fact, it was the second sentence of the announcement. This was cross posted to the usual places. I did the same in another post a bit later. I don't know how I can make it clearer. This is always a problem at multi track events, I think we do OK. Roughly 50% of attendees marked their talks.
It was hard to find the venue, as it was tucked back and not obvious. - The workshop site had details of the venue, including a map. We had signs up on the venue doors. Maybe we could have had one outside, but I'm not convinced that's a viable option given the location. We will put a photo of the venue on the site next time.
Allow more "general open source" topics, and possibly including topics intended/useful for managers - We allow just about any talk on just about any subject, people need to submit them.
A Telegram or Whatsapp or whatsoever chat group for the attendees would have been nice. - Good idea.
Do not need to provide swag - Yes, but some people complain when they don't get a t-shirt. I don't want to do t-shirts anyway, but thought the scarves were a nice thing (and useful). Plus cheaper and easier. All other swag was provided by sponsors, which we allow them to do because it is an incentive for them to sponsor us.
Next time, maybe allow one or two "partner" level sponsors? - Getting companies to sponsor us is hard. Really really hard. I talked about this at the end. That said, this might work so we will look into it.
Probably also a Wiki [would be useful], though I'm not sure about the costs. - The workshop site (ACT) supports a wiki. We will add a link to it on future workshop sites and suggest attendees use it.
That's all for now. Thanks to this year's sponsors, without whom LPW would not have happened:
Published by Gabor Szabo on Monday 16 December 2024 06:35
Originally published at Perl Weekly 699
Hi!
I am not superstitious, but when a camel crosses my path ... I write about it in the Perl Weekly.
Similarly | On the other hand, I avoid celebrating birthdays of people ahead of time, but I think I can wish Perl a happy birthday already. It won't die on us in the next 2 days, will it?
The other day I was looking (again) for open source projects that are also deployed as a SaaS product (e.g MediaWiki is deployed as Wikipedia and is deployed as DEV.to. I found the awesome-selfhosted which is not the same, but might be used a good starting point. This brought me to the question, is there an awesome Perl list?
A quick search found me two: awesome-perl by hachioji.pm and awesome-perl by uhub.
Finally, the Advent calendars are still on. There are some truly awesome articles in all of them: The 2024 PDL Advent Calendar, the Perl Advent Calendar 2024, and the Raku Advent Calendar.
And one more thing, if you'd like to have your Perl event listed on our events page and listed at the bottom of the newsletter, send a Pull-Request to add it to the events.json file.
And a last thing. I started to have guests on the Code Maven live events. I'd be happy to have guests who would like to talk about some Perl-related subject. They don't have to be 'well reharsed talks', they can be just 'sit down and let me show you how to do X' things. Talk to me if you are interested.
Enjoy your week!
--
Your editor: Gabor Szabo.
Oh the hated message...
The Weekly Challenge by Mohammad Sajid Anwar will help you step out of your comfort-zone. You can even win prize money of $50 by participating in the weekly challenge. We pick one champion at the end of the month from among all of the contributors during the month, thanks to the sponsor Lance Wicks.
Welcome to a new week with a couple of fun tasks "Beautiful Arrangement" and "Nested Array". If you are new to the weekly challenge then why not join us and have fun every week. For more information, please read the FAQ.
Enjoy a quick recap of last week's contributions by Team PWC dealing with the "Replace Words" and "Word Search" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
Keeping it simple with multi layered for loops. Plenty of information presented too. Great work.
Regex in action, brave heart. Loved it. Thanks for sharing knowledge with us every week.
PDL and matrix, what a deadly combination. Cool work to show the power of PDL. Well done.
One-liner from Peter? Yes, you heard me correctly. Don't forget to DIY. Brilliant work.
Not many tried Word Search this week and Robbie is not one of them. Please checkout his approach. Highly recommended.
My favourite Postscript is now the choosen on this week for the blog post. Simply love it. Thanks for the contributions.
For all Python lovers, you don't want to miss the regex solution in Python. There is always something new to learn, thanks for sharing the knowledge.
Great CPAN modules released last week;
MetaCPAN weekly report;
StackOverflow Perl report.
December 19, 2024, In Person Event
January 20, 2025, Virtual event in Zoom
You joined the Perl Weekly to get weekly e-mails about the Perl programming language and related topics.
Want to see more? See the archives of all the issues.
Not yet subscribed to the newsletter? Join us free of charge!
(C) Copyright Gabor Szabo
The articles are copyright the respective authors.
Published by Robert McIntosh on Sunday 15 December 2024 20:25
The Weekly Challenge, organized by Mohammad S. Anwar, is a friendly competition in which developers compete by solving a pair of tasks. It encourages participation from developers of all languages and levels through learning, sharing, and having fun.
Last week I competed in The Weekly Challenge 299 by solving Task 1: Replace Words. The task challenged developers to write a script that, when given an array and a sentence, replaced all words in the sentence that started with any of the words in the array.
In this post I present an overview of, and my solution to, Task 1: Replace Words from The Weekly Challenge 299 and finish with a brief conclusion.
You are given an array of words and a sentence.
Write a script to replace all words in the given sentence that start with any of the words in the given array.
Examples 1 - 3 illustrate the expected outputs from given inputs.
Input: @words = ("cat", "bat", "rat")
$sentence = "the cattle was rattle by the battery"
Output: "the cat was rat by the bat"
Output
can be obtained by replacing any word in $sentence
with $word
from @words
if it starts with $word
, for example:
cattle
starts with the word cat
, so replacing cattle
with cat
transforms the sentence into the cat was rattle by the battery
.battery
starts with the bat
, so replacing battery
with bat
transforms the sentence into the cat was rattle by the bat
.rattle
starts with the word rat
, so replacing rattle
with rat
, transforms the sentence into the cat was rattle by the bat
.Input: @words = ("a", "b", "c")
$sentence = "aab aac and cac bab"
Output: "a a a c b"
Input: @words = ("man", "bike")
$sentence = "the manager was hit by a biker"
Output: "the man was hit by a bike"
def replace_word(sentence, this_word):
return ' '.join([this_word if word.startswith(this_word) else word for word in sentence.split(' ')])
def replace_words(words, sentence):
for word in words:
sentence = replace_word(sentence,
word)
return sentence
My solution uses two functions: replace_word
and replace_words
.
The replace_word
function replaces any word in the string sentence
that starts with this_word
with this_word
using built-in string methods split
, startswith
, and join
and a list comprehension.
sentence.split(' ')
splits sentence
into a list of words using (' ')
as a delimiter.[this_word if word.startswith(this_word) else word for word in...]
builds another list of words from the split
sentence
list, replacing a word with this_word
when it startswith
the this_word
.' '.join(...)
concatenates the second list into a string using (' ')
return
returns the stringThe replace_words
function successively applies replace_word
to sentence
for each word
in the array words
. It then returns the transformed sentence.
In this post I presented an overview of, and my solution to, Task 1: Replace Words from The Weekly Challenge 299.
Since I used built-in methods like split
, join
, and startswith
in my solution, it is straightforward, verbose, and maybe easy to understand. Such an approach may be helpful to you if you are new to Python, new to programming, or unfamiliar with regular expressions.
Each week Mohammad S. Anwar sends out The Weekly Challenge, a chance for all of us to come up with solutions to two weekly tasks. My solutions are written in Python first, and then converted to Perl. It's a great way for us all to practice some coding.
You are given an array of words and a sentence.
Write a script to replace all words in the given sentence that start with any of the words in the given array.
For this task I use a regular expression to make the transformation. The complete code is
def replace_words(words: list[str], sentence: str) -> str:
regexp = r"(?<![a-z])(" + "|".join(map(re.escape, words)) + r")([a-z]*(?:'[a-z]+)?)"
return re.sub(regexp, r'\1', sentence)
The first bracket (?<![a-z])
is a negative look behind. This ensures that we only look at the first letters in each word. The next section (" + "|".join(map(re.escape, words)) + r")
joins the words we want to replace with a |
character, escaping any characters that have special meaning in a regular expression. The final part ([a-z]*(?:'[a-z]+)?)
matches any remaining letters, optionally with an apostrophe '
character and some more letters. This ensures we match compound words like can't
and would've
.
For the input from the command line, I take the last value as the sentence and everything else as words.
$ ./ch-1.py cat bat rat "the cattle was rattle by the battery"
the cat was rat by the bat
$ ./ch-1.py a b c "aab aac and cac bab"
a a a c b
$ ./ch-1.py man bike "the manager was hit by a biker"
the man was hit by a bike
$ ./ch-1.py can "they can't swim"
they can swim
$ ./ch-1.py row "the quick brown fox"
the quick brown fox
You are given a grid of characters and a string.
Write a script to determine whether the given string can be found in the given grid of characters. You may start anywhere and take any orthogonal path, but may not reuse a grid cell.
For this task, I start by checking all rows have the same number of columns. I then go through each cell. If the letter in that cell is the first letter of the word, I call the find_match
function. If that returns true, this function will return true. If it doesn't, I continue to the next cell that contains the starting letter. If none exists, I return false.
def word_search(matrix: list[list[str]], word: str) -> bool:
rows = len(matrix)
cols = len(matrix[0])
for row in range(rows):
if len(matrix[row]) != cols:
raise ValueError("Row %s has the wrong number of columns", row)
for row in range(rows):
for col in range(cols):
if matrix[row][col] == word[0]:
if find_match(matrix, word[1:], [[row, col]]):
return True
return False
The find_match
function is a recursive function. It takes three parameters
[row,col]
pairs of cells we have visited.From the last position, we can move one of four directions (up, down, left or right). I check that moving this direction does not put us out of bounds or is a cell we've already used. If it isn't and the letter in this cell matches the letter we are looking for, I call the function again, taking the first letter off the word
variable, and adding the new position. If I get to a point where there are no letters left, the word can be found and I return True
.
def find_match(matrix, word, positions):
if word == '':
return True
directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]
current_row = positions[-1][0]
current_col = positions[-1][1]
for direction in directions:
next_row = current_row + direction[0]
next_col = current_col + direction[1]
if next_row < 0 or next_row >= len(matrix) or next_col < 0 or next_col >= len(matrix[0]):
continue
if [next_row, next_col] in positions:
continue
if matrix[next_row][next_col] == word[0]:
if find_match(
matrix,
word[1:],
[*positions, [next_row, next_col]]
):
return True
return False
$ ./ch-2.py '[["A", "B", "D", "E"],["C", "B", "C", "A"],["B", "A", "A", "D"],["D", "B", "B", "C"]]' BDCA
True
$ ./ch-2.py '[["A", "A", "B", "B"],["C", "C", "B", "A"],["C", "A", "A", "A"],["B", "B", "B", "B"]]' ABAC
False
$ ./ch-2.py '[["B", "A", "B", "A"],["C", "C", "C", "C"],["A", "B", "A", "B"],["B", "B", "A", "A"]]' CCCAA
True
Published by Unknown on Saturday 14 December 2024 22:10
Published by Unknown on Saturday 14 December 2024 22:07
This is the weekly favourites list of CPAN distributions. Votes count: 33
Week's winner: Path::Tiny (+2)
Build date: 2024/12/14 21:06:47 GMT
Clicked for first time:
Increasing its reputation:
Published by alh on Sunday 08 December 2024 13:54
Paul writes:
I had a suddenly much more productive month in November, as a lot of little logjams and other things in the way all got cleared.
Hours:
Total: 31 hours
Published by Unknown on Saturday 07 December 2024 22:10
Published by Unknown on Saturday 07 December 2024 22:07
This is the weekly favourites list of CPAN distributions. Votes count: 33
Week's winner: App::Changelog (+2)
Build date: 2024/12/07 21:07:28 GMT
Clicked for first time:
Increasing its reputation:
Published by Mayur Koshti on Tuesday 03 December 2024 20:48
Published on Tuesday 03 December 2024 20:04
The Perl and Raku Conference 2025 is coming up June 27-29, 2025, and it’s set to be an exciting gathering for developers, enthusiasts, and community members alike. This annual event brings together some of the best minds in the world of Perl and Raku programming, and we can’t wait to see you there! For the first time ever, the conference will be presented in the Palmetto State, South Carolina! The technical and cultural hub of upstate South Carolina is the city of Greenville, home of the Lockheed-Martin F-16!
Whether you’re a seasoned Perl programmer or someone eager to learn more about Raku, the conference offers the perfect opportunity to share knowledge, network with like-minded individuals, and collaborate on innovative ideas. This year’s event promises to be an immersive experience full of workshops, discussions, and talks on the latest trends and developments in the Perl and Raku ecosystems.
This year’s conference format is a little different from past years, in a more-streamlined format. On Friday, June 27, we’ll have a day-long add-on class session, then the main conference on Saturday and Sunday, June 28 and 29. Lightning talks are scheduled both days, an we have an exciting keynote from a new voice to our community on Saturday morning: David Both, a writer, speaker, trainer, and long-time proponent of the Linux Philosophy.
Attending the Perl and Raku Conference means you’ll have the chance to:
The Call for Papers (CFP) for the Perl and Raku Conference 2025 is now open! This is your chance to contribute to the event by submitting your proposals for talks, tutorials, and workshops. Whether you’re an experienced developer with a deep dive into language features, or someone with a fascinating project that showcases the power of Perl and/or Raku, we want to hear from you!
Topics we’re looking for include, but are not limited to:
The Perl and Raku community thrives on collaboration and the sharing of knowledge. By submitting a proposal, you’ll not only have the chance to present your ideas to an engaged and passionate audience but also contribute to the growth of these incredible languages. Whether you’re a first-time speaker, or a seasoned pro, we encourage you to submit. Bring your passion and ideas to share with your colleagues! The submission deadline of January 15 is coming up quick, so start getting your ideas together, and submit your talk now. Accepted speakers will receive complimentary conference registration, as usual. With our streamlined, two-day format, there are fewer slots available, so talk acceptance will be more competitive than we’re used to.
We look forward to seeing you at the end of June in Greenville, SC! For more details, including special-price lodging at the conference hotel and submission guidelines, visit the official conference website.
Published by perlancar on Tuesday 03 December 2024 02:19
dist | author | abstract | date |
---|---|---|---|
Acme-CPANModules-Soundex | PERLANCAR | List of modules that implement the soundex algorithm | 2024-11-24T00:05:57 |
Acme-CaSe | CONTRA | The great new Acme::CaSe! | 2024-11-19T08:13:26 |
Acme-Case | CONTRA | The great new Acme::Case! | 2024-11-19T08:13:37 |
Akamai-PropertyFetcher | SHINGO | Akamaiプãƒãƒ‘ティã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãƒãƒ¼ã‚¸ãƒ§ãƒ³æƒ…å ±ã‚’å–å¾—ã™ã‚‹ãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ« | 2024-11-04T08:09:49 |
Algorithm-Gutter | JMATES | cellular automata to simulate rain in a gutter | 2024-11-25T15:59:02 |
Alien-DuckDB | PERIGRIN | Find or build DuckDB | 2024-11-26T03:41:37 |
Alien-cargo | PLICEASE | Find or download the cargo command (build system and package manager for Rust) | 2024-11-24T00:06:19 |
Alien-cargo-capi | PLICEASE | Find or build the cargo capi command | 2024-11-24T23:56:49 |
Alien-cargo-clone | PLICEASE | Find or build the cargo clone command | 2024-11-24T02:33:51 |
Alien-raylib5 | PERIGRIN | Alien distribution for raylib video game engine, version 5 and above | 2024-11-23T06:04:11 |
Amazon-SQS-Client | BIGFOOT | Perl classes for interacting with Amazon SQS | 2024-11-27T23:07:47 |
App-PasswordManager | OLOOEEZ | Simple password manager for adding, listing, editing, deleting, and copying passwords to the clipboard | 2024-11-26T12:46:39 |
App-Stouch | SAMYOUNG | Simple template file creator | 2024-11-22T02:59:41 |
App-TodoList | OLOOEEZ | Simple command-line to-do list manager written in Perl | 2024-11-25T23:32:50 |
App-coldigits | TULAMILI | TSVファイルの各列が何桁のものが何件あったかを、行列状に示す。 | 2024-11-18T14:35:34 |
App-gapstat | TULAMILI | 改行区切りの数値列に、差分が1を超えるギャップ(間隙)がないかを、チェックする。 | 2024-11-20T15:35:38 |
App-grep-sounds-like | PERLANCAR | Print lines with words that sound like to the specified word | 2024-11-20T05:47:15 |
Archive-Libarchive-Compress | PLICEASE | Recursively archive a directory (using libarchive) | 2024-11-15T19:49:10 |
Business-CAMT | MARKOV | ISO20022 Cash Management (CAMT) messages | 2024-11-25T08:57:16 |
CPANSA-DB | BDFOY | the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit | 2024-11-17T20:32:36 |
Chart-ECharts | GDT | Apache ECharts wrapper for Perl | 2024-11-21T22:24:32 |
CheerLights-API | NOTHANS | A Perl module for accessing the CheerLights API | 2024-11-06T02:28:43 |
Comparer-file_num_links | PERLANCAR | Compare file's number of (hard) links | 2024-11-24T00:06:08 |
Crypt-Bear | LEONT | BearSSL for Perl | 2024-11-12T20:07:54 |
Data-Annotation | POLETTIX | [Put something meaningful here!] | 2024-11-13T22:16:57 |
Data-FastPack-JPacker | DRCLAW | backend class for packing FastPack data files into web loadable JPack | 2024-11-11T00:47:35 |
Data-FastPack | DRCLAW | FastPack Record format, parsing and serialising module | 2024-11-08T04:39:52 |
Data-JPack | DRCLAW | Offline/Online Web application and data system | 2024-11-08T04:40:03 |
Environment-Is | PLICEASE | Detect environments like Docker or WSL | 2024-11-23T20:54:33 |
Eugener | EUGENER | A test package. DOnt install. | 2024-11-26T14:55:51 |
Eugener-test | EUGENER | A test package. DOnt install. | 2024-11-20T13:32:00 |
Eugener_test | EUGENER | A test package. DOnt install. | 2024-11-20T13:34:56 |
Eugenertest | EUGENER | A test package. DOnt install. | 2024-11-20T14:04:58 |
Future-IO-Impl-AnyEvent | MATHIAS | Future::IO implementation using AnyEvent | 2024-11-27T21:09:50 |
Game-EnergyLoop | JMATES | a simple energy system | 2024-11-05T19:13:51 |
Genealogy-FindaGrave | NHORNE | Find URLs on FindaGrave for a person | 2024-11-11T13:01:21 |
Geo-Leaflet | MRDVT | Generates Leaflet web page | 2024-11-09T02:48:31 |
Hash-Iter | PERLANCAR | Generate a coderef iterator for a hash | 2024-11-04T08:09:16 |
HashDataBundle-Display-Resolution | PERLANCAR | HashData::* modules related to Display::Resolution | 2024-11-10T00:05:43 |
Local-Acme | CONTRA | The great new Local::Acme! | 2024-11-19T07:43:34 |
MFab-Plugins-Datadog | DDROWN | Mojolicious plugin for Datadog APM integration | 2024-11-06T16:24:14 |
Mail-Colander | POLETTIX | Categorize and manage email messages | 2024-11-28T06:49:09 |
Mail-Sieve | POLETTIX | Categorize and manage email messages | 2024-11-16T10:11:54 |
Math-NumberBase-XS | ZARABOZO | Lighting fast number converter from one base to another base | 2024-11-11T05:30:35 |
Math-Symbolic-Custom-Polynomial | MJOHNSON | Polynomial routines for Math::Symbolic | 2024-11-27T17:41:18 |
Mojo-UserAgent-Role-TotalTimeout | KARJALA | Role for Mojo::UserAgent that enables setting total timeout including redirects | 2024-11-06T09:41:19 |
Music-Dice | GENE | Define and roll musical dice | 2024-11-30T22:57:09 |
PDL-Complex | ETJ | handle complex numbers (DEPRECATED – use native complex) | 2024-11-26T02:13:32 |
PDL-Graphics-Limits | ETJ | derive limits for display purposes | 2024-11-25T23:17:09 |
PDL-IO-Browser | ETJ | 2D data browser for PDL | 2024-11-26T00:27:38 |
PDL-Perldl2 | ETJ | Simple shell (version 2) for PDL | 2024-11-26T02:48:28 |
Perl-Version-Bumper | BOOK | Update use VERSION on any Perl code | 2024-11-20T08:48:15 |
Pongo | HAPPYBEAR | A Perl MongoDB interface using XS and the MongoDB C driver. | 2024-11-29T05:14:14 |
Random-Simple | BAKERSCOT | Simple, usable, real world random numbers | 2024-11-08T23:59:53 |
RecentInfo-Manager | CORION | 2024-11-03T07:32:19 | |
Result-Simple | KFLY | A dead simple perl-ish Result like F#, Rust, Go, etc. | 2024-11-23T09:23:32 |
STIX | GDT | Structured Threat Information Expression (STIX) | 2024-11-18T11:00:07 |
Standup-Diary | SMONFF | Manage a simple journal in Markdown files | 2024-11-23T14:08:21 |
Str-Iter | PERLANCAR | Generate a coderef iterator to iterate a string one (or more) character(s) at a time | 2024-11-17T00:06:14 |
Template-Plexsite | DRCLAW | Class for interlinked templating | 2024-11-12T03:59:54 |
Test2-Tools-ComboObject | PLICEASE | Combine checks and diagnostics into a single test as an object | 2024-11-22T07:22:27 |
UserAgent-Any | MATHIAS | Wrapper above any UserAgent library, supporting sync and async calls. | 2024-11-15T21:01:30 |
UserAgent-Any-JSON | MATHIAS | Specialization of UserAgent::Any for JSON APIs. | 2024-11-16T20:16:37 |
_Acme-Local | CONTRA | The great new _Acme::Local! | 2024-11-19T07:35:37 |
eugener-test | EUGENER | A test package. DOnt install. | 2024-11-20T12:58:19 |
Number of new CPAN distributions this period: 65
Number of authors releasing new CPAN distributions this period: 33
Authors by number of new CPAN distributions this period:
No | Author | Distributions |
---|---|---|
1 | PERLANCAR | 6 |
2 | PLICEASE | 6 |
3 | EUGENER | 5 |
4 | ETJ | 4 |
5 | CONTRA | 4 |
6 | DRCLAW | 4 |
7 | POLETTIX | 3 |
8 | MATHIAS | 3 |
9 | OLOOEEZ | 2 |
10 | GDT | 2 |
11 | PERIGRIN | 2 |
12 | TULAMILI | 2 |
13 | JMATES | 2 |
14 | LEONT | 1 |
15 | ZARABOZO | 1 |
16 | NOTHANS | 1 |
17 | SMONFF | 1 |
18 | BOOK | 1 |
19 | BIGFOOT | 1 |
20 | KFLY | 1 |
21 | HAPPYBEAR | 1 |
22 | MRDVT | 1 |
23 | NHORNE | 1 |
24 | DDROWN | 1 |
25 | SHINGO | 1 |
26 | GENE | 1 |
27 | SAMYOUNG | 1 |
28 | BAKERSCOT | 1 |
29 | BDFOY | 1 |
30 | MARKOV | 1 |
31 | CORION | 1 |
32 | MJOHNSON | 1 |
33 | KARJALA | 1 |
Published on Tuesday 03 December 2024 00:05
Today, on “Giving Tuesday”, The Perl and Raku Foundation (TPRF) is extremely pleased to announce a donation of $25,000 from DuckDuckGo. Since 2011, DuckDuckGo has donated over 6 million dollars to organizations that align with their “vision of raising the standard of trust online”.
TPRF is dedicated to advancing the Perl and Raku programming languages through open-source development, community engagement, and outreach to ensure their ongoing growth, relevance, and accessibility. This support from DuckDuckGo will allow TPRF to continue to do things like fund core Perl development, fund grants for projects which are important to the community and otherwise to support the community where it can.
Every donation sends a message that this work matters. On the occasion of this gift, The Perl and Raku Foundation is deeply grateful to DuckDuckGo not only for its generosity but also for its confidence in TPRF’s mission.
To learn more about TPRF’s current activities and goals for the future, please see the TPRF 2024 prospectus
If your organization is interested in becoming a TPRF donor, please reach out to me. I’d be more than happy to set up a call so that we can discuss how a donor relationship with TPRF can benefit your organization.
Welcome to “What’s new on CPAN”, a curated look at last month’s new CPAN uploads for your reading and programming pleasure. Enjoy!
getaddrinfo
Published on Sunday 01 December 2024 23:34
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
You are given an array of binary numbers, @binary.
Write a script to return the maximum length of a contiguous subarray with an equal number of 0 and 1.
Let’s not concern ourselves with any particular efficiencies and just analyze all the subsets of the given numbers!
The main loop for iterating over all the contiguous sub-arrays.
We really just need one subroutine to co-ordinate the inputs and run the main loop that’s required.
Putting it all together...
The rest of the code just runs some simple tests.
MAIN:{
say contiguous_array 1, 0;
say contiguous_array 0, 1, 0;
say contiguous_array 0, 0, 0, 0, 0;
say contiguous_array 0, 1, 0, 0, 1, 0;
}
◇
Fragment referenced in 3.
$ perl perl/ch-1.pl 2 2 0 4
You are given permutation of $n integers, @ints. Write a script to find the minimum number of swaps needed to make the @ints a semi-ordered permutation.
A permutation is called semi-ordered if the first number is 1 and the last number equals n. That means we need to count the number of swaps needed to move 1 to the beginning and $n to the end.
At first thought, I wonder if there’s a catch? Does the swapping end up moving 1 or $n further from its destination? That doesn’t seem to be the case even if they were adjacent to each other. That is because they are moving in opposite directions. 1 is always moving left and $n is always moving right. Although they may swap with each other it will always be of benefit to them both.
Let’s start with the code for swapping left and and right to move 1 and $n in their respective directions.
We’ll put everything together in a subroutine.
The rest of the code drives some tests.
MAIN:{
say semi_ordered 2, 1, 4, 3;
say semi_ordered 2, 4, 1, 3;
say semi_ordered 1, 3, 2, 4, 5;
}
◇
Fragment referenced in 9.
$ perl perl/ch-2.pl 2 3 0
Published by Maud Dollie Perry on Sunday 01 December 2024 21:59
Published by Unknown on Sunday 01 December 2024 16:06
Published by Mayur Koshti on Wednesday 27 November 2024 13:38
Published on Monday 25 November 2024 00:31
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
You are given a string of alphabetic characters, $chars. Write a script to compress the string with run-length encoding.
A compressed unit can be either a single character or a count followed by a character.
BONUS: Write a decompression function.
After working so much with recursion for the previous challenge, TWC 295, this time around we’ll use a simple loop mechanism available in Perl: a redo block.
The main loop for iterating over the characters one by one.
Here’s a subroutine which co-ordinates encoding: splits the string, invokes the loop, and returns the compressed format.
The BONUS seems to be fairly doable. Given an encoded string we can expand it back to the original by a similar process as the encoding. In fact, let’s use the same sort of loop.
As before we’ll define a subroutine which co-ordinates decoding.
Putting it all together...
The rest of the code just runs some simple tests.
MAIN:{
say encoding q/abbc/;
say encoding q/aaabccc/;
say encoding q/abcc/;
say q//;
say decoding encoding q/abbc/;
say decoding encoding q/aaabccc/;
say decoding encoding q/abcc/;
}
◇
Fragment referenced in 6.
$ perl perl/ch-1.pl a2bc 3ab3c ab2c abbc aaabccc abcc
You are given an array of integers, @ints. Write a script to find if it is possible to make one square using the sticks as in the given array @ints where $ints[$i] is the length of ith stick.
First let’s notice that the lengths must all sum to a number evenly divisible by four, so that’ll be an initial filter on the list. If that first test passes we divide the sum by four (to get the side length) and determine if we can get four subsets which all sum to that side length.
If we have a sum of lengths evenly divisible by four we’ll then check if if we have four subsets which sum to the computed side length. To do this we’ll compute the powerset (all subsets) of the list and check the sums.
The rest of the code drives some tests.
MAIN:{
say boolean is_square 1, 2, 2, 2, 1;
say boolean is_square 2, 2, 2, 4;
say boolean is_square 2, 2, 2, 2, 4;
say boolean is_square 3, 4, 1, 4, 3, 1;
}
◇
Fragment referenced in 12.
$ perl perl/ch-2.pl 1 0 0 1
Published on Sunday 24 November 2024 19:22
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
You are given a string, $str, and list of words, @words.
Write a script to return true or false whether the given string can be segmented into a space separated sequence of one or more words from the given list.
This can be brute forced rather easily: just try every concatenated combination from the list and see if we get a match. It is not too much work to add a bit more efficiency though. Our approach will be:
Since there may be many words which start with the same letter we will use a recursive implementation which will make sure to check each of them.
Here’s how we construct the hash of words keyed by their first letter. Nothing especially clever, just an ordinary loop over the words.
The biggest chunk of word is here in this subroutine, where we recursively explore all possibilities of matching words. If at any point we find all components of the string we return true. In cases where the string cannot be composed of words from the list then the recursion just simply ends and, by default, returns undef.
Here’s a subroutine which co-ordinates everything: invokes the hash construction and recursion. The boolean function is used to make sure something nicely printable is returned.
Putting it all together...
The rest of the code just runs some simple tests.
MAIN:{
say word_break q/weeklychallenge/, [q/challenge/, q/weekly/];
say word_break q/perlrakuperl/, [q/raku/, q/perl/];
say word_break q/sonsanddaughters/,[q/sons/, q/sand/, q/daughters/];
}
◇
Fragment referenced in 4.
$ perl perl/ch-1.pl 1 1 0
You are given an array of integers, @ints. Write a script to find the minimum number of jumps to reach the last element. $ints[$i] represents the maximum length of a forward jump from the index $i. In case last element is unreachable then return -1.
We always start at the array index 0. From there we can recursively explore the possible paths that may be taken. Keep in mind that at each step $i we can only move as many as $ints[$i] positions.
In many ways this is similar to the first part of this week’s challenge!
If at any point we detect we have reached list’s end then we save the number of moves made. At the end we sort the list of moves and return the smallest number of moves. If this list is empty then we return -1.
The rest of the code drives some tests.
MAIN:{
say jump_game 2, 3, 1, 1, 4;
say jump_game 2, 3, 0, 4;
say jump_game 2, 0, 0, 4;
}
◇
Fragment referenced in 9.
$ perl perl/ch-2.pl 2 2 -1