Skip to content
VLSI Mentor

Perl Part I · Module 5 · Arrays and Lists

splice: Insert and Remove in the Middle

The four functions in the last chapter all work at an array's ends, which covers most of what scripts need. When you have to act in the middle — drop the third row, insert two entries after the header, swap a span for a shorter one — splice is the single function that does all three. It takes an array, a starting position, how many elements to affect, and optionally what to put in their place, and the combinations of those arguments give you removal, insertion and replacement without rebuilding the array by hand. Two of its details are worth measuring rather than guessing: a negative length does not mean what most people assume, and the function returns something different depending on whether you assign it to an array or to a scalar.

Intermediate13 min readPerlPart IArraysData Structures

Part I · Module 5 · Chapter 5.5 · splice: Insert and Remove in the Middle

1. Four Arguments, Three Jobs

Azvya Education Pvt. Ltd.VLSI Mentor
The shape
splice(ARRAY, OFFSET, LENGTH, REPLACEMENT_LIST)
You supplyYou get
array, offset, lengthremove that span and return it
array, offsetremove from offset to the end
array, offset, 0, listinsert the list at offset, removing nothing
array, offset, length, listreplace the span with the list

One function, and which job it does is decided entirely by which arguments you give it. That is why it is worth learning as a unit rather than as four separate things.

2. Remove a Span

Azvya Education Pvt. Ltd.VLSI Mentor
remove.pl
use strict;
use warnings;

my @a = ("a", "b", "c", "d", "e");

my @removed = splice(@a, 1, 2);

printf "removed   [%s]\n", join(" ", @removed);
printf "remaining [%s]\n", join(" ", @a);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
removed   [b c]
remaining [a d e]

Starting at position 1, two elements — b and c — were removed, returned, and the array closed up behind them. @a is now three elements, and the remaining ones have moved down: d is at position 1 where b used to be.

That renumbering is the thing to hold onto. Unlike Chapter 5.2 §5's $# truncation, which just discards the tail, splice shifts everything after the removed span. So any index you were holding into the array is stale afterwards.

Omit the length to remove to the end

Azvya Education Pvt. Ltd.VLSI Mentor
remove-tail.pl
use strict;
use warnings;

my @a = ("a", "b", "c", "d");

my @tail = splice(@a, 2);

printf "tail [%s]  left [%s]\n", join(" ", @tail), join(" ", @a);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
tail [c d]  left [a b]

Two arguments means "from here to the end". This is the readable way to truncate an array while keeping what you removed — which is exactly what Chapter 5.2 §5 said $#a = n cannot do, because it discards the values.

3. Insert Without Removing

Azvya Education Pvt. Ltd.VLSI Mentor
insert.pl
use strict;
use warnings;

my @a = ("a", "d");

splice(@a, 1, 0, "b", "c");

printf "[%s]\n", join(" ", @a);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
[a b c d]

A length of 0 removes nothing, so the replacement list is simply inserted at the offset. The elements from the offset onwards move up to make room.

The 0 is doing real work and is easy to leave out by accident. splice(@a, 1, "b", "c") would be read as offset 1, length "b" — which numifies to 0 with a warning, by Chapter 2.13's rule — and then "c" as the replacement. Write the 0.

4. Replace, Including With a Different Count

Azvya Education Pvt. Ltd.VLSI Mentor
replace.pl
use strict;
use warnings;

my @a = ("a", "b", "c", "d");

my @old = splice(@a, 1, 2, "X");

printf "removed [%s]\n", join(" ", @old);
printf "now     [%s]  count=%d\n", join(" ", @a), scalar(@a);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
removed [b c]
now     [a X d]  count=3

Two elements out, one in. The array went from four elements to three, and the replacement did not have to match the span's length.

That asymmetry is what makes splice a single answer to all three jobs rather than three functions. A replacement list longer than the span grows the array; shorter shrinks it; equal keeps the length. And in every case the function hands back what was there, so you can log it or check it.

5. The Two Arguments People Guess Wrong

A negative offset counts from the end

Azvya Education Pvt. Ltd.VLSI Mentor
negative-offset.pl
use strict;
use warnings;

my @a = ("a", "b", "c", "d");

my @gone = splice(@a, -2, 1);

printf "removed [%s]  left [%s]\n", join(" ", @gone), join(" ", @a);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
removed [c]  left [a b d]

-2 is the second-from-last position, the same convention as Chapter 5.2 §1. One element was removed from there.

A negative length means "leave this many at the end"

Azvya Education Pvt. Ltd.VLSI Mentor
negative-length.pl
use strict;
use warnings;

my @a = ("a", "b", "c", "d", "e");

my @gone = splice(@a, 1, -2);

printf "removed [%s]  left [%s]\n", join(" ", @gone), join(" ", @a);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
removed [b c]  left [a d e]

This is the one to measure rather than guess. A negative length does not mean "work backwards". It means stop that many elements short of the end.

So splice(@a, 1, -2) read as "from position 1, up to but not including the last two" — which on a five-element array is positions 1 and 2, giving b and c. The last two, d and e, were left alone.

It is genuinely useful for "drop everything except the header and the last two rows", and genuinely confusing if you assumed it counted backwards. If the expression is not obviously readable, compute the length instead.

6. The Return Value Depends on Context

Azvya Education Pvt. Ltd.VLSI Mentor
context.pl
use strict;
use warnings;

my @a = ("a", "b", "c", "d");
my @list_ctx = splice(@a, 1, 2);

my @b = ("a", "b", "c", "d");
my $scalar_ctx = splice(@b, 1, 2);

