惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
T
The Blog of Author Tim Ferriss
H
Help Net Security
博客园 - 叶小钗
云风的 BLOG
云风的 BLOG
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
D
DataBreaches.Net
博客园 - 聂微东
A
About on SuperTechFans
大猫的无限游戏
大猫的无限游戏
P
Proofpoint News Feed
Martin Fowler
Martin Fowler
博客园 - 【当耐特】
S
SegmentFault 最新的问题
Blog — PlanetScale
Blog — PlanetScale
酷 壳 – CoolShell
酷 壳 – CoolShell
G
Google Developers Blog
I
InfoQ
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
GbyAI
GbyAI
B
Blog
Engineering at Meta
Engineering at Meta
V
V2EX
Hugging Face - Blog
Hugging Face - Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
PWC 373 Task 2: Let's Dance to List Division
Bob Lied · 2026-05-14 · via DEV Community

The task title reminded me of the song Let's Dance to Joy Division. Oddly applicable lyrics for a blog about programming: "But I worked something out last night that changed this little boy's brain, a small piece of advice that took 22 years in the make, and I will break it for you now. Please learn from my mistakes." And who doesn't love a wombat?

Task Description

You are given a list and a non-negative integer, $n. Write a script to divide the given list into $n equal parts. Return -1 if $n is more than the size of the list.

Example 1
Input: @list = ( 1,2,3,4,5), $n = 2
Output: ((1,2,3), (4,5))

Enter fullscreen mode Exit fullscreen mode

  • 5 / 2 = 2 remainder 1. The extra element goes into the first chunk.

Example 2

Input: @list = (1,2,3,4,5,6), $n = 3
Output: ((1,2), (3,4), (5,6))

Enter fullscreen mode Exit fullscreen mode

  • 6 / 3 = 2 remainder 0.

Example 3

Input: @list = (1,2,3), $n = 2
Output: ((1,2), (3))

Enter fullscreen mode Exit fullscreen mode

Example 4

Input: @list = (1,2,3,4,5,6,7,8,9,10), $n = 5
Output: ((1,2), (3,4), (5,6), (7,8), (9,10))

Enter fullscreen mode Exit fullscreen mode

Example 5

Input: @list = (1,2,3), $n = 4
Output: -1

Enter fullscreen mode Exit fullscreen mode

Example 6

Input: @list = (72,57,89,55,36,84,10,95,99,35), $n = 7;
Output: ((72,57), (89,55), (36,84), (10), (95), (99), (35))

Enter fullscreen mode Exit fullscreen mode

Elaboration

There's some simple modulo division here, and then a decision about what to do if it doesn't divide evenly into $n chunks. We could put the remainder into a chunk of its own, ignore the remainder, or we could distribute the leftovers in some way that feels balanced -- here, the specification implies that the bigger divisions should go at the front of the list.

Handling an invalid input (where $n is too big) by returning -1 instead of an array is a code smell. Having a function that returns different types of things is awkward to handle and error-prone. It would be more consistent to either throw an exception, or return an out-of-range value of the same type, such as an empty array or a null reference. But that wanker Kevin with his six months of Javascript experience got promoted to project manager for some reason <cough/>nepotism<cough> and that's the requirement we have.

This task has six examples, and usually we're given five. Is there some significance to this? Example 6 doesn't seem to add any new information beyond what we've already been given. But then we notice that the numbers could be ASCII characters, spelling "H9Y7$T?_c#". I'm pretty sure this is Kevin's password. Watch your back, Kevin.

To the CodeMobile, Robin!

sub listDivision($list, $n)
{
    my $size = @$list;
    return -1 if $n > $size;

    my $chunk = floor($size / $n);
    my $extra = $size % $n;
    my @answer;

    while ( $extra-- ) { push @answer, [ splice(@$list, 0, $chunk + 1) ]; }
    while ( @$list   ) { push @answer, [ splice(@$list, 0, $chunk    ) ]; }
    return \@answer;
}

Enter fullscreen mode Exit fullscreen mode

The input parameters are an array and a number, which forces us to confront the way that Perl passes arguments as a flat list. Here, I've chosen to pass an array reference for $list instead of an array, which accomplishes two things: it removes the flat list problem to make it obvious which parameter is $n, and it's more efficient than passing a copy of the array. I could have also passed the parameters in the other order, but "project manager" Kevin said according to ChatGPT and Claude it had to be in this order, so yeah, that's that then.

