in reply to How to combine these 2 subroutines in one?

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:

#! perl use strict; use warnings; use Data::Dump; my @array; while (<DATA>) { chomp; push @array, $_; } dd \@array; @array = clean(@array); # or clean(\@array); dd \@array; sub clean { my @array_with_duplicates = wantarray ? @_ : @{$_[0]}; my (@array_no_duplicates, %hash); for (@array_with_duplicates) { unless (exists $hash{$_}) { push @array_no_duplicates, $_; $hash{$_} = undef; } } return @array_no_duplicates if wantarray; @{ $_[0] } = @array_no_duplicates; } __DATA__ Frieda Adele Briony Hermione Cathy Dorothy Cathy Erin Ida Frieda Frieda Gertrude Hermione

Output:

22:14 >perl 890_SoPW.pl [ "Frieda", "Adele", "Briony", "Hermione", "Cathy", "Dorothy", "Cathy", "Erin", "Ida", "Frieda", "Frieda", "Gertrude", "Hermione", ] [ "Frieda", "Adele", "Briony", "Hermione", "Cathy", "Dorothy", "Erin", "Ida", "Gertrude", ] 22:14 >

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,

Replies are listed 'Best First'.
Re^2: How to combine these 2 subroutines in one?
by Anonymous Monk on Apr 03, 2014 at 13:31 UTC
    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,

        Aha, I see, will look further in my code, thank you for the tips :)