<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Bob Lied</title>
    <description>The latest articles on Forem by Bob Lied (@boblied).</description>
    <link>https://forem.com/boblied</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F470960%2F3925166c-2844-4880-92af-2dfede66665b.jpeg</url>
      <title>Forem: Bob Lied</title>
      <link>https://forem.com/boblied</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/boblied"/>
    <language>en</language>
    <item>
      <title>PWC 367 Overlapping Oddities</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Sun, 05 Apr 2026 14:15:46 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-367-oddity-ln3</link>
      <guid>https://forem.com/boblied/pwc-367-oddity-ln3</guid>
      <description>&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-367/#TASK1" rel="noopener noreferrer"&gt;Task 1, Maximum Odd Binary&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It's the week of the &lt;a href="https://en.wikipedia.org/wiki/Artemis_II" rel="noopener noreferrer"&gt;Artemis 2&lt;/a&gt; mission to the moon, and we have a problem about odd numbers. I'd call that a &lt;a href="https://www.youtube.com/watch?v=iYYRH4apXDo" rel="noopener noreferrer"&gt;Space Oddity&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task Description
&lt;/h3&gt;

&lt;p&gt;You are given a binary string that has at least one ‘1’.&lt;/p&gt;

&lt;p&gt;Write a script to rearrange the bits in such a way that the resulting binary number is the maximum odd binary number and return the resulting binary string. The resulting string can have leading zeros.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example 1:&lt;/strong&gt; &lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$str = "1011"&lt;/code&gt;, &lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;"1101"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example 2:&lt;/strong&gt; &lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$str = "100"&lt;/code&gt;, &lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;"001"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example 3:&lt;/strong&gt; &lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$str = "111000"&lt;/code&gt;, &lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;"110001"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example 4:&lt;/strong&gt; &lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$str = "0101"&lt;/code&gt;,&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;"1001"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example 5:&lt;/strong&gt; &lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$str = "1111"&lt;/code&gt;, &lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;"1111"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Task Cogitation
&lt;/h3&gt;

&lt;p&gt;To be as large as possible, a binary number must have all its 1s in the most-significant bits. To be odd, the least significant bit must be a one. So, what has to happen is that all the 1s shift to the left, one shifts to the right, and all the zeroes form a space between them. &lt;/p&gt;