printf "list context   gave [%s]\n", join(" ", @list_ctx);
printf "scalar context gave [%s]\n", $scalar_ctx;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
list context   gave [b c]
scalar context gave [c]

In list context you get everything removed. In scalar context you get the last element removed — c, not b.

7. splice Warns When the Offset Is Past the End

This is worth a section because it is the exception in this module.

Azvya Education Pvt. Ltd.VLSI Mentor
past-end.pl
use strict;
use warnings;

my @a = ("a", "b");

my @gone = splice(@a, 5, 2);

printf "removed %d  left [%s]\n", scalar(@gone), join(" ", @a);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDOUT
removed 0  left [a b]
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDERR
splice() offset past end of array at past-end.pl line 6.

Nothing was removed and the array is unchanged — and Perl said so.

Compare that with the rest of the module: Chapter 5.1 §5's out-of-range read, Chapter 5.2 §4's out-of-range write, and Chapter 5.3 §3's out-of-range slice are all completely silent. splice is the one array operation that reports a bad index.

Worth knowing for two reasons: you get a diagnostic here that you have learned not to expect, and when you see splice() offset past end of array in a log it means an offset was computed wrongly — usually because the array was modified between computing the offset and using it, which §2's renumbering point makes easy to do.

8. push and pop Are Special Cases of This

Every function from Chapter 5.4 can be written as a splice:

The simple formThe splice equivalent
push @a, $vsplice(@a, scalar(@a), 0, $v)
pop @asplice(@a, -1)
unshift @a, $vsplice(@a, 0, 0, $v)
shift @asplice(@a, 0, 1)

Use the simple forms. The table is here to show that splice is the general operation and the other four are named shortcuts for its common cases — not as a suggestion to write the right-hand column. push @a, $v says what it does; the splice version makes a reader work it out.

That is the practical rule for the whole chapter: reach for splice when you need the middle. At the ends, the named functions are clearer.

9. Why This Matters in Engineering Work

Dropping a header row. A file read into an array arrives with its header as element 0. shift @lines is the right tool for one row; splice(@lines, 0, 3) is right when there are three lines of preamble, and it hands them back so you can check they were what you expected.

Keeping the tail of a log. splice(@lines, 0, scalar(@lines) - 20) leaves the last twenty lines and returns the rest — or, more readably, @lines = @lines[-20 .. -1] with Chapter 5.3's negative slice when you do not need what you dropped.

Inserting a generated row. A report that needs a separator or a subtotal inserted after a group uses the length-0 insert form.

And the renumbering is the thing that bites. Removing elements in a loop while holding an index into the same array is a classic bug: after the first removal every later index is off by one, and the loop silently skips elements. The usual fixes are to iterate backwards, or to build a new array with grep from Chapter 5.10 instead of modifying in place — and the second is almost always clearer.

10. Common Mistakes

Forgetting the 0 when inserting. splice(@a, 1, "b", "c") reads "b" as the length, numifies it to 0 with a warning, and treats "c" as the replacement.

Expecting a negative length to count backwards. It means "stop that many from the end".

Assigning a multi-element splice to a scalar. You get the last element removed and lose the rest.

Holding an index across a splice. Everything after the removed span renumbers.

Removing inside a forward loop over the same array. Elements get skipped. Iterate backwards or build a new array.

Writing splice where push or pop would do. The named functions are clearer at the ends.

Assuming out-of-range is silent here. It is the one array operation that warns.

11. Exercise

Exercise 1 — the three jobs. Starting from ("a","b","c","d","e") each time, use splice to (a) remove b and c, (b) insert X and Y between b and c, and (c) replace c and d with a single Z. Print the array and the returned list for each, and report the count after each.

Exercise 2 — remove to the end. Remove everything from position 2 onwards, keeping what you removed. Print both halves. Then say why $#a = 1 from Chapter 5.2 could not have done this.

Exercise 3 — the negative length. Predict what splice(@a, 1, -2) removes from a six-element array before running it. Then run it and, if you were wrong, state the rule in your own words.

Exercise 4 — the context trap. Splice two elements out of an array into a scalar and print what you got. Say which of the two you received and where the other one went.

Exercise 5 — the warning. Splice at an offset past the end of a two-element array. Capture STDERR and record the exact message. Then say which three other array operations from this module would have stayed silent in the same situation.

Exercise 6 — the renumbering bug. Write a forward loop over 0 .. $#a that splices out every element equal to "drop" from ("keep","drop","drop","keep"). Record which elements survive, explain why it is wrong, then rewrite it with grep and confirm the correct result.

Exercise 7 — prefer the simple form. Write push @a, "x" as a splice, confirm the two behave identically, and then say in one clause which you would commit and why.

12. Summary

  • splice(ARRAY, OFFSET, LENGTH, LIST) removes, inserts or replaces, depending on which arguments you give it.
  • Omitting the length removes to the end and returns what it took — which $#array assignment cannot do.
  • A length of 0 inserts without removing. Write the 0 explicitly.
  • The replacement list need not match the span's length, so the array can grow or shrink.
  • A negative offset counts from the end; a negative length means "stop that many short of the end" — not "backwards".
  • In list context it returns everything removed; in scalar context it returns the LAST element removed and the rest are lost.
  • splice warns on an offset past the end — the only array operation in this module that reports a bad index.
  • Removing renumbers everything after the span, so indexes held across a splice are stale.
  • push, pop, shift and unshift are named special cases. Use them at the ends; use splice for the middle.

Chapter 5.6 steps back from the functions to settle a distinction the whole module has been leaning on: what exactly is the difference between the parenthesised thing on the right of an assignment and the array on the left.

Where this fits

Part of the Perl curriculum.