4

I'm looking for a way to clear all of my arrays in a Perl program.

Currently, I'm calling a subroutine that explicitly "resets" all arrays:

sub clear_arrays{(@array1,@array2,@array3)=((),(),());}

This forces me to find all the arrays in the program and literally reference them in the subroutine.

I've looked at the perldoc for reset, undef, and delete but couldn't interpret any of them in a way that would clear all arrays.

Is there a built-in function of Perl that can do this?

If not, is there a function that would return an array of all the array variables?

Ex:

my @prog_arrays = getarrays();
foreach(@prog_arrays){$_ = ();}

Where getarrays() might be a built-in Perl function that returns any/all initialized arrays in the program.


EDIT:
My particular situation involves only two global arrays that need to be reset. I broadened the question out of curiosity rather than necessity. Basically, my globals are @email_subject & @email_msg.

They have values pushed into them as the script progresses and data is gathered/analyzed. At the end of the script, the email message is sent, and the script may run again depending on the loop condition variable.

If it runs again, I need to clear these 2 globals so that they can be aggregated again during the next loop cycle. It's not killing me to clear these two arrays via literal reference, but I was just wondering if Perl already had some built-in function to clear the arrays without literally referencing them.

This may not be the best way to accomplish this, but it was the first intuitive option that I considered.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
CheeseConQueso
  • 5,831
  • 29
  • 93
  • 126
  • 9
    Not an answer, just a suggestion (it works for every language I deal with): Just manage each object's scope and lifetime as needed. Aggregation/composition of objects ("OO" or otherwise) and elimination/reduction of global variables simplifies the design. –  May 17 '11 at 19:49
  • The three upvotes worry me. There are people out there who want to do this. Never drive with your car doors unlocked – Borodin Jul 26 '15 at 20:01
  • This question is asking for a way to circumvent Perl's built-in protection against poor programming. The sad thing is that there are people who want to help – Borodin Jul 26 '15 at 20:03

3 Answers3

19

Don't use global arrays. It's as simple as that. Lexical arrays are limited to the scope where they are declared, and automatically start empty when you enter the scope.

If you must use globals, keeping track of them all in one place is a good idea anyway, so clearing them shouldn't be difficult.

Someone once posted an now-infamous tool to perlmonks to do what you want. The code was withdrawn after receiving much criticism of the whole idea; you can read some of the criticism here: http://www.perlmonks.org/index.pl?node_id=349496

ysth
  • 96,171
  • 6
  • 121
  • 214
  • The OP is asking for a way to circumvent Perl's built-in protection against poor programming. The sad thing is that there are people who want to help. Thank you for not being one of them – Borodin Jul 26 '15 at 20:04
8

The fact that you want this screams "bad design" to me. However, on the assumption that you know exactly what you're doing with this radioactive chainsaw, you can accomplish it by accessing the global symbol table hash %:: or %main::. (The colons are part of the name.) This hash contains a mapping from every defined global symbol to a reference to its variable.

Something like this should suffice:

for my $ref (values %::) {
    @{$ref} = ();
}

Edited to remove the check against array references. All of the values are in fact typeglob references, so there's no need to check.

JSBձոգչ
  • 40,684
  • 18
  • 101
  • 169
  • Yikes! Danger Will Robinson! `*arms flailing*` – Rob Raisch May 17 '11 at 20:13
  • 1
    Not sure what the `ref` is for. When I tried it (on 5.10.1 and 5.14.0), it wouldn't clear global variables. This will: `perl -Mstrict -wE 'for (values %::) { say; @{$_} = () } say for @INC;'` As you say, you wouldn't want to use that in production. :-) Also, reminds how I once did `Properties props = new Properties(); props.setProperty(name, value); System.setProperties(props);` in Java. :-) – Lumi May 17 '11 at 20:21
  • I just changed my display name to Lumi. Was curious to see whether SO would then propagate that change through all the references made to my name in comments. It wouldn't. :-) – Lumi May 17 '11 at 20:29
  • 3
    The `for` loop in your example installs an empty array into every existing entry in `%::` not just those that already contained arrays. You want something like `for (keys %::) {@{$::{$_}} = () if *{$::{$_}}{ARRAY}}` which will only clear the existing arrays and will not create new ones. Ideally, you should also run a regex against the glob names to make sure you aren't clearing things like `@_` or `@INC` or `@ISA`. – Eric Strom May 17 '11 at 20:43
6

As mentioned in other answers, your request speaks to a larger problem with the design of your program. You should either use lexicals that will fall out of scope, or closely manage all of your global arrays, and create a function that will clear them for you.

If you insist on bludgeoning every array in your namespace, at least take care and check to make sure you aren't writing over values that Perl may need:

for (keys %::) {  # for everything in `package main;`
    if (*{$::{$_}}{ARRAY}) {  # if there is an array in the slot
        # clear it unless it is a "special" array
        @{$::{$_}} = () unless /^(?:INC|ISA|EXPORT|EXPORT_OK|ARGV|_|\W)$/
    }
}

I would write it like this though:

my @global_arrays = \our (@foo, @bar, @baz);
sub clear_global_arrays {
    @$_ = () for @global_arrays
}

The effect is the same for the arrays in question, yet it does not run the risk of clobbering anything you did not intend to. You could even use my rather than our with the second example, whereas the first example requires the variables to be in the symbol table (aka defined with our).

Eric Strom
  • 39,821
  • 2
  • 80
  • 152
  • I really wish you hadn't described in public how to kill your grandmother and get her inheritance – Borodin Jul 26 '15 at 19:58