7

Can someone please explain why push behaves the way as shown below?

Basically I am trying to print values of an array populated by push as well unshift.

When I try to print array contents populated by push using array indexes, It always prints the element at the top of the array, whereas array populated by unshift prints contents of array based on array index. I don't understand why.

with unshift

#!/usr/bin/perl
@names = ("Abhijit","Royal Enfield","Google");
@numbers=();
$number=1;
$i=0;
foreach $name (@names) {
    #print $_ . "\n";
    $number=$number+1;
    #push(@numbers,($number));
    unshift(@numbers,($number));
    print("Array size is :" . @numbers . "\n");
    $i=$i+1;
    print("Individual Elements are:" . @numbers[i] . "\n");
    pop(@numbers);
}

rhv:/var/cl_ip_down>./run.sh
Array size is :1
Individual Elements are:2
Array size is :2
Individual Elements are:3
Array size is :3
Individual Elements are:4

without unshift

#!/usr/bin/perl
@names = ("Abhijit","Royal Enfield","Google");
@numbers=();
$number=1;
$i=0;
foreach $name (@names) {
    #print $_ . "\n";
    $number=$number+1;
    push(@numbers,($number));
    #unshift(@numbers,($number));
    print("Array size is :" . @numbers . "\n");
    $i=$i+1;
    print("Individual Elements are:" . @numbers[i] . "\n");
}

rhv:/var/cl_ip_down>./run.sh
Array size is :1
Individual Elements are:2
Array size is :2
Individual Elements are:2
Array size is :3
Individual Elements are:2

/without pop/

#!/usr/bin/perl
@names = ("Abhijit","Royal Enfield","Google");
@numbers=();
$number=1;
$i=0;
foreach $name (@names) {
    #print $_ . "\n";
    $number=$number+1;
    #push(@numbers,($number));
    unshift(@numbers,($number));
    print("Array size is :" . @numbers . "\n");
    $i=$i+1;
    print("Individual Elements are:" . @numbers[i] . "\n");
    #pop(@numbers);
}

rhv:/var/cl_ip_down>./run.sh
Array size is :1
Individual Elements are:2
Array size is :2
Individual Elements are:3
Array size is :3
Individual Elements are:4

with pop

#!/usr/bin/perl
@names = ("Abhijit","Royal Enfield","Google");
@numbers=();
$number=1;
$i=0;
foreach $name (@names) {
    #print $_ . "\n";
    $number=$number+1;
    #push(@numbers,($number));
    unshift(@numbers,($number));
    print("Array size is :" . @numbers . "\n");
    $i=$i+1;
    print("Individual Elements are:" . @numbers[i] . "\n");
    pop(@numbers);
}

rhv:/var/cl_ip_down>./run.sh
Array size is :1
Individual Elements are:2
Array size is :1
Individual Elements are:3
Array size is :1
Individual Elements are:4
Zoe
  • 27,060
  • 21
  • 118
  • 148
Abhijit K Rao
  • 1,097
  • 1
  • 8
  • 18
  • 10
    You should use `strict` and `warnings`. What is the `i` in `print("Individual Elements are:" . @numbers[i] . "\n");`? Why are you using an array slice there? And, how are you getting the output you show from that script? Oh, and, who voted this question up? If all you want is a badge for voting on questions, well, voting badly posed questions down is also a valid option, you know. – Sinan Ünür Feb 19 '10 at 11:36

5 Answers5

28

You really should be using use strict; and use warnings; in your code. Having them activated will allow you to identify errors in your code.

Change all instances of the following:

foreach $name (@names) -> for my $i (@names) as you don't do anything with the elements in the @names array.

@numbers[i] -> $numbers[$i] as this is where you've made a not uncommon mistake of using an array slice rather than referring to an array element.

This is not C. Every 'variable' has to have a sigil ($, @, %, &, etc.) in front of it. That i should really be $i.


As for the difference between push and shift, the documentation explains:

perldoc -f push:

push ARRAY,LIST

Treats ARRAY as a stack, and pushes the values of LIST onto the end of ARRAY. The length of ARRAY increases by the length of LIST. ... Returns the number of elements in the array following the completed "push".

perldoc -f unshift:

unshift ARRAY,LIST

Does the opposite of a shift. Or the opposite of a push, depending on how you look at it. Prepends list to the front of the array, and returns the new number of elements in the array.


To put it ASCII-matically...

        +---------+           +-----------+        +---------+ 
<-----  | ITEM(S) |  ----->   | (@) ARRAY | <----- | ITEM(S) | ----->
 shift  +---------+  unshift  +-----------+  push  +---------+   pop
                              ^           ^
                              FRONT       END
Community
  • 1
  • 1
Zaid
  • 36,680
  • 16
  • 86
  • 155
  • 1
    Great diagram. Wish I could +2 it. – friedo Feb 19 '10 at 15:46
  • Note: in the context of "shift-reduce parsing", a token is "shifted" from the front of the array containing the (tokenized) input string, and it is "pushed" onto the stack. – 0 _ May 13 '16 at 05:55
13

unshift is used to add a value or values onto the beginning of an array:

Does the opposite of a shift. Or the opposite of a push, depending on how you look at it.

The new values then become the first elements in the array.

push adds elements to the end of an array:

Treats ARRAY as a stack, and pushes the values of LIST onto the end of ARRAY.

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
b.roth
  • 9,421
  • 8
  • 37
  • 50
7

This should really be a comment but it is too long for a comment box, so here it is.

If you want to illustrate the difference between unshift and push, the following would suffice:

#!/usr/bin/perl

use strict; use warnings;

my @x;

push @x, $_ for 1 .. 3;

my @y;

unshift @y, $_ for 1 .. 3;

print "\@x = @x\n\@y = @y\n";

Output:

@x = 1 2 3
@y = 3 2 1

Note use strict; protects you against many programmer errors and use warnings; warns you when you use constructs of dubious value. At your level, neither is optional.

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
1

Note that

the preallocated array is balanced toward the 0 end of the array (meaning there is more free space at the far end of the list than there is before the list's 0 element). This is done purposely to make pushes more efficient than unshifts. http://www.perlmonks.org/?node_id=17890

Although lists do quite fine as "Perl is smartly coded because the use of lists as queues was anticipated (Ibid.)".

For comparison, in various JavaScript engines shift/unshift on arrays seems to be significantly slower.

Pavel
  • 81
  • 1
  • 7
0

I haven't seen any articulation of what these methods actually do in terms of operational complexity, which is what helped me with conceptualization: quintessentially, I believe it is called "shift" because it actually has to shift all of the n elements in your array to new indices in order to properly update the length property.

push() and pop() use simpler operational complexity. No matter what the number of n values in your array, or your array.length, push or pop will always execute 1 operation. It doesn't need to deal with indexes, it doesn't need to iterate, it only needs to execute one operation, always at the end of the stack, either adding or removing a value and index.

Most importantly, notice when using push/pop, that the other elements in the array are not affected - they are the same values in the same indices of your array. The length of the array is also automatically updated properly to what you'd expect when removing or adding values.

On the other hand, shift() and unshift() not only add or remove, but also have to actually "shift" all of the other elements in your array into different indices. This is more complex and takes more time because the amount of operations is dependent on n, the number of elements in your array, or array.length. For every n+1 larger, it has to do 1 more operation to shift each of the values into the correct index, properly updating the length properly.

Otherwise, if it didn't perform n operations after shift() and move the other elements, you would have no element at index 0, and it wouldn't change the length of your array, would it? We want the length of our arrays to update intuitively, and shift and unshift have to execute more operations to accomplish this.

mrmaclean89
  • 542
  • 6
  • 5