2

I am trying to run array_diff on 2 arrays.

sub array_diff(\@\@) {
    my %e = map { $_ => undef } @{$_[1]};
    return @{[
        ( grep { (exists $e{$_}) ? ( delete $e{$_} ) : ( 1 ) } @{ $_[0] } ),
        keys %e
    ] };
}

my $col = 'col';
my $stg = 'stg';
my @blocks = qw( block1 block2 block3 block4 block5 );
my %hash; @{$hash{$col}{$stg}} = qw( block1 block2 block3 );

my @diff = array_diff(@all_blocks, @{$hash{$col}{$stg}});
print ("diff : @diff\n");

It gives me the following error when above line is executed:

Possible unintended interpolation of @diff in string at get_blocks.pl line 56.
Type of arg 2 to main::array_diff must be array (not reference constructor) at get_blocks.pl line 55, near "})"
syntax error at get_blocks.pl line 55, near "})"
Execution of get_blocks.pl aborted due to compilation errors.

However when I try the same without an array of hash it works

my @a = qw( a b c d e);
my @b = qw( c d  );

sub array_diff(\@\@) {
    my %e = map { $_ => undef } @{$_[1]};
    return @{[
        ( grep { (exists $e{$_}) ? ( delete $e{$_} ) : ( 1 ) } @{ $_[0] } ),
        keys %e
    ] };
}
# symmetric difference
my @diff = array_diff(@a, @b);
print ("diff : @diff\n");

Result:

diff : a b e
ikegami
  • 367,544
  • 15
  • 269
  • 518
Rancho
  • 309
  • 4
  • 12
  • I absolutely back Schwern's words to loose the prototypes and `@{[...]}`. (And also note how to work through errors.) And then, there are very good libraries which deal with relationships between arrays. (Now, I think it's great to roll your own -- but then compare it to a good library for interface, error checking, performance, features...) – zdim May 01 '20 at 21:45
  • Tip: `return @{[ ... ]}` is a weird way of doing `return ...;` (Well, they may behave different in scalar context, but not in this case because `grep` returns the number of matching elements in scalar context.) – ikegami May 01 '20 at 21:56
  • The code you have posted does not exhibit the behaviour you claim it does. Please fix your question – ikegami May 01 '20 at 22:01

1 Answers1

4

We can consult perldiag to figure out the errors and warnings.

Possible unintended interpolation of @diff in string at get_blocks.pl line 56.

(W ambiguous) You said something like '@foo' in a double-quoted string but there was no array @foo in scope at the time. If you wanted a literal @foo, then write it as \@foo; otherwise find out what happened to the array you apparently lost track of.

You're doing "@diff" but @diff is not declared.

Type of arg 2 to main::array_diff must be array (not reference constructor) at get_blocks.pl line 55, near "})"

(F) This function requires the argument in that position to be of a certain type. Arrays must be @NAME or @{EXPR} . Hashes must be %NAME or %{EXPR} . No implicit dereferencing is allowed--use the {EXPR} forms as an explicit dereference. See perlref.

Just as it says, the \@ prototype must take an array, not a reference.

# Bad
array_diff([1,2,3], [4,5,6]);

# Good
array_diff(@{[1,2,3]}, @{[4,5,6]});

Your sample code and your errors do not match. @diff is declared and you have de-referenced $hash{$col}{$stg}.


I would recommend against using prototypes. They're not like declaring a function's arguments in other languages. Instead, they're for emulating the magic behavior of built-in functions.

Instead, pass in the references.

my @diff = array_diff(\@all_blocks, $hash{$col}{$stg});

True subroutine signatures are unfortunately still experimental.


There's no need for the baby-cart operator @{[]} around grep. grep already returns a new array, there's no need to clone it. It complicates the code and wastes memory.

Community
  • 1
  • 1
Schwern
  • 153,029
  • 25
  • 195
  • 336