> -----Original Message-----
> From: Merritt Krakowitzer [mailto:[EMAIL PROTECTED]]
> Sent: Wednesday, July 24, 2002 4:19 AM
> To: Bob Showalter; 'Timothy Johnson'; 'Beginners '
> Subject: RE: Comparing Arrays
> 
> > I just found out I can shorten my one-liner to:
> >
> >    @foo==@bar && "@{{map {$_, $_} @foo}}{@bar}" eq "@bar"
> 
> Thanks, this was perfect for my situation, the arrays contain 
> file listing
> in directories,
> so there cant be duplicates. Well unless u have a file like 
> "foo" and "foo "
> but thats highly unlikely i think.
> 
> I was hoping you could explain your one liner to me from what 
> i can tell, it
> *should*
> be giving an error about being unable to coerce an array to a 
> hash, but it
> seems to
> work fine, it's driving me nuts :)

OK, here's how it works.

   @foo==@bar && "@{{map {$_, $_} @foo}}{@bar}" eq "@bar"
   ^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     Part A                      Part B

Part A checks that the length of the two arrays is the same.
The use of "==" causes the two operands to be evaulated in
scalar context. Since an array name in scalar context evaluates
to the number of elements in the array, we are comparing the
lengths of the arrays.

Part B checks that @foo contains each of the elements in @bar.
Let's break it down:

   map {$_, $_} @foo

map() constructs a list by evaluating the block repeatedly for
each element in the input array, @foo. The block is evaulated in
list context, so can return multiple values. Inside the block, $_
is aliased to the current value from @foo being processed. So
the block {$_, $_} returns a pair of values for each input value.
If @foo contains qw(foo bar baz), then the output of map will be
qw(foo foo bar bar baz baz).

   { map {$_, $_} @foo }

The braces here are an anonymous hash constructor. This turns the
list into a reference to a hash. So, if @foo was qw(foo bar baz),
we now have a hash that looks like foo=>'foo', bar=>'bar', baz=>'baz'

   @{{map {$_, $_} @foo}}{@bar}

This is a hash slice operation. The basic format is:

   @{hashref}{list}

The hashref is the anonymous hash created above. The list is the list
of values from @bar. The hash slice returns a list of *values* from the
hash which correspond to the *keys* named in the second list (i.e. @bar).

Since the hash values and hash keys are the same (from the map()
operation), and are just the entries from @foo, the result of the 
hash slice is a list of @foo entries matching each @bar entry, and
in the order they appear in @bar. Now, if @bar contains an entry 
NOT found in the hash, the slice operation will return undef in 
that position in the list.

For example, if @foo is qw(foo bar baz) and @bar is qw(baz qux foo),
the hash slice will return the list 'baz', undef, 'foo'.

Now the trick is to just compare these two lists.

   "@{{map {$_, $_} @foo}}{@bar}" eq "@bar"

By enclosing the arrays in double quotes, I'm taking advantage of
Perl's variable interpolation. When an array appears in double
quotes, its elements are concatenated, with elements separated by
the value of $" (by default, a space). So, in the example above,
this expands to:

   "baz  foo" eq "baz qux foo"

(The undef in the second position expands to a zero-length string)

Since these strings aren't equal, the two arrays aren't equal.

Part A (checking the length of the arrays) is done to make sure
@foo doesn't have "extra" elements not in @bar. The hash slicing
business would not detect that.

There may be other defects in this besides the duplicate issue I
mentioned.

-- 
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to