Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

How to combine these 2 subroutines in one?

by Anonymous Monk
on Apr 03, 2014 at 11:02 UTC ( #1080927=perlquestion: print w/ replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,
I have a task which has as follows:
Make a subroutine that removes duplicates from a list. If the list is +passed as an array, then return a clean list. If the list is passed a +s a reference to an array then clean the given array in place.

My problem is that I have these two sub-routines, one for each case:
sub clean_list { my @array_with_duplicates=(@_); my @array_no_duplicates; my $previous = ''; while (scalar @array_with_duplicates > 0) { my $next = shift(@array_with_duplicates); if ($previous ne $next) { push(@array_no_duplicates, $next); $previous = $next; } }

that would work on a code like this (case 1):
open(IN, '<', 'myfile') or die "Could not read file\n"; my @array_with_duplicates = <IN>; close IN; @array_with_duplicates = sort @array_with_duplicates; clean_list(@array_with_duplicates); open(OUT, ">", "myfile.NO_DUPLICATES") or die "Could not create file\n +"; print OUT @array_no_duplicates; close OUT;
and
sub clean_array { my %hash; @hash{@{$_[0]}}=(); @{$_[0]}=keys %hash; }

that would work on a code like this (case 2):
open(IN, '<', 'myfile') or die "Could not read file\n"; my @initial_array = <IN>; close IN; clean_array(\@initial_array); open(OUT, ">", "myfile.NO_DUPLICATES") or die "Could not create file\n +"; print OUT @initial_array; close OUT;

Can you help me create 1 script that can handle both cases?

Comment on How to combine these 2 subroutines in one?
Select or Download Code
Re: How to combine these 2 subroutines in one?
by hdb (Parson) on Apr 03, 2014 at 12:26 UTC

    As a first step, have a look at ref, that let's you decide whether a variable contains a reference to an array.

Re: How to combine these 2 subroutines in one?
by Athanasius (Monsignor) on Apr 03, 2014 at 12:32 UTC

    The subroutine needs some way of knowing (or, at least, guessing) whether it has been passed an array or an array reference.

    First thought is to test whether the first argument is an array reference:

    if (ref($_[0]) eq 'ARRAY') ...

    See ref. The problem with that approach is that it won’t work if the array passed into the subroutine happens to have an array reference as its first element.

    A better approach is to test whether the subroutine is called in list context:

    my @array_with_duplicates = wantarray ? @_ : @{ $_[0] };

    See wantarray. If the sub is called in list context (i.e., the caller is expecting it to return a list), then from the specification we can assume the array was passed in as a list. Otherwise, we assume the array was passed in as a reference.

    Here is how I would solve the problem:

    Note: this approach does not rely on sorting the list, but outputs the no-duplicates version in its original order. The specification as given does not mention sorting.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Thanks to both of you!
      I have tried this,which works if I pass the array as array, but why can't I make it work if I pass the array as reference to array
      sub clean_list { my $type_of_arg=ref(\@_); my @array_with_duplicates=(@_); my @array_no_duplicates; if($type_of_arg eq 'ARRAY') #if the list is passed as an ar +ray { my $previous = ''; while (scalar @array_with_duplicates > 0) { my $next = shift(@array_with_duplicates); if ($previous ne $next) { push(@array_no_duplicates, $next); $previous = $next; } } } elsif($type_of_arg eq 'REF') #if the list is passed as a refere +nce to an array { my %hash; @hash{@{$_[0]}}=(); #@{$_[0]}=keys %hash; @array_no_duplicates=keys %hash; } return (@array_no_duplicates); } open(IN, '<', 'ex5.acc') or die "Could not read file\n"; my @initial_array = <IN>; close IN; @initial_array = sort @initial_array; my @final_array = clean_list(@initial_array); #this works # @final_array = clean_list(\@initial_array); #this does not work # Save the clean table open(OUT, ">", "ex5.acc.CLEAN") or die "Could not create file\n"; print OUT @final_array; close OUT;

        Two obvious problems (there may be others):

        • @_ is the array of arguments passed into the subroutine, so \@_ will always be an array reference. You need ref($_[0]) which tests the first argument (but see my answer above as to why this may not be the best approach).

        • If the array is passed in as a reference, $type_of_arg will be the string 'ARRAY', indicating an array reference. If the array is passed in as a list (and if the first element of that list doesn’t happen to be a reference), $type_of_arg will be the empty string, not 'REF'. Again, see ref.

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1080927]
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (8)
As of 2014-09-03 02:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite cookbook is:










    Results (35 votes), past polls