[comp.lang.perl] Best way to nullify an intersection

logan@rockville.dg.com (James L. Logan) (07/03/90)

Is there a better or faster way to drop all elements in set B that
intersect with set A?  Here's what I've got so far:

	open(FILE1, "file1") || die;
	@A = <FILE1>;
	close(FILE1) || die;

	open(FILE2, "file2") || die;
	@B = <FILE2>;
	close(FILE2) || die;

	foreach $element (@A) {
		foreach (@B) {
			if (/^$element$/) {
				$_ = '';
				last;
			}
		}
	}
	print @B;

Also, how can I delete the element that was matched in @B, while
also shortening the array by one element; rather than just
setting the element to the null string?  

			Thanks,
			-Jim
-- 
James Logan                        UUCP: uunet!inpnms!logan
Data General Telecommunications    Inet: logan@rockville.dg.com
2098 Gaither Road                 Phone: (301) 590-3198
Rockville, MD 20850

tneff@bfmny0.BFM.COM (Tom Neff) (07/03/90)

In article <601@inpnms.ROCKVILLE.DG.COM> logan@rockville.dg.com (James L. Logan) writes:
>Is there a better or faster way to drop all elements in set B that
>intersect with set A?  Here's what I've got so far:
 ...
>	foreach $element (@A) {
>		foreach (@B) {
>			if (/^$element$/) {
>				$_ = '';
>				last;
>			}
>		}
>	}
 ...

Try this:

	@B=grep(!do {$x=$_; grep($x eq $_,@A);},@B);

>Also, how can I delete the element that was matched in @B, while
>also shortening the array by one element; rather than just
>setting the element to the null string?  

Should do it.




-- 
"UNIX should be used          ::   Tom Neff <tneff@bfmny0.BFM.COM> or
 as an adjective." -- AT&T   ::    ...uunet!bfmny0!tneff (UUCP only)

tell@oscar.cs.unc.edu (Stephen Tell) (07/03/90)

In article <601@inpnms.ROCKVILLE.DG.COM> logan@rockville.dg.com (James L. Logan) writes:
>Is there a better or faster way to drop all elements in set B that
>intersect with set A?  Here's what I've got so far:
>	open(FILE1, "file1") || die;
>	@A = <FILE1>;
>	close(FILE1) || die;
>
>	open(FILE2, "file2") || die;
>	@B = <FILE2>;
>	close(FILE2) || die;
>
>	foreach $element (@A) {
>		foreach (@B) {
>			if (/^$element$/) {
>				$_ = '';
>				last;
>			}
>		}
>	}
>	print @B;

>Also, how can I delete the element that was matched in @B, while
>also shortening the array by one element; rather than just
>setting the element to the null string?  

Its called splice(@array, offset, length, [replace])
"Delete length elements from @array starting at offset and return them,
optionaly replacing them with new elements."

>			Thanks,
>			-Jim

This is my first comp.lang.perl posting with a coding suggestion, so be
gentle, guys!
It seems that using an associative array to say "did we see this anywhere"
would help a lot over the O(n**2) approach above.  You said these are
sets, if the order in which B gets printed matters, this goes out the window.
But try:

	open(FILE1, "file1") || die;
	@A=<FILE1>;
	close(FILE1) || die;

	open(FILE2, "file2") || die;
	while(<FILE2>) {
		$B{$_} = 1;
 	}
	close(FILE2) || die;

	foreach $element (@A) {
		if ($B{$element} {
			delete($B{$element});
		}
	}
	print keys(%B);

Is there a better way to read FILE2 into an associative array like this?
Perhaps the ability to say
keys(%B) = <FILE2>;
--------------------------------------------------------------------
Steve Tell      e-mail: tell@wsmail.cs.unc.edu usmail:  #5L Estes Park apts
CS Grad Student, UNC Chapel Hill.                       Carrboro NC 27510

merlyn@iwarp.intel.com (Randal Schwartz) (07/04/90)

In article <601@inpnms.ROCKVILLE.DG.COM>, logan@rockville (James L. Logan) writes:
| Is there a better or faster way to drop all elements in set B that
| intersect with set A?  Here's what I've got so far:
| 
| 	open(FILE1, "file1") || die;
| 	@A = <FILE1>;
| 	close(FILE1) || die;
| 
| 	open(FILE2, "file2") || die;
| 	@B = <FILE2>;
| 	close(FILE2) || die;
| 
| 	foreach $element (@A) {
| 		foreach (@B) {
| 			if (/^$element$/) {
| 				$_ = '';
| 				last;
| 			}
| 		}
| 	}
| 	print @B;
| 
| Also, how can I delete the element that was matched in @B, while
| also shortening the array by one element; rather than just
| setting the element to the null string?  

Well, I'd attack it something like this:

open(F1,"file1") || die "cannot open file1: $!";
while (<F1>) {
	$saw{"$_"}++;
}
close(F1);

open(F2,"file2") || die "cannot open file2: $!";
while (<F2>) {
	print unless $saw{$_};
}
close(F2);

Using the keys of an assoc array as a set-type is a very handy idiom.

print "Just another Perl hacker,"
-- 
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
| merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Welcome to Portland, Oregon, home of the California Raisins!"=/

logan@rockville.dg.com (James L. Logan) (07/04/90)

In article <15631@bfmny0.BFM.COM> tneff@bfmny0.BFM.COM (Tom Neff) writes:
# 
# 	@B=grep(!do {$x=$_; grep($x eq $_,@A);},@B);
# 

Could someone explain this construct?  I've only been using perl
for a week now...  

			-Jim
-- 
James Logan                        UUCP: uunet!inpnms!logan
Data General Telecommunications    Inet: logan@rockville.dg.com
2098 Gaither Road                 Phone: (301) 590-3198
Rockville, MD 20850

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (07/04/90)

In article <604@inpnms.ROCKVILLE.DG.COM> logan@rockville.dg.com (James L. Logan) writes:
: In article <15631@bfmny0.BFM.COM> tneff@bfmny0.BFM.COM (Tom Neff) writes:
: # 
: # 	@B=grep(!do {$x=$_; grep($x eq $_,@A);},@B);
: # 
: 
: Could someone explain this construct?  I've only been using perl
: for a week now...  

That's just a funny way of writing two nested loops.  It's more or less
equivalent to

    @outer_result = ();
    foreach $_ (@A) {
	$x = $_;
	@inner_result = ();
	foreach $_ (@B) {
	    push(@inner_result,$_) if $x eq $_;
	}
	push(@outer_result, $_) if @inner_result;
    }
    @B = @outer_result;

As such, it's just another O(n**2) algorithm.  Better to use the associative
array method mentioned earlier, which is basically a linear algorithm.

Larry