This is my response to the Perl Weekly Challenge #129.
Tree:
1
/ \
2 3
\
4
/ \
5 6
Node: 6
Output: 3 as the distance of given node 6 from the root (1).
Node: 5
Output: 3
Node: 2
Output: 1
Node: 4
Output: 2
Example 2:
Tree:
1
/ \
2 3
/ \
4 5
\ /
6 7
/ \
8 9
Node: 7
Output: 3 as the distance of given node 6 from the root (1).
Node: 8
Output: 4
Node: 6
Output: 3
The tree looks like a binary tree (where each node can have 0, 1 or 2 child nodes), but the challenge does not say so. Let us assume that it indeed is a binary tree (given the definition above). Then we can (re)use quite a lot of the code from the second part of challenge 125 Pythagorean Tree with Raku (Binary Tree Diameter).
The type of the node values is not described, but the values used in the examples are single digit positive integers. But I'll refrain from adding a restraint on the values.
The tree looks kind of sorted and there are no duplicates, but this is also just an observation. In case of duplicate matches, do we report one (and which one), or all? I'll get back to this conundrum.
The first part of the program (shown below) is a copy of the program «binary-tree-diameter» (from challenge 125; see above - and follow link for the explanation, if needed), except for the default tree and the extra node parameter:
File: root-distance (partial)
#! /usr/bin/env raku
unit sub MAIN (Str $tree = "1 | 2 3 | * * * 4 | 5 6",
Str $node = '6',
:v(:$verbose));
class BinaryNode
{
has Int $.value;
has BinaryNode $.left;
has BinaryNode $.right;
}
my @btree = $tree.split("|")>>.words;
my @old-nodes;
my @new-nodes;
for @btree.reverse -> $row
{
my @current = @$row;
@old-nodes = @new-nodes;
@new-nodes = ();
for @current -> $value
{
if $value eq "*"
{
@new-nodes.push("*");
next;
}
my $left = @old-nodes.shift // "*"; $left = Nil if $left eq "*";
my $right = @old-nodes.shift // "*"; $right = Nil if $right eq "*";
@new-nodes.push(BinaryNode.new(value => $value.Int,
left => $left // Nil,
right => $right // Nil));
}
}
my $btree = @new-nodes[0];
The recursive traversing procedure does the same traversal (as in challenge 125), but what it does on each node has been changed somewhat:
File: root-distance (the rest)
my $distance = -1;
traverse2($btree, $node, ());
say $distance;
sub traverse2 ($current, $target, @path is copy)
{
@path.push: $current.value;
if $current.value == $target # [1]
{
say ": Path: { @path.join(" > ") }" if $verbose;
$distance = @path.elems -1; # [2]
return; # [3]
}
if ($current.left || $current.right)
{
traverse2($current.left, $target, @path) if $current.left;
traverse2($current.right, $target, @path) if $current.right;
}
}
[1] Have we found the value we are looking for?.
[2] • Set the distance,
[3] • Return from the recursive call(s).
Note that the recursion may cause traversal to continue after we have found the node.
Running it:
$ ./root-distance
3
$ ./root-distance "1 | 2 3 | * * * 4 | 5 6" 6
3
$ ./root-distance "1 | 2 3 | * * * 4 | 5 6" 5
3
$ ./root-distance "1 | 2 3 | * * * 4 | 5 6" 2
1
$ ./root-distance "1 | 2 3 | * * * 4 | 5 6" 4
2
$ ./root-distance "1 | 2 3 | 4 * * 5 | * 6 7 | 8 9" 7
3
$ ./root-distance "1 | 2 3 | 4 * * 5 | * 6 7 | 8 9" 8
4
$ ./root-distance "1 | 2 3 | 4 * * 5 | * 6 7 | 8 9" 6
3
Looking good.
With verbose mode:
$ ./root-distance -v "1 | 2 3 | * * * 4 | 5 6" 6
: Path: 1 > 3 > 4 > 6
3
$ ./root-distance -v "1 | 2 3 | * * * 4 | 5 6" 5
: Path: 1 > 3 > 4 > 5
3
$ ./root-distance -v "1 | 2 3 | * * * 4 | 5 6" 2
: Path: 1 > 2
1
$ ./root-distance -v "1 | 2 3 | * * * 4 | 5 6" 4
: Path: 1 > 3 > 4
2
$ ./root-distance -v "1 | 2 3 | 4 * * 5 | * 6 7 | 8 9" 7
: Path: 1 > 3 > 5 > 7
3
$ ./root-distance -v "1 | 2 3 | 4 * * 5 | * 6 7 | 8 9" 8
: Path: 1 > 2 > 4 > 6 > 8
4
$ ./root-distance -v "1 | 2 3 | 4 * * 5 | * 6 7 | 8 9" 6
: Path: 1 > 2 > 4 > 6
3
The program will only print one match, which is ok if we assume that the values are distinct. Adding support for duplicates is easy:
File: root-distance-all (changes only)
unit sub MAIN (Str $tree = "1 | 2 3 | * * * 4 | 5 6",
Str $node = '6',
:a(:$all), # [1]
:v(:$verbose));
my $btree = @new-nodes[0];
my @distance; # [2]
traverse2($btree, $node, ());
say @distance.join(" "); # [3]
sub traverse2 ($current, $target, @path is copy)
{
@path.push: $current.value;
if $current.value == $target
{
say ": Path: { @path.join(" > ") }" if $verbose;
@distance.push(@path.elems -1); # [4]
return unless $all;
}
if ($current.left || $current.right)
{
traverse2($current.left, $target, @path) if $current.left;
traverse2($current.right, $target, @path) if $current.right;
}
}
[1] Enable «all» mode with this command line option.
[2] We are going to collect the distances here.
[3] Print the disnace - or distances.
[4] We have a match; add it to the list.
[4] We are finished, unless we want them all.
Running it:
$ ./root-distance-all "1 | 2 3 | 4 * 5 6 | 6" 6
3
$ ./root-distance-all -v "1 | 2 3 | 4 * 5 6 | 6" 6
: Path: 1 > 2 > 4 > 6
3
$ ./root-distance-all -v -a "1 | 2 3 | 4 * 5 6 | 6" 6
: Path: 1 > 2 > 4 > 6
: Path: 1 > 3 > 6
3 2
The program does a depth first search, so it reports a match with a longer distance first. This feature does not apply to the original program:
$ ./root-distance -v "1 | 2 3 | 4 * 5 6 | 6" 6
: Path: 1 > 2 > 4 > 6
: Path: 1 > 3 > 6
2
The fact that it goes on after finding a match is also a feature. Let us try mirroring the tree:
$ ./root-distance -v "1 | 3 2 | 6 5 * 4 | * * * * * 6" 6
: Path: 1 > 3 > 6
: Path: 1 > 2 > 4 > 6
3
Now it found them in reversed order, and the one with the longest distance won.
Conclusion: Duplicate values is unchartered territory, so we can argue that returning a random one is ok (albeit the same one each time to placate entropy haters). Returning all of them is also ok. As is the one with the shortest path. So, this isn't really much of a conclusion.
Input: L1 = 1 -> 2 -> 3
L2 = 3 -> 2 -> 1
Output: 4 -> 4 -> 4
Operation: Pick the first rightmost element of L1 i.e. 3 and adds to
the first rightmost element of L2 i.e. 1. Finally store the result
i.e. 3 in the new linked list. Move to the next one of both linked
lists L1 and L2, perform the same operation. In case the sum >= 10
then you apply the same rule you would do to regular addition problem
i.e. divide the sum by 10 keep the remainder and push to the new
linked list. Don't forget to carry, 1, to the next operation.
In case one linked list is smaller than the other, you can safely
assume it is 0..
Example 2:
Input: L1 = 1 -> 2 -> 3 -> 4 -> 5
L2 = 6 -> 5 -> 5
Output: 1 -> 3 -> 0 -> 0 -> 0
Operation:
a) 1st member of L1 = 5 and 1st member of L2 = 5
b) 5 + 5 = 10
c) 0 pushed to the new linked list.
d) carry forward 1.
e) 2nd member of L1 = 4 and 2nd member of L2 = 5
f) 4 + 5 + 1 (carry) = 10
h) 0 again pushed to the new linked list.
i) carry forward 1.
j) 3rd member of L1 = 3 and 3rd member of L2 = 6
k) 3 + 6 + 1 (carry) = 10
l) 0 pshed to the new linked list.
m) carry forward 1.
n) 4th member of L1 = 2 and assume 0 as the 4th member of L2
since there are only 3 members.
o) 2 + 1 (carry) = 3
p) 3 pushed to the new linked list.
q) 5th member of L1 = 1 and assume 0 as the 5th member of L2
since there are only 3 members.
r) 1 + 0 = 1
s) 1 pushed to the new linked list.
So the new linked list now have: 1 -> 3 -> 0 -> 0 -> 0.
The above suggestion is one way, not necessarily the best way
to deal with it.
Let us start with the class, and a program that generates the linked list given on the command line:
File: linked-list-sub
#! /usr/bin/env raku
unit sub MAIN ($values = "1 2 3 4"); # [1]
my @values = $values.words; # [2]
die "Must be 1..9 only" unless all(@values) ~~ /^<[1..9]>$/; # [3]
class LinkedList # [4]
{
has Int $.value; # [4a]
has LinkedList $.next is rw; # [4b]
}
sub create_linked_list (@values is copy) # [5]
{
my $value = @values.shift; # [6]
my $top = LinkedList.new(value => $value); # [7]
my $current = $top; # [8]
for @values -> $value # [9]
{
$current.next = LinkedList.new(value => $value); # [9a]
$current = $current.next; # [9b]
}
return $top; # [10]
}
my $ll = create_linked_list(@values>>.Int); # [11]
say $ll.raku; # [12]
[1] Specify the values in a space separated string. (This is in preparation for the full program, where we need two of them.)
[2] Get the individual values.
[3] The challenge specified «single digit positive numbers», which
excludes zero. That is probably an error, but let us adhere to this requirement
(for now). The program uses an all
junction to check that all the
values satisfy the regex.
[5] Procedure to create a linked lits, given a list of values.
[6] Get the first value.
[7] Create the top node (the first one in the linked list). this is the future return value (see [10]).
[8] This is a pointer to the currently last element in the list, as we go along adding values.
[9] For each additional value, add a new node to the end of the list [9a], and move the pointer one position outwards [9b].
[10] Return the list (the first element).
[11] Coerce the values to integers (as the constructor expects an integer), and off we go. Note that the check in [3] has verified that the strings can be coerced to integers without causing havoc (or truncating them).
[12] Print the linked list, using the internal representation.
See
docs.raku.org/routine/all
for more information about the all
Junction.
See
docs.raku.org/routine/is rw
for more information about is rw
.
Illegal values/characters are caught:
$ ./linked-list-sub "1 9 9 2 8 a"
Must be 1..9 only
…
$ ./linked-list-sub "1 9 9 2 8 0"
Must be 1..9 only
…
$ ./linked-list-sub "1 9 9 2 8 10"
Must be 1..9 only
…
Running it with legal values. Newlines and indentation has been added for clarity:
$ ./linked-list-sub "1 2 3"
LinkedList.new(
value => 1,
next => LinkedList.new(
value => 2,
next => LinkedList.new(
value => 3,
next => LinkedList
)
)
)
Let us try something better (clarity wise); the output format requested by the challenge: «1 -> 2 -> 3» - as a method.
Let us add support for zero as an input value at the same time, enabled by a command line option.
File: linked-list-sub2
#! /usr/bin/env raku
unit sub MAIN ($values = "1 2 3 4", :z(:$zero)); # [1]
my @values = $values.words;
$zero # [1a]
?? ( die "Must be 0..9 only" unless all(@values) ~~ /^<[0..9]>$/ )
!! ( die "Must be 1..9 only" unless all(@values) ~~ /^<[1..9]>$/ );
class LinkedList
{
has Int $.value;
has LinkedList $.next is rw;
method recursive-value
{
return flat(self.value, self.next.recursive-value) if self.next; # [2]
return self.value; # [2a]
}
}
sub create_linked_list (*@values is copy)
{
my $value = @values.shift;
my $top = LinkedList.new(value => $value);
my $current = $top;
for @values -> $value
{
$current.next = LinkedList.new(value => $value);
$current = $current.next;
}
return $top;
}
my $ll = create_linked_list(@values>>.Int);
say $ll.recursive-value.join(" -> "); # [3]
[1] Enable support for zeroes with the «-z» command line option. I have places the validation outside of the class on purpose, so that we can use it in non-digit situastions as well.
[2] The method is recursive. Call it on any element in a linked list, and
it will give you the values from there to the end (as a list). If there is a next node, it
will return the current node value and recursively call istelf on the next node to get the
rest. If this is the end, return the value [2a]. Note the use of flat
to avoid
recursive lists of lists.
[3] We got a list. Join the elements to form the required string.
See
docs.raku.org/routine/flat
for more information about flat
.
Running it:
$ ./linked-list-sub2
1 -> 2 -> 3 -> 4
$ ./linked-list-sub2 "1 9 9 2 8"
1 -> 9 -> 9 -> 2 -> 8
Why use a procedure to generate the linked list?
Why indeed. The challenge asked for a method (as a suggestion, mind you) and we should do just that:
File: linked-list-method
#! /usr/bin/env raku
unit sub MAIN ($values = "1 2 3 4", :z(:$zero));
my @values = $values.words;
$zero
?? ( die "Must be 0..9 only" unless all(@values) ~~ /^<[0..9]>$/ )
!! ( die "Must be 1..9 only" unless all(@values) ~~ /^<[1..9]>$/ );
class LinkedList
{
has Int $.value;
has LinkedList $.next is rw;
method add-elems (*@values is copy) # [1]
{
my $list = self; # [2]
my $first = @values.shift; # [3]
my $obj = LinkedList.new(value => $first); # [4]
($list ~~ LinkedList:D) # [5]
?? ( $list.next = $obj ) # [5a]
!! ( $list = $obj ); # [5b]
$obj.add-elems(@values) if @values.elems; # [6]
return $obj; # [7]
}
method recursive-value
{
return flat(self.value, self.next.recursive-value) if self.next;
return self.value;
}
}
my $ll = LinkedList.add-elems(@values>>.Int);
say $ll.recursive-value.join(" -> ");
[1] Note the slurpy *
prexif. This makes it possible to call the method with
a comma separated list of values - as well as the ordinary list.
[2] Get the object we were called on.
[3] Get the first value.
[4] Create a new object with the value from [3].
[5]
Adding the type constraint :D
(defined) to
the type of a parameter means that it must be defined. In a class setting, that
means calling the method on an instance, e.g. $l1.add-elems(...)
.
Calling it on the class itself, e.g. LinkedList.add-elems(...)
,
satisfies the :U
(undefined) type constraint. They are
mutually exclusive. The point here is that calling «add-elems» on an existing
object will add the values to it, and calling it on the class itself will give
a new list (acting as a constructor).
[6] Recursively call ouselves if there are more values to add.
[7] Return the pointer to the first object we created.
See
docs.raku.org/type/Signature#index-entry-type_constraint_:D for more
information about the :D
:U
type constraints.
Note that «add-elems» will replace an existing child element, if any. That is ok here (as we call it on the currently last node as we generate the list), but it is not what the challenge wanted.
The add-elems
method can either be called on the type object,
giving us a new list:
my $list1 = LinkedList.add-elems(1); # -> (1)
my $list2 = LinkedList.add-elems(1, 2, 3, 4); # -> (1 -> 2 -> 3 -> 4)
Or an existing LinkedList object, where it adds the value(s) to the existing list.
my $list3 = $list2.add-elems(5); # -> (1 -> 5) ### Oops
Replacing the second value in the list (and getting rid of the rest of it) is probably not what you would expect. Or want. We can fix that.
We do not actually need is copy
on the values, as we can use array
slices instead.
We should make a module of it, so we can stop copying the code all the time.
The module (called «LinkedList1», as there will be more versions of it):
File: lib/LinkedList1.rakumod
class LinkedList
{
has Int $.value;
has LinkedList1 $.next is rw;
method add-elems (@values, :r(:$replace-next)) # [1]
{
my $list = self;
my $first = @values[0];
my $obj = LinkedList1.new(value => $first);
if $list ~~ LinkedList1:D
{
unless $replace-next { $list = $list.next while $list.next; } # [2]
$list.next = $obj; # [3]
}
else
{
$list = $obj;
}
$obj.add-elems(@values[1 .. *-1]) if @values.elems > 1;
return $obj;
}
method recursive-value
{
return flat(self.value, self.next.recursive-value) if self.next;
return self.value;
}
method nice (Str $sep = ' => ') # [4]
{
return self.recursive-value.join($sep); # [4a]
}
}
[1] Get the old (replacement) functionality with the «:r» flag. If not, add to the end of the list
[2] If replacement, work on the current node. If not, get to the last node in the list.
[3] Add the new element to the end.
[4] Nice way of getting nice output. Note the support for a custom string between the values.
The program:
File: linked-list-module
#! /usr/bin/env raku
use lib "lib"; # [6]
use LinkedList1; # [7]
unit sub MAIN ($values = "1 2 3 4", :z(:$zero));
my @values = $values.words;
$zero
?? ( die "Must be 0..9 only" unless all(@values) ~~ /^<[0..9]>$/ )
!! ( die "Must be 1..9 only" unless all(@values) ~~ /^<[1..9]>$/ );
my $ll = LinkedList1.add-elems(@values>>.Int);
say $ll.nice; # [8]
[6] I have placed the module file in the «lib» directory. Tell Raku to look there.
[7] Use the module.
[8] Much shorter (and nicer) than before…
Then we can finally have a go a the program doing what the challenge asked for; two linked lists as input, and the sum of them as output.
Note that the challenge basically wants us to reinvent (or reimplement) integer addition. We do not really have to do that.
File: add-linked-lists
#! /usr/bin/env raku
use lib "lib";
use LinkedList1;
unit sub MAIN (Str $string1 = '1 2 3', # [1]
Str $string2 = '3 2 1',
:z(:$zero),
:v(:$verbose));
my @values1 = $string1.words; # [2]
my @values2 = $string2.words; # [2]
$zero # [3]
?? ( die "Must be 0..9 only"
unless all((@values1, @values2).flat) ~~ /^<[0..9]>$/ )
!! ( die "Must be 1..9 only"
unless all((@values1, @values2).flat) ~~ /^<[1..9]>$/ );
my $l1 = LinkedList1.add-elems(@values1>>.Int); # [4]
my $l2 = LinkedList1.add-elems(@values2>>.Int); # [4]
say "L1: ", $l1.nice if $verbose;
say "L2: ", $l2.nice if $verbose;
my @values3 = ( $l1.nice("") + $l2.nice("") ).comb>>.Int; # [5]
my $l3 = LinkedList1.add-elems(@values3); # [6]
say "L1+L2: ", $l3.nice if $verbose;
say $l3.nice; # [7]
[1] Specify the two lists as space separated strings.
[2] Get the inidvidual values from both strings.
[3] Validate the individual values. Note the flat
used on the combination
of both lists, as the any
junction expects a (as in one list
without recursion).
[4] Generate the two linked lists.
[5] Get the values for the combined list. We get the values from the first list, as
a string without spaces (nice("")
. That string is coerced to an integer
value by the addition. The right hand side is the same for the second list. We have
now generated the value for the combined list by integer addition. Then we split it
into separate digits, coerced into integers as the constructor (in [6]) expects that.
[6] Generate the new list.
[7] Print it, nicely.
Running it:
$ ./add-linked-list
4 => 4 => 4
$ ./add-linked-list "1 2 3 4 5" "6 5 5"
1 => 3 => 0 => 0 => 0
Looking good.
With verbose mode:
$ ./add-linked-list -v
L1: 1 => 2 => 3
L2: 3 => 2 => 1
L1+L2: 4 => 4 => 4
4 => 4 => 4
$ ./add-linked-list -v "1 2 3 4 5" "6 5 5"
L1: 1 => 2 => 3 => 4 => 5
L2: 6 => 5 => 5
L1+L2: 1 => 3 => 0 => 0 => 0
1 => 3 => 0 => 0 => 0
The challenge asked for a method adding two lists. We have not done so, so let us add one (method, that is):
File: lib/LinkedList2.rakumod (changes only)
method sum (LinkedList2 $l1, LinkedList $l1)
{
die "Do not call on an object" if self ~~ LinkedList2:D; # [1]
return LinkedList2.add-elems(( $l1.nice("") + $l2.nice("") ).comb>>.Int);
} # [2]
[1] Ensure that we have not invoked it on an object, as both lists are supposed to be passed as arguments.
[2] We did this in the program, now it is hidden inside a method.
The program:
File: add-linked-list2
#! /usr/bin/env raku
use lib "lib";
use LinkedList2;
unit sub MAIN (Str $string1 = '1 2 3',
Str $string2 = '3 2 1',
:z(:$zero),
:v(:$verbose));
my @values1 = $string1.words;
my @values2 = $string2.words;
$zero
?? (die "Must be 0..9 only" unless all((@values1, @values2).flat) ~~ /^<[0..9]>$/)
!! (die "Must be 1..9 only" unless all((@values1, @values2).flat) ~~ /^<[1..9]>$/);
my $l1 = LinkedList2.add-elems(@values1>>.Int);
my $l2 = LinkedList2.add-elems(@values2>>.Int);
say "L1: ", $l1.nice if $verbose;
say "L2: ", $l2.nice if $verbose;
my $l3 = LinkedList2.sum($l1, $l2); # [1]
say "L1+L2: ", $l3.nice if $verbose;
say $l3.nice;
[1] Magic...
Running it gives the same result as before:
$ ./add-linked-list2
4 => 4 => 4
$ ./add-linked-list2 "1 2 3 4 5" "6 5 5"
1 => 3 => 0 => 0 => 0
Note that the idea of keeping the node value restriction outside of the class does not make any sense after adding (pun intended) the «add» method. So we should really move the restriction and check inside the class.
File: lib/LinkedList3.rakumod
class LinkedList3
{
has Int $.value;
has LinkedList3 $.next is rw;
method from-string ($string, :z(:$zero)) # [2]
{
die "Do not call on an object" if self ~~ LinkedList3:D; # [2a]
my @values = $string.comb.grep({ /\S/ }); # [3]
_validate(@values, :$zero); # [1a]
return LinkedList3.add-elems(@values>>.Int, :$zero);
}
method add-elems (@values, :r(:$replace-next), :z(:$zero))
{
_validate(@values, :$zero); # [1b]
my $list = self;
my $first = @values[0];
my $obj = LinkedList3.new(value => $first);
if $list ~~ LinkedList3:D
{
unless $replace-next { $list = $list.next while $list.next; }
$list.next = $obj;
}
else
{
$list = $obj;
}
$obj.add-elems(@values[1 .. *-1], :$zero) if @values.elems > 1;
return $obj;
}
method recursive-value
{
return flat(self.value, self.next.recursive-value) if self.next;
return self.value;
}
method nice (Str $sep = ' => ')
{
return self.recursive-value.join($sep);
}
method sum (LinkedList3 $l1, LinkedList3 $l2)
{
die "Do not call on an object" if self ~~ LinkedList3:D;
return LinkedList3.from-string( ( $l1.nice("") + $l2.nice("") ), :zero );
} # [4]
sub _validate(@values, :z(:$zero)) # [1]
{
$zero
?? ( die "Must be 0..9 only" unless all(@values) ~~ /^<[0..9]>$/ )
!! ( die "Must be 1..9 only" unless all(@values) ~~ /^<[1..9]>$/ );
return True;
}
}
[1] The validation code is used by two methods ([1a] and [1b]), so here it is as an (internal) procedure. Note the support for zero, with a flag.
[2] A new constructor method that takes a string, instead of the list used by «add-elems». It does not support adding to an existing list, so can only be called on the class itself (not an object) [2a].
[3] This splits the string into separate characters (with comb
) and gets
rid of spaces (with grep({ /\S/ })
, that matches non-space characters and
lets them through). This makes it possible to call the procedure with a space separated
string, or one without spaces; e.g. «1 2 3» or «123».
[4] Explicitly allow zeroes in the combined value, even if not allowed in the two user supplied values - as e.g. «1 5 5« and «1 4 5» give «3 0 0».
Note that the validation of the remaning list of
digits is done for each recursive iteration. That is somewhat excessive, but it does
not do any harm. As long as we remeber to pass on :zero
in [4].
The program is rather short:
File: add-linked-list3
#! /usr/bin/env raku
use lib "lib";
use LinkedList3;
unit sub MAIN (Str $string1 = '1 2 3',
Str $string2 = '3 2 1',
:z(:$zero),
:v(:$verbose));
my $l1 = LinkedList3.from-string($string1, :$zero);
my $l2 = LinkedList3.from-string($string2, :$zero);
my $l3 = LinkedList3.sum($l1, $l2);
if $verbose
{
say "L1: ", $l1.nice;
say "L2: ", $l2.nice;
say "L1+L2: ", $l3.nice;
}
say $l3.nice;
It would be even shorter if we remove the verbose handling code. Feel free to do so…
Running it:
$ ./add-linked-list3
4 => 4 => 4
$ ./add-linked-list3 "1 2 3" "3 2 1"
4 => 4 => 4
$ ./add-linked-list3 "123" "321"
4 => 4 => 4
With verbose mode:
$ ./add-linked-list3 -v
L1: 1 => 2 => 3
L2: 3 => 2 => 1
L1+L2: 4 => 4 => 4
4 => 4 => 4
$ ./add-linked-list3 -v "1 2 3" "3 2 1"
L1: 1 => 2 => 3
L2: 3 => 2 => 1
L1+L2: 4 => 4 => 4
4 => 4 => 4
$ ./add-linked-list3 -v "123" "321"
L1: 1 => 2 => 3
L2: 3 => 2 => 1
L1+L2: 4 => 4 => 4
4 => 4 => 4
And that's it.