Time to be serious. We dispense with the special case of returning -1 (more on that later). Then we calculate the quantities involved, and set up an array for producing the answer, initially empty.

The answer has two segments: a head part where there might be one extra element, and the tail part where the segments are of the calculated chunk size. One while loop ticks off the extra-sized chunks, and the second while loop completes the division.

To grab a group of contiguous elements from a list, the splice function seems like the obvious thing to do. Each splice is surrounded with [] so that the chunks are treated as array references. An alternative would have been to take an array slice, but that would introduce index variables for the range being sliced, which looks and feels more complicated and error-prone.

splice reduces the size of the list every time it's called, so the second while loop can use the size of $list as its condition. However, we are destroying $list as we go, so the calling function needs to be aware that the array it passed in is no longer valid. If it were important to keep the original list, a copy would need to be made either from the caller, or near the beginning of listDivision().

Let's think a moment about how to use this function. When it's called it could return either an array of arrays (references actually), or it could return a scalar -1. Here's how I wanted to write the script to use this function, using -n as a command-line option to get that parameter:

use GetOpt::Long;
my $N = 1;
GetOptions("n:i" => \$N);
my $div = listDivision([@ARGV], $N);
say showAofA($div);

Enter fullscreen mode Exit fullscreen mode

where showAofA() is a function that formats the array of arrays into a parenthesized list of comma-separated lists, as in the examples.

However, $div isn't always a reference; sometimes it's a scalar -1. That throws up an ugly error about trying to use a scalar value as a reference. The way to check what's behind a variable in Perl is to use the ref function. It will give a string identifying the variable type, or, for a scalar, it will give an empty string. In perl, an empty string counts as false. So, to satisfy the requirement, the script needs to test whether ref($div) is empty:

say ( ref($div) ? showAofA($div) : $div );

Enter fullscreen mode Exit fullscreen mode

What I probably should have done is change listDivision so that it consistently returns an array reference, but for the invalid case, throws an error. Variations on try/catch have been available as modules in Perl for decades, but it finally (see what I did there?) became official in version 5.38. That simplifies the usage of the function (if you consider try/catch simpler, which some consider a debatable topic).

try          { say showAofA( listDivision([@ARGV], $N) ); }
catch ( $e ) { say "-1" }

Enter fullscreen mode Exit fullscreen mode

To make the function throw an exception, we only need to change the early return from the function into a die:

sub listDivision($list, $n)
{
    my $size = @$list;
    die "N out of range (must be <= $size)" if $n > $size;
    # [ ... ]

Enter fullscreen mode Exit fullscreen mode

That's cleaner, but it slightly complicates the unit testing, because now we have to test for exceptions. The incantation is:

sub runTest
{
    use Test2::V0;
    use Test2::Tools::Exception;
    # [Test cases that should work
    is( listDivision([1,2,3], 1), [[1,2,3]], "One chunk");
    # [ ... ]
    # Test things that throw exceptions
    like ( dies { listDivision( [1,2,3], 4) }, qr/out of range/);

Enter fullscreen mode Exit fullscreen mode

The dies function returns the exception string that results from trying to execute a code block, and like does a pattern match against the expected exception.

And finally, to tidy up a loose end, here's the showAofA() function.

sub showAofA($ref)
{
    return '('. join(',', map { '('.join(',', $_->@*).')' } $ref->@*) .')';
}

Enter fullscreen mode Exit fullscreen mode

Let's take a bunch of punctuation out of that so that we can see the structure:

return ( join( map { join } ) ref )

Enter fullscreen mode Exit fullscreen mode

$ref is a reference to an array of references; hence, $ref->@* is de-referencing it to yield a list of array references.

Each of those array references is then transformed by map. Inside the map {} block, $_ refers to one of the inner arrays, so we de-reference that ($_->@*) to get a list of numbers, which we will join into a comma-separated string. Wrap that with a pair of parentheses, and out of the map comes the inner lists formatted as needed.

Now each of the inner lists is a string. The outer join inserts commas to make it a list of lists, and finally we wrap the whole thing in parentheses to complete the string.