&lt;p&gt;I see three possible solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sort: sort the bits so that the 1s are on the left, the 0s on the right, and then rotate a 1 into the right-most position.&lt;/li&gt;
&lt;li&gt;Split: make a pass over the bits, creating a pile of 1s and a pile of 0s. Form the required output string from the piles.&lt;/li&gt;
&lt;li&gt;Count: There's no need to move the bits around. Count how many 1s there are. Conjure a string of 1s, and a string of 0s.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Task Implementation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Solution 1: Sorting
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;mobSort&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt; &lt;span class="ow"&gt;cmp&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;@bits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;shift&lt;/span&gt; &lt;span class="nv"&gt;@bits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;("",&lt;/span&gt; &lt;span class="nv"&gt;@bits&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sort in descending order to put 1s on the left&lt;/li&gt;
&lt;li&gt;Rotate by taking the bit on the left and pushing it onto the right.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Solution 2: Splitting
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;mobSplit&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@bit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;"",&lt;/span&gt; &lt;span class="p"&gt;""&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$bit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt; &lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$bit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$bit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I want a group of 0s and a group of 1s. That's an array of size two, conveniently indexed by &lt;code&gt;[0]&lt;/code&gt; and &lt;code&gt;[1]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For each 0 or 1, append to its string.&lt;/li&gt;
&lt;li&gt;To form the output, use the four-argument form of &lt;code&gt;substr&lt;/code&gt;, which replaces a substring. In the string of 1s, insert the string of 0s just before the zero-length string in front of the last bit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Solution 3: Counting
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;mobCount&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$zero&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;tr/0/0/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;$zero&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;$zero&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The output will be the same length as the input.&lt;/li&gt;
&lt;li&gt;The number of zeroes can be counted by the &lt;a href="https://perldoc.perl.org/perlfaq4#How-can-I-count-the-number-of-occurrences-of-a-substring-within-a-string?" rel="noopener noreferrer"&gt;Perl FAQ idiom&lt;/a&gt; of using the &lt;code&gt;tr///&lt;/code&gt; operator -- it returns the number of substitutions.&lt;/li&gt;
&lt;li&gt;Now we know how any 1s and 0s we need, so replicate them appropriately.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Task Benchmark
&lt;/h3&gt;

&lt;p&gt;It's too trivial a problem to be concerned about performance, but let's benchmark it anyway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;               Rate   sorting splitting  counting
sorting    128205/s        --      -28%      -95%
splitting  178571/s       39%        --      -93%
counting  2500000/s     1850%     1300%        --
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not surprised that sorting is slowest (it's an O(&lt;em&gt;n_log_n&lt;/em&gt;) algorithm). I am a little surprised how much faster counting is.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-367/#TASK2" rel="noopener noreferrer"&gt;Task 2, Conflict Events&lt;/a&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Task Description
&lt;/h2&gt;

&lt;p&gt;You are given two events' start and end time.&lt;/p&gt;

&lt;p&gt;Write a script to find out if there is a conflict between the two events. A conflict happens when two events have some non-empty intersection.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example 1:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Input: &lt;code&gt;@event1 = ("10:00", "12:00")&lt;/code&gt; &lt;code&gt;@event2 = ("11:00", "13:00")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Output: true&lt;/li&gt;
&lt;li&gt;Both events overlap from "11:00" to "12:00".&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 2:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Input: &lt;code&gt;@event1 = ("09:00", "10:30")&lt;/code&gt; &lt;code&gt;@event2 = ("10:30", "12:00")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Output: false&lt;/li&gt;
&lt;li&gt;Event1 ends exactly at 10:30 when Event2 starts.
Since the problem defines intersection as non-empty, exact boundaries touching is not a conflict.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 3:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Input: &lt;code&gt;@event1 = ("14:00", "15:30")&lt;/code&gt; &lt;code&gt;@event2 = ("14:30", "16:00")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Output: true&lt;/li&gt;
&lt;li&gt;Both events overlap from 14:30 to 15:30.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Example 4:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input: &lt;code&gt;@event1 = ("08:00", "09:00")&lt;/code&gt; &lt;code&gt;@event2 = ("09:01", "10:00")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Output: false&lt;/li&gt;
&lt;li&gt;There is a 1-minute gap from "09:00" to "09:01".&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Example 5:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input: &lt;code&gt;@event1 = ("23:30", "00:30")&lt;/code&gt; &lt;code&gt;@event2 = ("00:00", "01:00")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Output: true&lt;/li&gt;
&lt;li&gt;They overlap from "00:00" to "00:30".&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Task Cogitation
&lt;/h3&gt;

&lt;p&gt;The string form of time is annoying to work with. The first thing to do is to convert the time to a minute of the day. As we learned last week, there are 1440 minutes in a day. And as we learned long ago, there are &lt;a href="https://www.youtube.com/watch?v=XNbwiGbETSQ" rel="noopener noreferrer"&gt;525,600 minutes&lt;/a&gt; in a year, but that's not relevant. Stay focused.&lt;/p&gt;

&lt;p&gt;Once we have the times converted, then we have a problem in detecting overlapping ranges. I know how to do that. The examples highlight the edge cases.&lt;/p&gt;

&lt;p&gt;Example 5 throws a curve: the ranges can cross midnight. There will be math involving modulo operators.&lt;/p&gt;

&lt;p&gt;I'm going to break this down into small pieces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task Solution
&lt;/h3&gt;

&lt;p&gt;What I'm going for is: does the range of event 1 overlap with the range of event 2? That means I need a function to see if ranges overlap, which means I need a function to convert times to ranges, which means I need functions to convert time strings to numbers. Let's pop that stack.&lt;/p&gt;

&lt;h4&gt;
  
  
  Subtask: Time conversion
&lt;/h4&gt;

&lt;p&gt;Very straightforward. I'm assuming times have been validated and are in the specified format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;toMinute&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;hhmm&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="nv"&gt;$hhmm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$minute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$m&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$h&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$minute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Subtask: Event range
&lt;/h4&gt;

&lt;p&gt;Here's where we isolate the problem of spanning midnight. We figure out how long the event is, using a 24-hour clock modulo operation. Then, replace the beginning of the range by subtracting the duration from the end. That means we might get a negative number for the beginning, but that's okay -- it represents a time before midnight.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;toRange&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;minBeg&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;minEnd&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$minEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$minBeg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;1440&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$minEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$minEnd&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Subtask: Range overlap
&lt;/h4&gt;

&lt;p&gt;Given two ranges, whether they overlap depends on where their edges line up.&lt;/p&gt;

&lt;p&gt;I like &lt;code&gt;use enum&lt;/code&gt; as a way to introduce a little readability. It's a very efficent module, and quite flexible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;isOverlap&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;range1&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;range2&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;enum&lt;/span&gt; &lt;span class="sx"&gt;qw( BEG=0 END=1 )&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$range1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$range2&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;BEG&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$range1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;BEG&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$range2&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Composition of the solution
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;isConflict&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;event1&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;event2&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;isOverlap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;toRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;toMinute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;$event1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
                     &lt;span class="nv"&gt;toRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;toMinute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;$event2&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Given all the pieces, the solution falls into place.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;map { toMinute() "&lt;/code&gt; -- convert the time strings into pairs of numbers that we can do arithmetic with.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;toRange( map {} )&lt;/code&gt; - convert pairs of points in time into ranges that might span midnight.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isOverlap(...)&lt;/code&gt; - the answer is the result of checking for range overlap.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>perl</category>
      <category>perlweeklychallenge</category>
      <category>pwc</category>
    </item>
    <item>
      <title>PWC 366, Task 2, Valid Times</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Fri, 27 Mar 2026 14:31:07 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-366-task-2-valid-times-38h5</link>
      <guid>https://forem.com/boblied/pwc-366-task-2-valid-times-38h5</guid>
      <description>&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-366/#TASK2" rel="noopener noreferrer"&gt;PWC 366 Task 2, Valid Times&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Here we are at another Weekly Challenge, &lt;a href="https://www.youtube.com/watch?v=Qr0-7Ds79zo" rel="noopener noreferrer"&gt;ticking away the moments&lt;/a&gt; that make up a dull day. Fritter and waste the hours in an off-hand way. Kicking around on a piece of ground in your home town, waiting for someone or something to show you the way. Here's a way.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Requirements Phase
&lt;/h3&gt;

&lt;p&gt;You are given a time in the form ‘HH:MM’. The earliest possible time is ‘00:00’ and the latest possible time is ‘23:59’. In the string time, the digits represented by the ‘?’ symbol are unknown, and must be replaced with a digit from 0 to 9. Write a script to return the count of different ways we can make it a valid time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example 1&lt;/strong&gt;: &lt;em&gt;Input&lt;/em&gt;: &lt;code&gt;$time = "?2:34"&lt;/code&gt;, &lt;em&gt;Output&lt;/em&gt;: &lt;code&gt;3&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;02:34, 12:34, 22:34&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 2&lt;/strong&gt;: &lt;em&gt;Input&lt;/em&gt;: &lt;code&gt;$time = "?4:?0"&lt;/code&gt;, &lt;em&gt;Output&lt;/em&gt;: &lt;code&gt;12&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Combinations of hours 04 and 14, with minutes 00, 10, 20, 30, 40, 50&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 3&lt;/strong&gt;: &lt;em&gt;Input&lt;/em&gt;: &lt;code&gt;$time = "??:??"&lt;/code&gt;, &lt;em&gt;Output&lt;/em&gt;: &lt;code&gt;1440&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 4&lt;/strong&gt;: &lt;em&gt;Input&lt;/em&gt;: &lt;code&gt;$time = "?3:45"&lt;/code&gt;, &lt;em&gt;Output&lt;/em&gt;: &lt;code&gt;3&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;03:45, 13:45, 23:45&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 5&lt;/strong&gt;: &lt;em&gt;Input&lt;/em&gt;: &lt;code&gt;$time = "2?:15"&lt;/code&gt;, &lt;em&gt;Output&lt;/em&gt;: &lt;code&gt;4&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;20:15, 21:15, 22:15, 23:15&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Design Phase
&lt;/h3&gt;

&lt;p&gt;My first thought is that it would be fun to make some kind of iterator or generator that replaces each &lt;code&gt;?&lt;/code&gt; with its valid possibilities. My second thought is, "Nah, that seems like a lot of work." And since the cardinal virtue of Perl programming is laziness, I moved on to something simpler.&lt;/p&gt;

&lt;p&gt;There are 24 valid hours and 60 valid minutes. The total valid combinations is the cross-product of the two. Let's go with that.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Implementation Phase
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;validTime&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;state&lt;/span&gt; &lt;span class="nv"&gt;@hour&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;00&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;23&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
    &lt;span class="nv"&gt;state&lt;/span&gt; &lt;span class="nv"&gt;@minute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;00&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;59&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;

    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="sr"&gt;s/\?/./gr&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/:/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$time&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="sr"&gt;/$h/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;@hour&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="sr"&gt;/$m/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;@minute&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;state&lt;/code&gt; variables -- These reference lists only need to be set up once, and only in the scope of this function. That's what &lt;code&gt;state&lt;/code&gt; does.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;'00' .. '23'&lt;/code&gt; -- The &lt;code&gt;..&lt;/code&gt; sequence operator has the nice feature that if you want leading zeroes, you get them. Looking at you, &lt;code&gt;bash&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;split(/:/, $time)&lt;/code&gt; -- Divide the input into its components. As in so many weekly challenge tasks, I'm assuming that the input has already been sanitized and is showing up here in a valid form.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;map { ... }&lt;/code&gt; -- Do something to both the hours and the minutes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s/\?/./gr&lt;/code&gt; -- The something is to replace &lt;code&gt;?&lt;/code&gt; with &lt;code&gt;.&lt;/code&gt; so that it becomes a regular expression. The &lt;code&gt;?&lt;/code&gt; is a meta-character, so it needs to be quoted. Adding the &lt;code&gt;r&lt;/code&gt; flag yields the modified string; otherwise &lt;code&gt;s///g&lt;/code&gt; would yield the number of substitutions, which is not useful in this context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;my ($h, $m) = ...&lt;/code&gt; -- Declaring and initializing two variables from a list of two things.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;(grep /$h/, @hour)&lt;/code&gt; -- Select a list of the valid hours that can match the &lt;code&gt;$h&lt;/code&gt; pattern. This is in a scalar context (numerical multiplication), so the scalar value will result -- the number of matches. Similarly for minutes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We only need the count, so multiply the two. We could have generated the list of valid times by using the hours and minutes returned from &lt;code&gt;grep&lt;/code&gt; in array context, but (I believe I already mentioned this), lazy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Delivery Phase
&lt;/h3&gt;

&lt;p&gt;And there you have it. You run and you run to catch up with the sun, but it's sinking, and racing around to come up behind you again. The sun is the same in a relative way, but you're older, shorter of breath, and one day closer to death. But at least you solved weekly challenge 366.&lt;/p&gt;

</description>
      <category>perl</category>
      <category>perlweeklychallenge</category>
      <category>pwc</category>
    </item>
    <item>
      <title>PWC 366 Count Prefixes: Could We Start Again, Please</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Fri, 27 Mar 2026 13:37:30 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-366-count-prefixes-could-we-start-again-please-19kc</link>
      <guid>https://forem.com/boblied/pwc-366-count-prefixes-could-we-start-again-please-19kc</guid>
      <description>&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-366/#TASK1" rel="noopener noreferrer"&gt;PCW 366, Task 1, Count Prefix&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;So many prefixes, starting from the beginning over and over. It reminds me of &lt;a href="https://www.youtube.com/watch?v=I4lfETL2fm4" rel="noopener noreferrer"&gt;"Could We Start Again, Please?"&lt;/a&gt; from &lt;em&gt;Jesus Christ, Superstar&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Task
&lt;/h3&gt;

&lt;p&gt;You are given an array of words and a string (contains only lowercase English letters). Write a script to return the number of words in the given array that are a prefix of the given string.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example 1:&lt;/strong&gt; 

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input&lt;/em&gt;: &lt;code&gt;@array = ("a", "ap", "app", "apple", "banana")&lt;/code&gt;, &lt;code&gt;$str = "apple"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;4&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 2:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input&lt;/em&gt;: &lt;code&gt;@array = ("cat", "dog", "fish")&lt;/code&gt;, &lt;code&gt;$str = "bird"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Output: &lt;code&gt;0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 3&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input&lt;/em&gt;: &lt;code&gt;@array = ("hello", "he", "hell", "heaven", "he")&lt;/code&gt;, &lt;code&gt;$str = "hello"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output&lt;/em&gt;: &lt;code&gt;4&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 4&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input&lt;/em&gt;: &lt;code&gt;@array = ("", "code", "coding", "cod")&lt;/code&gt;, &lt;code&gt;$str = "coding"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output&lt;/em&gt;: &lt;code&gt;3&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 5&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input&lt;/em&gt;: &lt;code&gt;@array = ("p", "pr", "pro", "prog", "progr", "progra", "program")&lt;/code&gt;, &lt;code&gt;$str = "program"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output&lt;/em&gt;: &lt;code&gt;7&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  An Obvious Solution Using a Regular Expression
&lt;/h3&gt;

&lt;p&gt;The first thing that springs to mind is to do a pattern match of each string in &lt;code&gt;@array&lt;/code&gt; against the string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;countPrefix&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;scalar&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;m/^$_/&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;@*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because of the way Perl passes array arguments, I've chosen to invert the order and pass the scalar string first. Then, I further chose to pass an array reference instead of a copy of the array, for premature optimization.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;grep&lt;/code&gt; returns different things depending on whether it's in scalar context or array context. I want the scalar value always (the count of the number of matches).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Less-obvious Solution Using a Regular Expression
&lt;/h3&gt;

&lt;p&gt;Another way to approach the problem is to look at it from the point of view of the string. What are all the possible prefixes? Then see if any of the &lt;code&gt;@array&lt;/code&gt; values is in that set of alternatives. The programming task in this solution is to build a regular expression that matches every possible prefix.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;onerex&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$regex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;|&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;scalar&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="sr"&gt;/^(?:$regex)$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;@*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;0 .. length(str)&lt;/code&gt; -- generates the sequence of possible prefix lengths, including the empty string (one of the given examples implies that an empty string should be considered a valid prefix).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;map { substr($str, 0, $_) }&lt;/code&gt; -- For each length, extract the prefix of that length, giving an array of increasingly longer strings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;join('|', ...)&lt;/code&gt; -- Combine the strings into a set of alternatives that can be used in a regular expression.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;$array-&amp;gt;@*&lt;/code&gt; -- Post-fix notation for de-referencing the array. I like it; Perl classic would probably use &lt;code&gt;@$array&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/^(...)$/&lt;/code&gt; -- The potential prefixes must be anchored at the start and end to exactly match one of the alternatives in &lt;code&gt;$regex&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/^(?:...)/&lt;/code&gt; -- We need the parentheses for grouping, but we don't need to extract the pattern match. The &lt;code&gt;?:&lt;/code&gt; assertion does that; it saves the overhead of extracting the match and then ignoring it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Solution Using String Compare
&lt;/h3&gt;

&lt;p&gt;All of the strings involved in this task are constants; there's no meta-characters or repetitions that require a regular expression. It could be done with string compares, and that would probably be more efficient.  Let's find out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;precmp&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;scalar&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="ow"&gt;eq&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;@*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implicitly loop over &lt;code&gt;@array&lt;/code&gt;. For each possible prefix, extract the same length of string from the front of &lt;code&gt;$str&lt;/code&gt; and see if they match exactly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Benchmark
&lt;/h2&gt;

&lt;p&gt;Let's see how the solutions compare. I'll make up a string of modest length, and an array with a lot of words.  For the purpose of the benchmark, I don't really care how many prefixes are found.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;runBenchmark&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;Benchmark&lt;/span&gt; &lt;span class="sx"&gt;qw/cmpthese/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abcdefghijklmnopqrstuvwxy&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
  &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="sx"&gt;qw(lorem ipsum dolor sit amet consectetur adipiscing elit e tiam elit neque venenatis ut dolor consectetur iaculis venenatis arcu in in terdum mauris eget neque venenatis dapibus sed finibus varius sapien quis mal esuada fusce libero augue vulputate sit amet faucibus at feugiat at l ectus maecenas vehicula sem metus nullam lobortis massa et est vulputate f acilisis in tempus arcu metus vitae aliquam nibh sodales ac sed vitae era t nec nisl laoreet fringilla aliquam et lobortis purus ut a tristique nisi nec molest ie arcu duis mattis ultrices nisl eget consectetur donec ac lo rem non augue bibendum tincidunt interdum quis velit in gravida metus vitae)&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;


    &lt;span class="nv"&gt;cmpthese&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$repeat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;regex&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;countPrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="s"&gt;substr&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;precmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="s"&gt;onerex&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;onerex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now for the unveil:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;           Rate  regex onerex substr
regex    8636/s     --   -73%   -95%
onerex  31447/s   264%     --   -83%
substr 185185/s  2044%   489%     --
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The solution with simple string compares wins by blowout. This is not surprising to me. The overhead of setting up regular expressions and doing their evaluation is mostly unnecessary for this task, and the string compares are highly optimized operations in Perl.&lt;/p&gt;

</description>
      <category>perl</category>
      <category>perlweeklychallenge</category>
      <category>pwc</category>
    </item>
    <item>
      <title>PWC 362 Echo Chamber</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Thu, 26 Feb 2026 15:34:51 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-362-echo-chamber-1dn1</link>
      <guid>https://forem.com/boblied/pwc-362-echo-chamber-1dn1</guid>
      <description>&lt;p&gt;The musical accompaniment must obviously be Pink Floyd's &lt;a href="https://www.youtube.com/watch?v=KBca3xf-j3o" rel="noopener noreferrer"&gt;Echoes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Task
&lt;/h2&gt;

&lt;p&gt;You are given a string containing lowercase letters.&lt;/p&gt;

&lt;p&gt;Write a script to transform the string based on the index position of each character (starting from 0). For each character at position i, repeat it i + 1 times.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Example 1&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input&lt;/em&gt;: &lt;code&gt;"abca"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output&lt;/em&gt;: &lt;code&gt;"abbcccaaaa"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Index 0: "a" -&amp;gt; repeated 1 time  -&amp;gt; "a"&lt;br&gt;
Index 1: "b" -&amp;gt; repeated 2 times -&amp;gt; "bb"&lt;br&gt;
Index 2: "c" -&amp;gt; repeated 3 times -&amp;gt; "ccc"&lt;br&gt;
Index 3: "a" -&amp;gt; repeated 4 times -&amp;gt; "aaaa"&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Thoughts, Both Profound and Superficial
&lt;/h2&gt;

&lt;p&gt;A string transformation problem, an ideal domain for Perl. There are at least three approaches I can see.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Treat it as a regular expression problem, substituting each character for &lt;em&gt;n&lt;/em&gt; copies of itself. It wouldn't be Perl if at least one of the more-than-one-way-do-its didn't have more punctuation than letters.&lt;/li&gt;
&lt;li&gt;Treat it as a list problem. Separate the string into a list of characters and map each list element into &lt;em&gt;n&lt;/em&gt; copies of itself.&lt;/li&gt;
&lt;li&gt;Treat it as a string generation problem. Build a new string out of bits of the original.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In any case, the linchpin is going to be the &lt;a href="https://perldoc.perl.org/perlop#Multiplicative-Operators" rel="noopener noreferrer"&gt;repetition operator, &lt;code&gt;x&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Solutions
&lt;/h2&gt;
&lt;h3&gt;
  
  
  A Regular Expression Substition
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;echoRE&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;English&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;s/(.)/$1 x $LAST_MATCH_END[1]/g&lt;/span&gt;&lt;span class="nv"&gt;er&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Several features of Perl regular expressions come into play.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We need to do some math in the substitution to replicate the characters. To put executable code into the substitution, we use the &lt;em&gt;/e&lt;/em&gt; flag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We need the index of the character we're replacing. Perl regular expressions have many useful side effects that are available in special variables. When using a capture group, the indexes of the beginning and end of the matching text are available in the &lt;code&gt;@-&lt;/code&gt; and &lt;a href="https://perldoc.perl.org/perlvar#@LAST_MATCH_END" rel="noopener noreferrer"&gt;&lt;code&gt;@+&lt;/code&gt; special array variables&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The default result of a &lt;code&gt;s///g&lt;/code&gt; global substitution is the number of replacements made, which can be useful in some contexts, but not this one. The &lt;em&gt;/r&lt;/em&gt; flag changes the behavior so that it results in the modified string instead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I'm conceding a tiny bit to readability. &lt;code&gt;use English&lt;/code&gt; lets me use &lt;code&gt;$LAST_MATCH_END&lt;/code&gt; in place of &lt;code&gt;$+&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the breakdown of willing the regular expression into existence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s///gr&lt;/code&gt; -- It's going to be a global substituion (every character), and we want the result.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s/(.)//gr&lt;/code&gt; -- Capture every character, no matter what it is.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s/(.)/$1 x .../gre&lt;/code&gt; -- Replicate the captured character the appropriate number of times. Add the &lt;code&gt;e&lt;/code&gt; flag to execute this expression.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s/(.)/$1 x $+[1]/gre&lt;/code&gt; -- After a visit to the Perl references (just to spite AI, I used an actual book of genuine paper), we find that the offset of the captured character is in the &lt;code&gt;@-&lt;/code&gt; and &lt;code&gt;@+&lt;/code&gt; special variables. Using &lt;code&gt;@+&lt;/code&gt; (end of the match) instead of &lt;code&gt;@-&lt;/code&gt; takes care of the &lt;code&gt;index+1&lt;/code&gt; annoyance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As evidenced by the step above, I can't remember the special variables, so I add &lt;code&gt;use English&lt;/code&gt; to get the name &lt;code&gt;LAST_MATCH_END&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  A List Solution
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;echoMap&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;join&lt;/span&gt; &lt;span class="p"&gt;"",&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This solution turns each character into a longer string of repeated characters. It maps indexes into a corresponding string of the correct length.&lt;/p&gt;
&lt;h3&gt;
  
  
  A String-building solution
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;echoStr&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;"";&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;indexed&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A variation on the list processing, this one builds a string with repeated concatenation. Instead of building a list with &lt;code&gt;map&lt;/code&gt; and then &lt;code&gt;join&lt;/code&gt;-ing it into a result, this one goes straight to the string.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;split&lt;/code&gt; gives me the individual characters as a list. It's more efficient than repeated calls to &lt;code&gt;substr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since we need both the character and its index, I relied on my favorite variation of the &lt;code&gt;for&lt;/code&gt; loop using &lt;code&gt;indexed&lt;/code&gt; to conveniently give me variable names.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Comparison
&lt;/h2&gt;

&lt;p&gt;As always, I'm curious about the relative efficiency of the solutions. Here's a benchmark result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;           Rate    regex  mapjoin splitstr
regex    2365/s       --     -43%     -57%
mapjoin  4118/s      74%       --     -25%
splitstr 5469/s     131%      33%       --
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm a little surprised that regular expressions didn't fare better. I hypothesize that the addition of executable code in the substitution is a significant cost.&lt;/p&gt;

&lt;p&gt;I'm not surprised that string building won. Iterating over lists and manipulating strings are a core strength of Perl, and those operations have no doubt been optimized to heck over the decades.&lt;/p&gt;

</description>
      <category>perl</category>
      <category>perlweeklychallenge</category>
      <category>pwc</category>
    </item>
    <item>
      <title>PWC 360 Pertaining to a subtlety of sorting</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Wed, 11 Feb 2026 17:05:44 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-360-pertaining-to-a-subtlety-of-sorting-39b4</link>
      <guid>https://forem.com/boblied/pwc-360-pertaining-to-a-subtlety-of-sorting-39b4</guid>
      <description>&lt;p&gt;It's been a while since I commented on a Weekly Challenge solution, but here we are at week 360. Such a useful number. So divisible, so circular. It deserves twenty minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-360/#TASK2" rel="noopener noreferrer"&gt;Task 2: Word Sorter&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The task
&lt;/h3&gt;

&lt;p&gt;You are given a sentence.  Write a script to order words in the given&lt;br&gt;
sentence alphabetically but keep the words themselves unchanged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Example 1 Input: $str = "The quick brown fox"
#           Output: "brown fox quick The"
# Example 2 Input: $str = "Hello    World!   How   are you?"
#           Output: "are Hello How World! you?"
# Example 3 Input: $str = "Hello"
#           Output: "Hello"
# Example 4 Input: $str = "Hello, World! How are you?"
#           Output: "are Hello, How World! you?"
# Example 5 Input: $str = "I have 2 apples and 3 bananas!"
#           Output: "2 3 and apples bananas! have I"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The thoughts
&lt;/h3&gt;

&lt;p&gt;This should be quite simple: split the words, sort them, put them back together. The sort should be case-insensitive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nb"&gt;join&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;lc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;lc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Creeping doubt #1
&lt;/h4&gt;

&lt;p&gt;Is converting to lowercase with &lt;code&gt;lc&lt;/code&gt; the right way to do case-insenstive compares? Not really. Perl has the &lt;code&gt;fc&lt;/code&gt; -- fold-case -- function to take care of subtleties in Unicode. We won't see those in simple ASCII text, but for the full rabbit hole, start with the &lt;a href="https://perldoc.perl.org/functions/fc" rel="noopener noreferrer"&gt;documentation of fc&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creeping doubt #2
&lt;/h4&gt;

&lt;p&gt;Doing the case conversion inside the sort means that we will invoke that every time there's a string comparison, which will be quite redundant. We could (probably?) speed it up by pre-calculating the conversions once.&lt;/p&gt;

&lt;h3&gt;
  
  
  The solution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;sorter&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;join&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt;
        &lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;cmp&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This solution uses the idiom of Schwartzian transformation. Every word gets turned into a pair of [original_word, case_folded_word]. That array of pairs gets sorted, and then we select the original words out of the sorted pairs. This is best read bottom-up.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;split(" ", $str)&lt;/code&gt; -- turns the string into an array of words, where words are loosely defined by being white-space separated.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;map { [$_, fc($_)] }&lt;/code&gt; -- every word turns into a pair: the original, and its case-folded variation. The result is a list of array references.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sort { $a-&amp;gt;[1] cmp $b-&amp;gt;[1] }&lt;/code&gt; -- sort by the case-folded versions. The result is still a list of array references.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;map { $_-&amp;gt;[0] }&lt;/code&gt; -- select the original word from each pair&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;join " "&lt;/code&gt; -- Return a single string, where the words are separated by one space.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Does it blend?
&lt;/h3&gt;

&lt;p&gt;A quick benchmark shows that it is indeed faster to pre-calculate the case folding. This example used a text string of about 60 words.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;           Rate oneline  pre_lc
oneline 32258/s      --    -45%
pre_lc  58140/s     80%      --
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My intuition says that when the strings are much shorter, the overhead of the transform might not offset the gains in the sort, but as is so often true, my intuition is crap. This is the result for a string of five words:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;oneline  470588/s      --    -59%
pre_lc  1142857/s    143%      --
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>perl</category>
      <category>perlweeklychallenge</category>
      <category>pwc</category>
    </item>
    <item>
      <title>PWC 353 To each (array) his own</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Mon, 22 Dec 2025 15:12:56 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-353-to-each-array-his-own-23o8</link>
      <guid>https://forem.com/boblied/pwc-353-to-each-array-his-own-23o8</guid>
      <description>&lt;p&gt;&lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-353/" rel="noopener noreferrer"&gt;This week's challenges&lt;/a&gt; were early and on the easy side, which was appreciated as the holidays loom. The musical interlude this week ignores the challenge tasks in favor of seasonal light jazz; I have Spotify playing "Winter's Songs: A Windham Hill Christmas."&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-353/#TASK1" rel="noopener noreferrer"&gt;Task 1: Max Words&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task
&lt;/h3&gt;

&lt;p&gt;You are given an array of sentences. Write a script to return the maximum number of words that appear in a single sentence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example 1:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;@sentences = ("Hello world", "This is a test", "Perl is great")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; 4&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;An easy one -- an early Xmas gift? Split each sentence into words and count them, then take the maximum. Completely ignoring all the complexities of punctuation and internationalization, it's a one-liner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;maxWords&lt;/span&gt;&lt;span class="err"&gt;(@&lt;/span&gt;&lt;span class="nf"&gt;sentence&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;List::&lt;/span&gt;&lt;span class="nv"&gt;Util&lt;/span&gt; &lt;span class="sx"&gt;qw/max/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;max&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;scalar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;@sentence&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only thing mildly tricky here is transforming the list of words into a count. Inside the body of the &lt;code&gt;map&lt;/code&gt; block, we're in array context, so &lt;code&gt;split&lt;/code&gt; will return a list. Coercing it with &lt;code&gt;scalar&lt;/code&gt; gives the count.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-353/#TASK2" rel="noopener noreferrer"&gt;Task 2: Validate Coupon&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task
&lt;/h3&gt;

&lt;p&gt;You are given three arrays, &lt;code&gt;@codes&lt;/code&gt;, &lt;code&gt;@names&lt;/code&gt; and &lt;code&gt;@status&lt;/code&gt;. Write a script to validate codes in the given array. A code is valid when the following conditions are true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;codes[i]&lt;/code&gt; is non-empty and consists only of alphanumeric characters (&lt;code&gt;a-z, A-Z, 0-9&lt;/code&gt;) and underscores (&lt;code&gt;_&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;names[i]&lt;/code&gt; is one of the following four categories: "electronics", "grocery", "pharmacy", "restaurant".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status[i]&lt;/code&gt; is true.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Return an array of booleans indicating validity: &lt;code&gt;output[i]&lt;/code&gt; is true if and only if &lt;code&gt;codes[i]&lt;/code&gt;, &lt;code&gt;names[i]&lt;/code&gt; and &lt;code&gt;status[i]&lt;/code&gt; are all valid.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example 1:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input&lt;/em&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@codes  = ("A123", "B_456", "C789", "D@1", "E123")&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@names  = ("electronics", "restaurant", "electronics", "pharmacy", "grocery")&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@status = ("true", "false", "true", "true", "true")&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;(true, false, true, false, true)&lt;/code&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Thoughts
&lt;/h3&gt;

&lt;p&gt;The volume of text for this one was daunting at first glance, but it turns out to be mostly example data and actually quite easy.&lt;/p&gt;

&lt;p&gt;The validation is straight-forward. &lt;code&gt;@codes&lt;/code&gt; is a regular expression match; &lt;code&gt;@names&lt;/code&gt; is a keyword lookup from a dictionary table (hash); and &lt;code&gt;@status&lt;/code&gt; is just a string compare.&lt;/p&gt;

&lt;p&gt;We'll be operating on parallel arrays. I see two ways of doing it. First, we could use a variable to loop over the indexes. That's going to yield code with a lot of array subscripts. Or second, we could use &lt;code&gt;each_array&lt;/code&gt; from &lt;code&gt;List::More::Utils&lt;/code&gt; to do the indexing for us. I'm curious which is more efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  To the code-sleigh, Rudolph!
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Loop with &lt;code&gt;indexed&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;valid&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;state&lt;/span&gt; &lt;span class="nv"&gt;%ValidName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;electronics&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;grocery&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="s"&gt;pharmacy&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;restaurant&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@valid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;@*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;indexed&lt;/span&gt; &lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$valid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!~&lt;/span&gt; &lt;span class="sr"&gt;m/[^a-zA-Z0-9_]/&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$valid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;exists&lt;/span&gt; &lt;span class="nv"&gt;$ValidName&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$valid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;=&lt;/span&gt; &lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;eq&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;@valid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Passing multiple arrays as subroutine parameters implies using references. We could use traditional &lt;a href="https://perldoc.perl.org/perlsub#Prototypes" rel="noopener noreferrer"&gt;prototype attributes&lt;/a&gt; to make it possible to pass arrays, but let's not.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;state %ValidName&lt;/code&gt; -- These are valid keywords for the &lt;code&gt;@names&lt;/code&gt; input array. Using &lt;code&gt;state&lt;/code&gt; makes this a static table, initialized only the first time the subroutine is entered.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@valid&lt;/code&gt; -- Our output array, same size as the input array. Assume they're all valid and turn elements false if any check fails.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;indexed&lt;/code&gt; -- My favorite recent Perl improvement. Returns both the index and the value from an array during a &lt;code&gt;for&lt;/code&gt; loop.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;valid-&amp;gt;[$i] &amp;amp;&amp;amp;= ...&lt;/code&gt; -- Any failing test will turn the value to false.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$code-&amp;gt;[$i] !~ m/[^a-zA-Z0-9_]/&lt;/code&gt; -- The code has to consist entirely of valid characters, or conversely, it must &lt;em&gt;not&lt;/em&gt; match (&lt;code&gt;!~&lt;/code&gt;) anything that contains a character other than those. Later I went back to look up exactly what &lt;code&gt;\w&lt;/code&gt; and &lt;code&gt;\W&lt;/code&gt; mean in Perl regular expressions and realized that I could have used that instead of the character classes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exists $ValidName{...}&lt;/code&gt; -- Use &lt;code&gt;exists&lt;/code&gt; explicitly to return a boolean value.  A simple lookup that fails would return &lt;code&gt;undef&lt;/code&gt;, and will make the result of the expression &lt;code&gt;undef&lt;/code&gt; instead of &lt;code&gt;false&lt;/code&gt;. Although &lt;code&gt;undef&lt;/code&gt; is treated as false in many contexts, it failed the unit test.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;return \@valid&lt;/code&gt; -- Return a reference. This is convenient for the  &lt;code&gt;is()&lt;/code&gt; function from the &lt;code&gt;Test2::V0&lt;/code&gt; unit testing framework, and returning a reference also improves performance by avoiding an array copy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Loop with array iterator
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;validEach&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;state&lt;/span&gt; &lt;span class="nv"&gt;%ValidName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;electronics&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;grocery&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="s"&gt;pharmacy&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;restaurant&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@valid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;List::&lt;/span&gt;&lt;span class="nv"&gt;MoreUtils&lt;/span&gt; &lt;span class="sx"&gt;qw/each_arrayref/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$ea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;each_arrayref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ea&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;@valid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$c&lt;/span&gt; &lt;span class="o"&gt;!~&lt;/span&gt; &lt;span class="sr"&gt;m/[^a-zA-Z0-9_]/&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
                  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;exists&lt;/span&gt; &lt;span class="nv"&gt;$ValidName&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
                  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$s&lt;/span&gt; &lt;span class="ow"&gt;eq&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt;             &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;@valid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is basically the same function, except that the explicit indexing has been replaced by an iterator.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;each_arrayref&lt;/code&gt; -- There's an &lt;code&gt;each_array&lt;/code&gt; function, but since we have array references, this one is convenient.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;my $ea = ...&lt;/code&gt; -- Creates an iterator over the three arrays.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ea-&amp;gt;()&lt;/code&gt; -- Call the iterator. &lt;code&gt;ea&lt;/code&gt; is the equivalent of a function pointer, and this is function dereference syntax.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;while ( my ($c, $n, $s) = ea-&amp;gt;() )&lt;/code&gt; -- Each call to the iterator returns a tuple of the next element from each of the &lt;code&gt;$code&lt;/code&gt;, &lt;code&gt;$name&lt;/code&gt;, and &lt;code&gt;$status&lt;/code&gt; arrays.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;push @valid, ...&lt;/code&gt; -- The same three checks as before, but building the list instead of modifying it, because we don't have an index in hand.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Performance check
&lt;/h4&gt;

&lt;p&gt;I think the iterator version is a little cleaner, because it eliminates the clutter of repeated array subscripts. But I was curious how it compared in efficiency, so I set up a quick benchmark using the examples.&lt;/p&gt;

&lt;p&gt;It turns out that the explicit indexing is about twice as efficient as the &lt;code&gt;each_arrayref&lt;/code&gt; iterator. Still gonna use it, though.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ perl ch-2.pl -b 100000
             Rate iterator forindex
iterator  72464/s       --     -47%
forindex 136986/s      89%       --
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To do the benchmarking, I set up the examples as an array of test cases, and used &lt;code&gt;Benchmark::cmpthese&lt;/code&gt; to do a lot of iterations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;runBenchmark&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;Benchmark&lt;/span&gt; &lt;span class="sx"&gt;qw/cmpthese/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;cmpthese&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$repeat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;forindex&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;@case&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nv"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;names&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="s"&gt;iterator&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;@case&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nv"&gt;validEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;names&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the input data looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@case&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Example 1&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
      &lt;span class="s"&gt;codes&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A123&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;B_456&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;      &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C789&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;D@1&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;      &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;E123&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;names&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;electronics&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;restaurant&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;electronics&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pharmacy&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grocery&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;false&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;      &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;     &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;expect&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Example 2&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
      &lt;span class="s"&gt;codes&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Z_9&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;      &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AB_12&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;       &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;G01&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;     &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X99&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;         &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;names&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pharmacy&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;electronics&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grocery&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;electronics&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;     &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;false&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;   &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;expect&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Example 3&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
      &lt;span class="s"&gt;codes&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_123&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;       &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;         &lt;span class="p"&gt;'',&lt;/span&gt;            &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Coupon_A&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alpha&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;names&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;restaurant&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;electronics&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;electronics&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pharmacy&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grocery&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;       &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;false&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;       &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;     &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;expect&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Example 4&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
      &lt;span class="s"&gt;codes&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ITEM_1&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;      &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ITEM_2&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;      &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ITEM_3&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;  &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ITEM_4&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;names&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;electronics&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;electronics&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grocery&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grocery&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;expect&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Example 5&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
      &lt;span class="s"&gt;codes&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CAFE_X&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;     &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ELEC_100&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FOOD_1&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;  &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DRUG_A&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;   &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ELEC_99&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;names&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;restaurant&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;electronics&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grocery&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pharmacy&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;electronics&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;       &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;        &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;    &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;     &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;false&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s"&gt;expect&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>perl</category>
      <category>perlweeklychallenge</category>
      <category>pwc</category>
    </item>
    <item>
      <title>PWC 352 Five is the one-liest number</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Fri, 19 Dec 2025 22:27:21 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-352-five-is-the-one-liest-number-487e</link>
      <guid>https://forem.com/boblied/pwc-352-five-is-the-one-liest-number-487e</guid>
      <description>&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-352/#TASK1" rel="noopener noreferrer"&gt;Task 1: Match String&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task
&lt;/h3&gt;

&lt;p&gt;You are given an array of strings. Write a script to return all strings that are a substring of another word in the given array in the order they occur.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example 1:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;@words = ("cat", "cats", "dog", "dogcat", "dogcat", "rat", "ratcatdogcat")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;("cat", "dog", "dogcat", "rat")&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Deep Thoughts
&lt;/h3&gt;

&lt;p&gt;Example 1 implies a couple of constraints. A complete match counts as a substring. Words may be repeated in the input. Repeated words are not duplicated in the output. The output order must match the input order. Noted.&lt;/p&gt;

&lt;p&gt;For each word, we'll need to take it out of the list, check for substrings, and then put it back for the next round. A relatively simple way of doing that would be to shift it off the front of the list, do what needs to be done, and then return it by pushing it onto the back.&lt;/p&gt;

&lt;p&gt;I don't see much of a way of getting around the idea that every word must be compared to every other word, so we're looking at an O(&lt;em&gt;n&lt;/em&gt;&lt;sup&gt;2&lt;/sup&gt;) performance algorithm.&lt;/p&gt;

&lt;p&gt;One possibility for reducing the operation count might be to compare a pair of words both ways. If I have &lt;code&gt;$w1&lt;/code&gt; and &lt;code&gt;$w2&lt;/code&gt; in hand, I can check both directions for substring-i-ness. That way, instead of having to loop over the entire NxN matrix, we would only have to loop over the upper or lower triangle of the matrix. It's fewer looping operations, but still the same number of string comparisons. But it would mean more complexity in keeping track of what's been checked, so, meh.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;matchString&lt;/span&gt;&lt;span class="err"&gt;(@&lt;/span&gt;&lt;span class="nf"&gt;words&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;List::&lt;/span&gt;&lt;span class="nv"&gt;Util&lt;/span&gt; &lt;span class="sx"&gt;qw/any/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;%seen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@match&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="nv"&gt;@words&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;shift&lt;/span&gt; &lt;span class="nv"&gt;@words&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;@match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$w&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$seen&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$w&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;any&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;@words&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;@words&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$w&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;@match&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a hash (&lt;code&gt;%seen&lt;/code&gt;) to eliminate duplicates, and coincidentally optimize a little by not searching multiple times.&lt;/li&gt;
&lt;li&gt;The main loop is once per word, so simply count. Normally, I would prefer something obviously tied to the array (like &lt;code&gt;for (@words)&lt;/code&gt;), but since I'm changing the array within the loop, I don't want to have to think too hard about whether the iterator remains valid after removing and adding an element.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;index&lt;/code&gt; is a cheaper operation than a regular expression match. Benchmarking bears this out -- it's about three times more efficient in my environment and test data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;List::Util::any&lt;/code&gt; stops as soon as something works, so it's cheaper than &lt;code&gt;grep&lt;/code&gt;, which would evaluate the entire list every time. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-352/#TASK2" rel="noopener noreferrer"&gt;Task 2: Binary Prefix&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task
&lt;/h3&gt;

&lt;p&gt;You are given an array, &lt;code&gt;@nums&lt;/code&gt;, where each element is either 0 or 1.&lt;/p&gt;

&lt;p&gt;Define xi as the number formed by taking the first &lt;code&gt;i+1&lt;/code&gt; bits of &lt;code&gt;@nums&lt;/code&gt; (from &lt;code&gt;$nums[0&lt;/code&gt;] to &lt;code&gt;$nums[i]&lt;/code&gt;) and interpreting them as a binary number, with &lt;code&gt;$nums[0]&lt;/code&gt; being the most significant bit.&lt;/p&gt;

&lt;p&gt;For example: If &lt;code&gt;@nums = (1, 0, 1)&lt;/code&gt;, then:&lt;br&gt;
  x0 = 1 (binary 1)&lt;br&gt;
  x1 = 2 (binary 10)&lt;br&gt;
  x2 = 5 (binary 101)&lt;/p&gt;

&lt;p&gt;For each &lt;em&gt;i&lt;/em&gt;, check whether xi is divisible by 5.&lt;/p&gt;

&lt;p&gt;Write a script to return an array &lt;code&gt;@answer&lt;/code&gt; where &lt;code&gt;$answer[i]&lt;/code&gt; is true if &lt;em&gt;x&lt;/em&gt;i is divisible by 5, otherwise false.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example 1:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Input: &lt;code&gt;@nums = (0,1,1,0,0,1,0,1,1,1)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Output: (true, false, false, false, false, true, true, false, false, false)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Binary numbers formed (decimal values):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;         0:   0  (false)
        01:   1  (false)
       011:   3  (false)
      0110:   6  (false)
     01100:  12  (false)
    011001:  25  ( true)
   0110010:  50  ( true)
  01100101: 101  (false)
 011001011: 203  (false)
0110010111: 407  (false)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Deep Thoughts
&lt;/h3&gt;

&lt;p&gt;Weird flex, but OK.&lt;/p&gt;

&lt;p&gt;This puts me in mind of bit-twiddling with C code, but bit-twiddling in Perl is equally possible. The other alternative is probably to build strings and do binary number conversions by prefixing with '0b'.&lt;/p&gt;

&lt;p&gt;I kind of want to do a list-based solution, using &lt;code&gt;map&lt;/code&gt; to convert each 0 or 1 to a &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;, but I think that would be obfuscating a pretty simple loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;bpre&lt;/span&gt;&lt;span class="err"&gt;(@&lt;/span&gt;&lt;span class="nf"&gt;nums&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@isFive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$bit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;shift&lt;/span&gt; &lt;span class="nv"&gt;@nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;$bit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;@isFive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;@isFive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Destroy the input by shifting a 1 or 0 off the left until the list is gone.&lt;/li&gt;
&lt;li&gt;Do bit operations to form a new number.&lt;/li&gt;
&lt;li&gt;Build an answer array consisting of boolean values.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Musical Interlude
&lt;/h2&gt;

&lt;p&gt;The solution to task 1 reminds me of Katy Perry's &lt;a href="https://www.youtube.com/watch?v=C-jAPrdc3PI" rel="noopener noreferrer"&gt;Hot N Cold&lt;/a&gt; -- "You're yes, then you're no. You're in, then you're out."&lt;/p&gt;

&lt;p&gt;And for Task 2, &lt;a href="https://www.youtube.com/watch?v=is6AYSCWwKM" rel="noopener noreferrer"&gt;Mambo Number 5&lt;/a&gt; is oddly appropriate. "Take one step left and one step right, One to the front and one to the side. Clap your hands once and clap your hands twice, And if it looks like this, then you're doin' it right."&lt;/p&gt;

</description>
      <category>perl</category>
      <category>perlweeklychallenge</category>
      <category>pwc</category>
    </item>
    <item>
      <title>PWC 350 Good Substring / Shuffle Pairs</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Wed, 03 Dec 2025 02:48:23 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-350-good-substring-shuffle-pairs-3gfn</link>
      <guid>https://forem.com/boblied/pwc-350-good-substring-shuffle-pairs-3gfn</guid>
      <description>&lt;h2&gt;
  
  
  Musical Interlude
&lt;/h2&gt;

&lt;p&gt;The movie version of &lt;em&gt;Wicked&lt;/em&gt; is in theaters right now, so I am reminded of the song &lt;a href="https://www.youtube.com/watch?v=Y8YMfgu92hQ" rel="noopener noreferrer"&gt;For Good&lt;/a&gt; -- but I'm gonna link to the Broadway version, because I'm classy like that. It's relevant to programming in Perl because "I don't know if I've been changed for the better, but I have been changed for good."  For part two, &lt;a href="https://www.youtube.com/watch?v=HQZBaJAngH8" rel="noopener noreferrer"&gt;Lido Shuffle&lt;/a&gt; by Boz Scaggs.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-350/#TASK1" rel="noopener noreferrer"&gt;Task 1: Good Substrings&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task
&lt;/h3&gt;

&lt;p&gt;You are given a string.  Write a script to return the number of good substrings of length three in the given string.  A string is good if there are no repeated characters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example 1: Input $str = "abcaefg", Output: 5

&lt;ul&gt;
&lt;li&gt;Good substrings of length 3: abc, bca, cae, aef and efg&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Example 2: Input: $str = "xyzzabc", Output: 3&lt;/li&gt;

&lt;li&gt;Example 3: Input: $str = "aababc", Output: 1&lt;/li&gt;

&lt;li&gt;Example 4: Input: $str = "qwerty",  Output: 4&lt;/li&gt;

&lt;li&gt;Example 5: Input: $str = "zzzaaa",  Output: 0&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Think-y Part
&lt;/h2&gt;

&lt;p&gt;There's probably a regular expression for this, but I'm not going to find it. Do the simplest thing that works: take three characters at a time and see if they're different.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code-y Part
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;goodSubstring&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$good&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="nv"&gt;$#s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$third&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;@s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nv"&gt;$good&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$first&lt;/span&gt; &lt;span class="ow"&gt;ne&lt;/span&gt; &lt;span class="nv"&gt;$second&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$first&lt;/span&gt; &lt;span class="ow"&gt;ne&lt;/span&gt; &lt;span class="nv"&gt;$third&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$second&lt;/span&gt; &lt;span class="ow"&gt;ne&lt;/span&gt; &lt;span class="nv"&gt;$third&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$good&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start by turning the string into a list of characters. It could be done with &lt;code&gt;substr&lt;/code&gt;, but that would be untidy.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@s[$_, $_+1, $_+2]&lt;/code&gt; -- With a nod to readability, I'll extract three consecutive characters with an array slice. It occurs to me that I'll always have two of the next three characters in hand at the bottom of the loop, so doing a complete splice every time could probably be optimized, but I declare it "good" enough. &lt;/li&gt;
&lt;li&gt;Since there's exactly three characters in play, check for uniqueness in the most obvious way.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-350/#TASK2" rel="noopener noreferrer"&gt;Task 2:Shuffle Pairs&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task
&lt;/h3&gt;

&lt;p&gt;If two integers A &amp;lt;= B have the same digits but in different orders, we say that they belong to the same shuffle pair if and only if there is an integer k such that A = B * k. k is called the witness of the pair. For example, 1359 and 9513 belong to the same shuffle pair, because 1359 * 7 = 9513.&lt;/p&gt;

&lt;p&gt;Interestingly, some integers belong to several different shuffle pairs. For example, 123876 forms one shuffle pair with 371628, and another with 867132, as 123876 * 3 = 371628, and 123876 * 7 = 867132.&lt;/p&gt;

&lt;p&gt;Write a function that for a given &lt;code&gt;$from&lt;/code&gt;, &lt;code&gt;$to&lt;/code&gt;, and &lt;code&gt;$count&lt;/code&gt; returns the number of integers &lt;code&gt;$i&lt;/code&gt; in the range &lt;code&gt;$from &amp;lt;= $i &amp;lt;= $to&lt;/code&gt; that belong to at least &lt;code&gt;$count&lt;/code&gt; different shuffle pairs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example 1:

&lt;ul&gt;
&lt;li&gt;Input: $from = 1, $to = 1000, $count = 1&lt;/li&gt;
&lt;li&gt;Output: 0&lt;/li&gt;
&lt;li&gt;There are no shuffle pairs with elements less than 1000.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Example 2:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input: $from = 1500, $to = 2500, $count = 1&lt;/li&gt;
&lt;li&gt;Output: 3&lt;/li&gt;
&lt;li&gt;There are 3 integers between 1500 and 2500 that belong to shuffle pairs.

&lt;ul&gt;
&lt;li&gt;1782, the other element is 7128 (witness 4)&lt;/li&gt;
&lt;li&gt;2178, the other element is 8712 (witness 4)&lt;/li&gt;
&lt;li&gt;2475, the other element is 7425 (witness 3)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Example 3:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input: $from = 1_000_000, $to = 1_500_000, $count = 5&lt;/li&gt;
&lt;li&gt;Output: 2&lt;/li&gt;
&lt;li&gt;There are 2 integers in the given range that belong to 5 different shuffle pairs.

&lt;ul&gt;
&lt;li&gt;1428570 pairs with 2857140, 4285710, 5714280, 7142850, and 8571420&lt;/li&gt;
&lt;li&gt;1429857 pairs with 2859714, 4289571, 5719428, 7149285, and 8579142&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Example 4:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input: $from = 13_427_000, $to = 14_100_000, $count = 2&lt;/li&gt;
&lt;li&gt;Output: 11&lt;/li&gt;
&lt;li&gt;6 integers in the given range belong to 3 different shuffle pairs,&lt;/li&gt;
&lt;li&gt;5 integers belong to 2 different ones.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Example 5:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input: $from = 1030, $to = 1130, $count = 1&lt;/li&gt;
&lt;li&gt;Output: 2&lt;/li&gt;
&lt;li&gt;There are 2 integers between 1020 and 1120 that belong to at least one shuffle pair:

&lt;ul&gt;
&lt;li&gt;1035, the other element is 3105 (witness k = 3)&lt;/li&gt;
&lt;li&gt;1089, the other element is 9801 (witness k = 9)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deliberations
&lt;/h3&gt;

&lt;p&gt;It takes a minute to digest this one.&lt;/p&gt;

&lt;p&gt;I first wondered if there's some algebraic number theory trick that would cut the search space way down, but that made my head hurt, so I moved to doing what computers do best: grinding through a lot of possibilities.&lt;/p&gt;

&lt;p&gt;A bad first thought was to try all combinations of the digits, but that's going to die an excruciating slow death on the crucifix of combinatorics, not to mention that we'd be completely wasting our time on all but a few combinations.&lt;/p&gt;

&lt;p&gt;A better thought is to look only at the multiples of a given number. There are at most 8 multiples of a number in play: the 10th would add a digit, and therefore can't possibly be a reordering. Examples 3 and 4 have a lot of numbers to grind through, but how long can it take, really? It's one banana, Michael; how much could it cost, ten dollars?&lt;/p&gt;

&lt;p&gt;How will I decide that a number is a re-ordering? I think I'll reduce each number to a canonical form where the digits are sorted, then use string compare to see if a multiple has the same canonical form.&lt;/p&gt;

&lt;h3&gt;
  
  
  To the bat-editor, Robin!
&lt;/h3&gt;

&lt;p&gt;First, a little function to turn a number into a canonical form with its digits in sorted order. Turn the number into a list of digits, sort, and then join the digits back into a string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;canonical&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;n&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;("",&lt;/span&gt; &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the main course. I'll want to examine every number in the range &lt;code&gt;$from&lt;/code&gt; to &lt;code&gt;$to&lt;/code&gt;, inclusive.  For each number, I'll want to examine its multiples to see if they have the same digits. I need to count the ones that work so that I can check that there are at least &lt;code&gt;$count&lt;/code&gt; of them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;shufflePair&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$n&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$from&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="nv"&gt;$to&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;canonical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$pair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$multiple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;$multiple&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$max&lt;/span&gt;
                 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multiple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multiple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;canonical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$multiple&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;eq&lt;/span&gt; &lt;span class="nv"&gt;$base&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nv"&gt;$pair&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nv"&gt;$answer&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;$pair&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$answer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;my $base = canonical($n)&lt;/code&gt; -- hang on to this for comparison.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;my $max = (9 x length($n))+0;&lt;/code&gt; -- An optimization. The maximum number we need to be concerned with is one that has the same number of digits, but is all 9s.  For example, if &lt;code&gt;$n&lt;/code&gt; is 480, then we are dealing with 3-digit numbers, so the largest possible is 999. That's less than 480*3=1440, so we don't have to examine any of the multiples beyond 480*2.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;for ( 2..9 )&lt;/code&gt; -- These are the only multiples of &lt;code&gt;$n&lt;/code&gt; that could possibly have the same number of digits.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;next if ...&lt;/code&gt; -- Besides the check on &lt;code&gt;$max&lt;/code&gt;, we can make cheap checks on a single digit. If the first or last digit isn't one of the possible digits, we can avoid the overhead of &lt;code&gt;canonical()&lt;/code&gt;, which isn't horrendous, but it does involve allocating lists and a sort.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;canonical($multiple) eq $base&lt;/code&gt; -- This string compare is where we decide if we have a shuffle pair.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$answer++ if $pair &amp;gt;= $count&lt;/code&gt; -- We increment the answer if this number has at least &lt;code&gt;$count&lt;/code&gt; shuffle pairs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This solution takes a few seconds to run the examples. My optimizations to bail early in many cases cut the run time approximately in half (from about 8 seconds to about 4.5).&lt;/p&gt;

</description>
      <category>perl</category>
      <category>perlweeklychallenge</category>
      <category>pwc</category>
    </item>
    <item>
      <title>PWC 349 More complex than it has to be</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Wed, 26 Nov 2025 03:32:52 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-349-more-complex-than-it-has-to-be-2c7e</link>
      <guid>https://forem.com/boblied/pwc-349-more-complex-than-it-has-to-be-2c7e</guid>
      <description>&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-349/#TASK2" rel="noopener noreferrer"&gt;PWC 349 Task 2: Meeting Point&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Musical Interlude
&lt;/h3&gt;

&lt;p&gt;We're going deep cut from the classic &lt;em&gt;Blood on the Tracks&lt;/em&gt; album by Bob Dylan, &lt;a href="https://www.youtube.com/watch?v=VE6-uc1zr3s" rel="noopener noreferrer"&gt;Meet Me in the Morning&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Task
&lt;/h3&gt;

&lt;p&gt;You are given an instruction string made up of U (up), D (down), L (left) and R (right). Write a script to return true if following the instructions, you meet (0,0) at any point along the sequence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Example 1:&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$path = "ULD"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;U -&amp;gt; (0,1), L -&amp;gt; (-1,1), D -&amp;gt; (-1, 0)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;em&gt;Example 2:&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$path = "ULDR"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;U -&amp;gt; (0,1), L -&amp;gt; (-1,1), D -&amp;gt; (-1, 0), R -&amp;gt; (0,0)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;em&gt;Example 5:&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$path = "RRUULLDDRRUU"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;RRUULLDD -&amp;gt; (0,0), RRUU -&amp;gt; (2,2)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Deliberation
&lt;/h3&gt;

&lt;p&gt;The first thought is emulate the movement on the grid and see if we end up at (0,0).&lt;/p&gt;

&lt;p&gt;A clever thought arises: to end up back where we began, every Up must be matched with a Down, and every Right must be matched with a Left. So all we really have to do is count moves, right?&lt;/p&gt;

&lt;p&gt;But Example 5 throws a wrench. The path makes a big square and passes through (0,0), but doesn't end there.  I guess I really do need to check every move in the path.&lt;/p&gt;

&lt;p&gt;Second clever thought (this one might be worth keeping): moving around a grid can be like moving in the complex plane. A horizontal move is along the real axis, and a vertical move is along the imaginary axis. If we think of the location as a complex number, then a move is an increment of either the real or the imaginary part.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Nice try, no cigar
&lt;/h4&gt;

&lt;p&gt;Let's dispense with what didn't work, because it looked good until we hit Example 5.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;meet&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;scalar&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;m/$_/g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sx"&gt;qw(U D L R)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We'll try to count the number of each direction and see if Ups cancel Downs and Rights cancel Lefts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;my @m = map {...} qw(U D L R)&lt;/code&gt; -- for each move, map to the number of occurrences of that move in &lt;code&gt;$path&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$path =~ m/$_/g&lt;/code&gt; -- &lt;code&gt;$_&lt;/code&gt; is one of (U,D,L,R). The regular expression match with a &lt;code&gt;g&lt;/code&gt; flag can return a list of all the occurrences of that letter in &lt;code&gt;$path&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scalar(()= ...)&lt;/code&gt; -- assigning the match to an empty list will create an array context, so that &lt;code&gt;m//&lt;/code&gt; will yield a list. Then taking the &lt;code&gt;scalar&lt;/code&gt; of that will give a count. This is a variant of the &lt;code&gt;=()=&lt;/code&gt; &lt;a href="https://metacpan.org/dist/perlsecret/view/lib/perlsecret.pod#Goatse" rel="noopener noreferrer"&gt;Saturn secret operator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Now it only remains to check if the numbers align.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Moving on to something that works
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;meetComplex&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Math::&lt;/span&gt;&lt;span class="nv"&gt;Complex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;state&lt;/span&gt; &lt;span class="nv"&gt;$origin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;*i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;state&lt;/span&gt; &lt;span class="nv"&gt;%move&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;R&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;*i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;L&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;*i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="s"&gt;U&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nv"&gt;*i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;D&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nv"&gt;*i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$place&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$origin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;uc&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$place&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nv"&gt;$move&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;$place&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$origin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;use Math::Complex&lt;/code&gt; -- Yes, Perl does complex arithmetic.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;state $origin = (0 + 0*i)&lt;/code&gt; -- Set up a "constant" for the origin. &lt;code&gt;state&lt;/code&gt; makes a variable that is initialized only the first time the function is entered, and limits the scope to the function. &lt;code&gt;Math::Complex&lt;/code&gt; makes it possible to write complex constants in a natural way.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;state %move&lt;/code&gt; -- Set up "constants" for movements. Moving Right and Left affects the real part of the complex number; moving Up and Down affects the imaginary part.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;for ( split(...) )&lt;/code&gt; -- Take the path apart to process each move. I threw in an &lt;code&gt;uc&lt;/code&gt; to make sure we would only have uppercase input.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$place += $move{$_}&lt;/code&gt; -- For each move, change &lt;code&gt;$place&lt;/code&gt; in the complex plane. &lt;code&gt;Math::Complex&lt;/code&gt; overloads operators as expected.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;return true if $place == $origin&lt;/code&gt; -- Complex numbers can be compared, of course.&lt;/li&gt;
&lt;li&gt;If we didn't find (0,0), then the answer is &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>perl</category>
      <category>perlweeklychallenge</category>
      <category>pwc</category>
    </item>
    <item>
      <title>PWC 348 String Alike, Convert Time</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Wed, 19 Nov 2025 03:18:12 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-348-string-alike-convert-time-3nf9</link>
      <guid>https://forem.com/boblied/pwc-348-string-alike-convert-time-3nf9</guid>
      <description>&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-348/#TASK1" rel="noopener noreferrer"&gt;PWC 348 Task 1: String Alike&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task
&lt;/h3&gt;

&lt;p&gt;You are given a string of even length. Write a script to find out whether the given string can be split into two halves of equal lengths, each with the same non-zero number of vowels.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$str = "textbook"&lt;/code&gt; t&lt;b&gt;e&lt;/b&gt;xt | b&lt;b&gt;oo&lt;/b&gt;k (1, 2)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; false&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$str = "book"&lt;/code&gt; b&lt;b&gt;o&lt;/b&gt; | &lt;b&gt;o&lt;/b&gt;k (1, 1)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; true&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$str = "AbCdEfGh"&lt;/code&gt; &lt;b&gt;A&lt;/b&gt;bCd | &lt;b&gt;E&lt;/b&gt;fGh (1, 1)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; true&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Example 4:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$str = "rhythmmyth"&lt;/code&gt; rhyth | mmyth (0, 0)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; false&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Example 5:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$str = "UmpireeAudio"&lt;/code&gt; &lt;b&gt;U&lt;/b&gt;mp&lt;b&gt;i&lt;/b&gt;r&lt;b&gt;e&lt;/b&gt; | &lt;b&gt;eAu&lt;/b&gt;d&lt;b&gt;io&lt;/b&gt; (3, 5)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; false&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Thoughts
&lt;/h3&gt;

&lt;p&gt;Two steps are required: dividing the string in half, and counting vowels in each half. Let's do the obvious thing with &lt;code&gt;substr&lt;/code&gt; to get the halves, and we can exploit the quirks of &lt;code&gt;tr&lt;/code&gt; to count.&lt;/p&gt;

&lt;p&gt;The examples give us a couple of important test cases: the string can contain uppercase and lowercase; and the string might not have any vowels at all. Also notice that vowels are strictly "aeiou" -- "y" is not counted as a vowel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;strAlike&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;lc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$mid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;tr/aeiou//&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$mid&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$mid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lc($str)&lt;/code&gt; -- eliminate uppercase letters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$mid = ...&lt;/code&gt; -- assuming even length given, as specified&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;substr($str, 0, mid)&lt;/code&gt; -- the left half&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;substr($str, $mid)&lt;/code&gt; -- the right half (third argument not needed). The two &lt;code&gt;substr&lt;/code&gt; calls result in a list of two strings, which we will pass to ...&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@count = map { }&lt;/code&gt; -- count vowels in each substring and assign into an array of vowel counts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$_ =~ tr/aeiou//&lt;/code&gt; -- Ah, &lt;a href="https://perldoc.perl.org/perlop#Transliteration-Quote-Like-Operators" rel="noopener noreferrer"&gt;&lt;code&gt;tr&lt;/code&gt;&lt;/a&gt;. A quirk of &lt;code&gt;tr&lt;/code&gt; is that it returns a count of replacements made. Replace all the vowels of interest, and there's our count. Another quirk of &lt;code&gt;tr&lt;/code&gt; is that the first argument is &lt;em&gt;not&lt;/em&gt; a regular expression, so there are no brackets for character class here. Note the literal use of &lt;code&gt;aeiou&lt;/code&gt;. Yet another quirk of &lt;code&gt;tr&lt;/code&gt; is that there is no variable interpolation, so it would be awkward to try to parameterize this. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;return ...&lt;/code&gt; -- The description specifies non-zero counts, so the extra condition is needed to make Example 4 work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-348/#TASK2" rel="noopener noreferrer"&gt;PWC 348 Task 2: Convert Time&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task
&lt;/h3&gt;

&lt;p&gt;You are given two strings, &lt;code&gt;$source&lt;/code&gt; and &lt;code&gt;$target&lt;/code&gt;, containing time in 24-hour time form. Write a script to convert the source into target by performing one of the following operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add  1 minute&lt;/li&gt;
&lt;li&gt;Add  5 minutes&lt;/li&gt;
&lt;li&gt;Add 15 minutes&lt;/li&gt;
&lt;li&gt;Add 60 minutes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Find the total operations needed to get to the target.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example 1:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$source = "02:30"&lt;/code&gt; &lt;code&gt;$target = "02:45"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;1&lt;/code&gt; (Add 15 minutes)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 2:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$source = "11:55"&lt;/code&gt; &lt;code&gt;$target = "12:15"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;2&lt;/code&gt; (Add 15 minutes, Add 5 minutes)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 3:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$source = "09:00"&lt;/code&gt; &lt;code&gt;$target = "13:00"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;4&lt;/code&gt; (Add 60 minutes four times)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 4:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$source = "23:45"&lt;/code&gt; &lt;code&gt;$target = "00:30"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;3&lt;/code&gt; (Add 15 minutes three times)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example 5:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;$source = "14:20"&lt;/code&gt; &lt;code&gt;$target = "15:25"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;2&lt;/code&gt; (Add 60 minutes, Add 5 minutes)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Thoughts
&lt;/h3&gt;

&lt;p&gt;This is a variation on the problem of making change from a set of coins, or finding the combination of stamps to pay postage.&lt;/p&gt;

&lt;p&gt;We need to find the difference in minutes from &lt;code&gt;$source&lt;/code&gt; to &lt;code&gt;$target&lt;/code&gt;. Example 4 shows that we might have to cross midnight.&lt;/p&gt;

&lt;p&gt;I could take the hours and minutes apart and do the math. But I think I'd rather use well-tested modules, so I'll use &lt;code&gt;Time::Piece&lt;/code&gt; to parse the times and find the difference.&lt;/p&gt;

&lt;p&gt;The operation count will be taking the biggest number of chunks possible, so in reverse order: 60, 15, 5, 1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;source&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;target&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Time::&lt;/span&gt;&lt;span class="nv"&gt;Piece&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Time::&lt;/span&gt;&lt;span class="nv"&gt;Piece&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%H:%M&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Time::&lt;/span&gt;&lt;span class="nv"&gt;Piece&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%H:%M&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$t&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$min&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$min&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$period&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$min&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nv"&gt;$period&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$min&lt;/span&gt; &lt;span class="o"&gt;%=&lt;/span&gt; &lt;span class="nv"&gt;$period&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;strptime()&lt;/code&gt; -- parse the time strings and create a &lt;code&gt;Time::Piece&lt;/code&gt; object. Without any day information, this will be a time on January 1, 1970, but we don't care.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;($t-$s)-&amp;gt;minutes&lt;/code&gt; -- &lt;code&gt;Time::Piece&lt;/code&gt; objects can be subtracted to get a duration. That duration is actually a &lt;code&gt;Time::Seconds&lt;/code&gt; object, which has a method that returns the duration in minutes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;if ( $min &amp;lt; 0 )&lt;/code&gt; -- This covers the case where &lt;code&gt;$target&lt;/code&gt; is after midnight. We'll add 24 hours of minutes to wrap it around back to positive.&lt;/li&gt;
&lt;li&gt;The rest is math. Reduce by hours, then quarter-hours, then 5-minute blocks. Since the last block is 1 minute, this will always eventually terminate.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>perl</category>
      <category>perlweeklychallenge</category>
      <category>pwc</category>
    </item>
    <item>
      <title>PWC 345: I Went to the Mountains</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Thu, 30 Oct 2025 13:28:42 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-345-i-went-to-the-mountains-43i0</link>
      <guid>https://forem.com/boblied/pwc-345-i-went-to-the-mountains-43i0</guid>
      <description>&lt;p&gt;I found the two tasks this week straight-forward and oddly specific, respectively.&lt;/p&gt;

&lt;p&gt;Music: the task titles this week are "Peak Positions" and "Last Visitor", which led me to the refrain "I went to the mountains." It's a reference to &lt;a href="https://youtu.be/HUgwM1Ky228" rel="noopener noreferrer"&gt;Closer to Fine&lt;/a&gt; by the Indigo Girls, which references Perl philosophy: "There's more than one answer to these questions pointing me in a crooked line, and the less I seek my source for some definitive, the closer I am to fine."&lt;/p&gt;

&lt;p&gt;However, for "Peak Positions", let's go with &lt;a href="https://www.youtube.com/watch?v=Gq-Ufl_dNwk" rel="noopener noreferrer"&gt;Ain't No Mountain High Enough&lt;/a&gt; by Marvin Gaye; and for "Last Visitor", with it's odd back and forth, how about the venerable &lt;a href="https://www.youtube.com/watch?v=rblYSKz_VnI" rel="noopener noreferrer"&gt;Hello, Goodbye&lt;/a&gt; by the Beatles.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-345/#TASK1" rel="noopener noreferrer"&gt;Task 1: Peak Position&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The task
&lt;/h3&gt;

&lt;p&gt;You are given an array of integers, &lt;code&gt;@ints&lt;/code&gt;. Find all the peaks in the array. A peak is an element that is strictly greater than its left and right neighbors. Return the indices of all such peak positions.&lt;/p&gt;

&lt;h3&gt;
  
  
  The thought process
&lt;/h3&gt;

&lt;p&gt;My first thought was to use &lt;code&gt;List::MoreUtils::indexes&lt;/code&gt; to test each element, but the edges make it weird. One way or another, they will have to be handled separately. Maybe put a fake element on each end? Do them first and then process the rest? Or test for edges while looping?&lt;/p&gt;

&lt;p&gt;My mental model is that the edges of the list fall away into an abyss, so anything outside the list should be below the altitudes given at the edge positions. I suppose it's equally valid to assume that the list is surrounded by huge ice walls (winter is coming).&lt;/p&gt;

&lt;p&gt;There are a couple of unspecified anomalies, for which I'm going to take executive decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Empty list: return an empty list&lt;/li&gt;
&lt;li&gt;Single element: that's one peak at index 0&lt;/li&gt;
&lt;li&gt;Ramp up: (e.g. 7,8,9) the rightmost element is the only peak&lt;/li&gt;
&lt;li&gt;Ramp down: (e.g. 6,5,4) the left-most element is the only peak&lt;/li&gt;
&lt;li&gt;Plateau: (e.g., 7,7,7) No peak, return an empty list. This contradicts my decision for a single element (a plateau of one). Do I contradict myself? Very well then, I contradict myself. (I am large, I contain multitudes.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;peakPos&lt;/span&gt;&lt;span class="err"&gt;(@&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@peak&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;indexed&lt;/span&gt; &lt;span class="nv"&gt;@int&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$left&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;     &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$int&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$#int&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$int&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nb"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;@peak&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$left&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$right&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;@peak&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once again I chose &lt;code&gt;indexed&lt;/code&gt; (anointed as non-experimental in Perl 5.40) for concise and efficient array traversal.&lt;/li&gt;
&lt;li&gt;Each element has a left neighbor and a right neighbor. For the edges, I'm going to conjure up the missing neighbor and make it less, assuming that outside the list is a bottomless void. You know the one -- the one we scream into while we're on mute during Zoom calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-345/#TASK2" rel="noopener noreferrer"&gt;Task 2: Last Visitor&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The task
&lt;/h3&gt;

&lt;p&gt;You are given an integer array &lt;code&gt;@ints&lt;/code&gt; where each element is either a positive integer or -1. We process the array from left to right while maintaining two lists:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@seen&lt;/code&gt;: stores previously seen positive integers (newest at the front)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@ans&lt;/code&gt;: stores the answers for each -1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If $ints[i] is a positive number -&amp;gt; insert it at the front of &lt;a class="mentioned-user" href="https://dev.to/seen"&gt;@seen&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;If $ints[i] is -1:&lt;/li&gt;
&lt;li&gt;Let $x be how many -1s in a row we’ve seen before this one.&lt;/li&gt;
&lt;li&gt;If $x &amp;lt; len(&lt;a class="mentioned-user" href="https://dev.to/seen"&gt;@seen&lt;/a&gt;) -&amp;gt; append seen[x] to &lt;a class="mentioned-user" href="https://dev.to/ans"&gt;@ans&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Else -&amp;gt; append -1 to &lt;a class="mentioned-user" href="https://dev.to/ans"&gt;@ans&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;At the end, return &lt;a class="mentioned-user" href="https://dev.to/ans"&gt;@ans&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The thought process
&lt;/h3&gt;

&lt;p&gt;This task description is highly proscriptive. I'm not sure there's anything better to do here than to translate the text into Perl statements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;lastVisitor&lt;/span&gt;&lt;span class="err"&gt;(@&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@ans&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;# Count of consecutive -1s&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;@int&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nb"&gt;unshift&lt;/span&gt; &lt;span class="nv"&gt;@seen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;@seen&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;@ans&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;@ans&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;@ans&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes: I don't think there's much to add. There's a couple of basic language features being exploited.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;for (@int)&lt;/code&gt; -- use the implicit &lt;code&gt;$_&lt;/code&gt; variable for array elements to avoid having to think of a name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unshift&lt;/code&gt; -- put things on the front of a list&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@seen&lt;/code&gt; -- in scalar context gives the size of the array (no need for a &lt;code&gt;length()&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only thing tricky here is incrementing and resetting &lt;code&gt;$x&lt;/code&gt; so that it correctly counts the consecutive occurrences of -1. Because there was a possibility of error, I of course made that error, but unit tests straightened me out. &lt;/p&gt;

</description>
      <category>perl</category>
      <category>pwc</category>
      <category>perlweeklychallenge</category>
    </item>
    <item>
      <title>PWC 344, Task 2: Pick Up the Pieces</title>
      <dc:creator>Bob Lied</dc:creator>
      <pubDate>Thu, 23 Oct 2025 14:51:34 +0000</pubDate>
      <link>https://forem.com/boblied/pwc-344-task-2-pick-up-the-pieces-3391</link>
      <guid>https://forem.com/boblied/pwc-344-task-2-pick-up-the-pieces-3391</guid>
      <description>&lt;h2&gt;
  
  
  &lt;a href="https://theweeklychallenge.org/blog/perl-weekly-challenge-344/#TASK2" rel="noopener noreferrer"&gt;Task 2: Array Formation&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task
&lt;/h3&gt;

&lt;p&gt;You are given two lists: &lt;code&gt;@source&lt;/code&gt; and &lt;code&gt;@target&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Write a script to see if you can build the exact &lt;code&gt;@target&lt;/code&gt; by putting these smaller lists from &lt;code&gt;@source&lt;/code&gt; together in some order. You cannot break apart or change the order inside any of the smaller lists in &lt;code&gt;@source&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;@source=([2,3], [1], [4])&lt;/code&gt;, &lt;code&gt;@target=(1, 2, 3, 4)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use in the order: [1], [2,3], [4]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;@source=([1,3], [2,4])&lt;/code&gt;, &lt;code&gt;@target=(1, 2, 3, 4)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;@source=([9,1], [5,8], [2])&lt;/code&gt;, &lt;code&gt;@target=(5, 8, 2, 9, 1)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use in the order: [5,8], [2], [9,1]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example 4:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;@source=([1], [3])&lt;/code&gt;, &lt;code&gt;@target=(1, 2, 3)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Missing number: 2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example 5:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Input:&lt;/em&gt; &lt;code&gt;@source=([7,4,6])&lt;/code&gt;, &lt;code&gt;@target=(7, 4, 6)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use in the order: [7, 4, 6]&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Thought Process
&lt;/h3&gt;

&lt;p&gt;The musical selection for our programming journey is the funky disco-adjacent &lt;a href="https://www.youtube.com/watch?v=DyjlxsJEknc" rel="noopener noreferrer"&gt;Pick Up the Pieces&lt;/a&gt;, by Average White Band. It was a number one hit in 1974, a time when it was still possible for an instrumental tune to make the charts. Fun personal anecdote: in my high school, the English department partnered with a local radio station (KYVA AM 1230 in Gallup, New Mexico) to put on a 15-minute show on Thursday evenings, and "Pick Up the Pieces" was our intro music.&lt;/p&gt;

&lt;p&gt;Nostalgia aside, with a small number of lists in &lt;code&gt;@source&lt;/code&gt;, it's tempting to just plow through all the possible combinations and see if any work, but factorial complexity is scary.&lt;/p&gt;

&lt;p&gt;There's a little bit of ambiguity in the description. Although the examples imply it, it's not clear if we need to use all the substrings in &lt;code&gt;@source&lt;/code&gt;, or if we can accept a solution that only uses some of them. I'm going with not necessarilly using all of the lists in &lt;code&gt;@source&lt;/code&gt; if we don't need them.&lt;/p&gt;

&lt;p&gt;What about reusing them? I'm going to say no to that.&lt;/p&gt;

&lt;p&gt;The solution I lean toward is to use a stack of possibilities and backtrack if we can't satisfy the &lt;code&gt;@target&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Does it blend?
&lt;/h4&gt;

&lt;p&gt;Let's start with the sub-problem of checking if one of the lists in &lt;code&gt;@source&lt;/code&gt; is a prefix of &lt;code&gt;@target&lt;/code&gt;. I want a predicate function that answers whether a list &lt;code&gt;p&lt;/code&gt; is a leading prefix of a list &lt;code&gt;t&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;isPrefix&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;p&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;@$p&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;@$t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;indexed&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$match&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$t&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$match&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our arguments are array references, so we'll be de-referencing them a lot.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;$p&lt;/code&gt; is longer than &lt;code&gt;$t&lt;/code&gt;, then it can't possibly be a prefix.&lt;/li&gt;
&lt;li&gt;Start by assuming that the answer is yes.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://perldoc.perl.org/builtin#indexed" rel="noopener noreferrer"&gt;&lt;code&gt;indexed&lt;/code&gt;&lt;/a&gt; builtin in Perl is a convenient way to get variables for the index and the thing at the index in a simple syntax. Love it.&lt;/li&gt;
&lt;li&gt;For each element in the prefix, &lt;code&gt;$match&lt;/code&gt; continues to be true if the same position &lt;code&gt;$t&lt;/code&gt; matches. We could bail from the loop as soon as &lt;code&gt;$match&lt;/code&gt; becomes false, but with expected short lists, it might be faster to complete the loop than to insert more logic into each execution. No, I'm not going to benchmark for it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Does it match?
&lt;/h4&gt;

&lt;p&gt;On to the main event. I want to pick lists from &lt;code&gt;@source&lt;/code&gt; that match the beginning of &lt;code&gt;@target&lt;/code&gt;. Then, I remove that from consideration, both from &lt;code&gt;@source&lt;/code&gt; and from the front of &lt;code&gt;@target&lt;/code&gt;. Repeat.&lt;/p&gt;

&lt;p&gt;If we hit a dead end, I want to be able to backtrack to an earlier point, so I'll stack my possibilities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;canMake&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="nf"&gt;source&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;target&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$source&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;@stack&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;@*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;indexed&lt;/span&gt; &lt;span class="nv"&gt;$s&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="nv"&gt;isPrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$t&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;@*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;# Make a copy of remaining target.&lt;/span&gt;
            &lt;span class="nb"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;@$p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;# Remove prefix from target.&lt;/span&gt;

            &lt;span class="c1"&gt;# Have we completely matched the target?&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;@t&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$s&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;@*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;# Make a copy of remaining source.&lt;/span&gt;
            &lt;span class="nb"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;# Remove the segment we used.&lt;/span&gt;

            &lt;span class="nb"&gt;push&lt;/span&gt; &lt;span class="nv"&gt;@stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;@s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="nv"&gt;@t&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;  &lt;span class="c1"&gt;# Try again with smaller sets.&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Although the task description uses arrays, I'm passing in array references.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@stack = ( [ ..., ...] )&lt;/code&gt; -- Every element of &lt;code&gt;@stack&lt;/code&gt; is a pair: the current set of possible lists, and the current target list. The stack starts with a copy of &lt;code&gt;@source&lt;/code&gt; and a copy of &lt;code&gt;@target&lt;/code&gt;. There's a lot of references here, and I want to operate on copies, not accidentally change things by going through references.&lt;/li&gt;
&lt;li&gt;The main loop continues as long as there are things to try on the stack. There will be at least one iteration, because we explicitly pushed one possibility.&lt;/li&gt;
&lt;li&gt;Take the top thing off the stack, and assign the anonymous references to variables we can use. &lt;code&gt;$s&lt;/code&gt; will be a reference to the possible source lists, and &lt;code&gt;$t&lt;/code&gt; will be a reference to the remaining target list that we have to match.&lt;/li&gt;
&lt;li&gt;Oh look, it's &lt;code&gt;indexed&lt;/code&gt; again. When I want to remove a list from &lt;code&gt;@source&lt;/code&gt;, I'll need to know its position in &lt;code&gt;$s&lt;/code&gt;, so this is a convenient way to get it.&lt;/li&gt;
&lt;li&gt;Weed out candidates that aren't prefixes of &lt;code&gt;@target&lt;/code&gt;. Luckily we have that &lt;code&gt;isPrefix&lt;/code&gt; function.  If none of the remaining lists match &lt;code&gt;$t&lt;/code&gt;, then we'll backtrack to whatever was on the stack.&lt;/li&gt;
&lt;li&gt;If we have a working prefix, we'll want to reduce our data. Make copies, and remove the matching pieces.&lt;/li&gt;
&lt;li&gt;There are two uses of &lt;code&gt;splice&lt;/code&gt;: one to chop off the prefix of &lt;code&gt;$t&lt;/code&gt;, and one to surgically remove one element out of &lt;code&gt;$s&lt;/code&gt;. It amazes and depresses me that after all these years, I still have to re-read the documentation for &lt;code&gt;splice&lt;/code&gt; to remind myself of the order of arguments and what gets returned.&lt;/li&gt;
&lt;li&gt;If we've whittled &lt;code&gt;$t&lt;/code&gt; down to nothing, success! At this point, if we wanted to enforce the requirement to use all of &lt;code&gt;@source&lt;/code&gt;, we could also check that the size of &lt;code&gt;$s&lt;/code&gt; is down to 1 (not zero, because we haven't cleaned it up yet).&lt;/li&gt;
&lt;li&gt;Note here that if we wanted to allow reuse of the lists in &lt;code&gt;@source&lt;/code&gt;, we could do it by not removing the chosen prefix at this point.&lt;/li&gt;
&lt;li&gt;Now we're down to a smaller set of possibilities. Push that checkpoint to the top of the stack and continue.&lt;/li&gt;
&lt;li&gt;If we ever get out of this loop, it means that we never got &lt;code&gt;@target&lt;/code&gt; down to zero, so that's a "no" from me, dawg.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>perl</category>
      <category>perlweeklychallenge</category>
      <category>pwc</category>
    </item>
  </channel>
</rss>
