File Coverage

blib/lib/DataStructure/LinkedList.pm
Criterion Covered Total %
statement 146 151 96.6
branch 20 22 90.9
condition 2 2 100.0
subroutine 26 27 96.3
pod 8 10 80.0
total 202 212 95.2


line stmt bran cond sub pod time code
1             # A linked list data-structure.
2              
3             package DataStructure::LinkedList;
4              
5 1     1   204118 use strict;
  1         5  
  1         30  
6 1     1   6 use warnings;
  1         2  
  1         23  
7 1     1   5 use utf8;
  1         2  
  1         6  
8 1     1   25 use feature ':5.24';
  1         2  
  1         168  
9 1     1   7 use feature 'signatures';
  1         2  
  1         47  
10 1     1   7 no warnings 'experimental::signatures';
  1         3  
  1         50  
11              
12 1     1   436 use DataStructure::LinkedList::Node;
  1         2  
  1         37  
13              
14 1     1   452 use parent qw(DataStructure::Queue);
  1         319  
  1         7  
15              
16             package DataStructure::ReverseLinkedList {
17 1     1   62 use parent qw(DataStructure::LinkedList DataStructure::Queue DataStructure::Stack);
  1         3  
  1         5  
18             }
19              
20             =pod
21              
22             =head1 NAME
23              
24             DataStructure::LinkedList
25              
26             =head1 SYNOPSIS
27              
28             A linked list data-structure, written in pure Perl.
29              
30             See also L for a double-linked list version that
31             offers a richer interface.
32              
33             =head1 DESCRIPTION
34              
35             =head2 CONSTRUCTOR
36              
37             C<< DataStructure::LinkedList->new(%options) >>
38              
39             Creates an empty list.
40              
41             The following options are available:
42              
43             =over 4
44              
45             =item reverse
46              
47             By default this class implements the standard C and C methods
48             that operate on the beginning of the list and the C method that operates
49             on the end of the list. And C is a synonym for C (so not the
50             opposite of C).
51              
52             If the C option is set to a true value then the semantics of the list
53             is reversed and C and C operate on the beginning of the list,
54             C operates on the end of the list and C becomes a synonym for
55             C.
56              
57             =back
58              
59             =cut
60              
61 4     4 0 123902 sub new ($class, %options) {
  4         11  
  4         8  
  4         9  
62 4 100       16 if ($options{reverse}) {
63 1 50       4 die unless $class eq 'DataStructure::LinkedList';
64 1         3 $class = 'DataStructure::ReverseLinkedList';
65             }
66             return bless {
67             size => 0,
68             first => undef,
69             last => undef,
70 4   100     46 reverse => $options{reverse} // 0,
71             }, $class;
72             }
73              
74             =pod
75              
76             =head2 METHODS
77              
78             All the functions below are class methods that should be called on a
79             B object. Unless documented otherwise, they run in
80             constant time.
81              
82             =over 4
83              
84             =item first()
85              
86             Returns the first L of the list, or B if
87             the list is empty.
88              
89             =cut
90              
91 34     34 1 49 sub first ($self) {
  34         53  
  34         47  
92 34         72 return $self->{first};
93             }
94              
95             =pod
96              
97             =item last()
98              
99             Returns the last L of the list, or B if
100             the list is empty.
101              
102             =cut
103              
104 0     0 1 0 sub last ($self) {
  0         0  
  0         0  
105 0         0 return $self->{last};
106             }
107              
108             # Actual unshift that always operates on the beginning of the list.
109 8     8   12 sub _unshift ($self, $value) {
  8         12  
  8         11  
  8         14  
110 8         33 my $new_node = DataStructure::LinkedList::Node->new($self, $self->{first}, $value);
111 8         17 $self->{first} = $new_node;
112 8 100       24 $self->{last} = $new_node unless defined $self->{last};
113 8         15 $self->{size}++;
114 8         17 return $new_node;
115             }
116              
117             # Actual push that always operates on the end of the list.
118 4     4   7 sub _push ($self, $value) {
  4         7  
  4         6  
  4         4  
119 4         16 my $new_node = DataStructure::LinkedList::Node->new($self, undef, $value);
120 4 100       13 if (defined $self->{last}) {
121 2         7 $self->{last}{next} = $new_node;
122             } else {
123 2         5 $self->{first} = $new_node;
124             }
125 4         6 $self->{last} = $new_node;
126 4         7 $self->{size}++;
127 4         9 return $new_node;
128             }
129              
130             # Actual shift that always operates on the beginning of the list.
131 14     14   21 sub _shift ($self) {
  14         19  
  14         22  
132 14 100       67 return unless defined $self->{first};
133 8         22 my $old_first = $self->first();
134 8         27 $self->{first} = $old_first->next();
135 8 100       21 $self->{last} = undef unless defined $self->{first};
136 8         24 return $old_first->_delete_first();
137             }
138              
139             =pod
140              
141             =item unshift($value)
142              
143             Adds a new node at the beginning of the list with the given value. Returns the
144             newly added node.
145              
146             =cut
147              
148 8     8 1 25 sub unshift ($self, $value) {
  8         12  
  8         16  
  8         10  
149 8 100       29 return $self->_push($value) if $self->{reverse};
150 6         18 return $self->_unshift($value);
151             }
152              
153             =pod
154              
155             =item push($value)
156              
157             Adds a new node at the end of the list with the given value. Returns the
158             newly added node.
159              
160             =cut
161              
162 4     4 1 10 sub push ($self, $value) {
  4         8  
  4         8  
  4         5  
163 4 100       22 return $self->_unshift($value) if $self->{reverse};
164 2         6 return $self->_push($value);
165             }
166              
167             =pod
168              
169             =item shift()
170              
171             Removes the first node of the list and returns its value. Returns B if
172             the list is empty. Note that the method can also return B if the first
173             node’s value is B
174              
175             For convenience, C can be used as a synonym of C.
176              
177             =cut
178              
179 9     9 1 18 sub shift ($self) {
  9         17  
  9         14  
180 9         23 return $self->_shift();
181             }
182              
183 5     5 0 12 sub pop ($self) {
  5         9  
  5         8  
184 5         19 return $self->_shift();
185             }
186              
187             =pod
188              
189             =item size()
190              
191             Returns the number of nodes in the list.
192              
193             =cut
194              
195 42     42 1 77 sub size ($self) {
  42         58  
  42         58  
196 42         161 return $self->{size};
197             }
198              
199             =pod
200              
201             =item empty()
202              
203             Returns whether the list is empty.
204              
205             =cut
206              
207 3     3 1 6 sub empty ($self) {
  3         5  
  3         5  
208 3         8 return $self->size() == 0;
209             }
210              
211             =pod
212              
213             =item values()
214              
215             Returns all the values of the list, as a normal Perl list. This runs in linear
216             time with the size of the list.
217              
218             =cut
219              
220 21     21 1 10047 sub values ($self) {
  21         36  
  21         33  
221 21 50       53 return $self->size() unless wantarray;
222 21         48 my @ret = (0) x $self->size();
223 21         37 my $i = 0;
224 21         63 my $cur = $self->first();
225 21         49 while (defined $cur) {
226 24         69 $ret[$i++] = $cur->value();
227 24         57 $cur = $cur->next();
228             }
229 21         180 return @ret;
230             }
231              
232             # Runs a consistency check of the list. Assumes that tests are running with
233             # Test::More.
234 18     18   39 sub _self_check ($self, $name) {
  18         29  
  18         30  
  18         24  
235 1     1   1700 eval { use Test2::Tools::Compare qw(is T D U); use Test2::Tools::Subtest };
  1     1   10106  
  1         95  
  1         9  
  1         2  
  1         542  
  18         36  
  0         0  
236             subtest_streamed $name => sub {
237 18     18   10610 my $s = $self->{size};
238 18         54 is($s >= 0, T(), 'Size is non-negative');
239 18 100       8561 if ($s == 0) {
240 6         23 is($self->{first}, U(), 'No first when size is 0');
241 6         2798 is($self->{last}, U(), 'No last when size is 0');
242             } else {
243 12         46 is($self->{first}, D(), 'Has first when size is not 0');
244 12         5902 is($self->{last}, D(), 'Has last when size is not 0');
245 12         5833 my $n = $self->{first};
246 12         26 my $c = 0;
247 12         31 while ($n) {
248 20         26 $c++;
249 20         64 is($n->{list} == $self, T(), 'Self pointer in node');
250 20 100       9303 if ($c < $s) {
251 8         32 is($n->{next}, D(), 'Node has next element');
252             } else {
253 12         37 is($n->{next}, U(), 'Node has no next element');
254 12         5392 is($n == $self->{last}, T(), 'Correct last element');
255             }
256 20         9389 $n = $n->{next};
257             }
258 12         34 is($c, $s, 'Correct node count');
259             }
260 18         111 };
261             }
262              
263             # The destructor is not strictly needed because all the nodes don’t have cyclic
264             # references. But let’s keep it.
265 4     4   1637 sub DESTROY ($self) {
  4         7  
  4         8  
266 4         10 my $next = $self->{first};
267 4         12 while (defined $next) {
268 4         7 my $cur = $next;
269 4         8 $next = $cur->{next};
270 4         8 undef %{$cur};
  4         13  
271             }
272 4         167 return;
273             }
274              
275             =pod
276              
277             =back
278              
279             =head1 SEE ALSO
280              
281             L
282              
283             =head1 AUTHOR
284              
285             Mathias Kende
286              
287             =head1 LICENCE
288              
289             Copyright 2021 Mathias Kende
290              
291             This program is distributed under the MIT (X11) License:
292             L
293              
294             Permission is hereby granted, free of charge, to any person
295             obtaining a copy of this software and associated documentation
296             files (the "Software"), to deal in the Software without
297             restriction, including without limitation the rights to use,
298             copy, modify, merge, publish, distribute, sublicense, and/or sell
299             copies of the Software, and to permit persons to whom the
300             Software is furnished to do so, subject to the following
301             conditions:
302              
303             The above copyright notice and this permission notice shall be
304             included in all copies or substantial portions of the Software.
305              
306             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
307             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
308             OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
309             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
310             HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
311             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
312             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
313             OTHER DEALINGS IN THE SOFTWARE.
314              
315             =cut
316              
317             1;