stu96art has asked for the wisdom of the Perl Monks concerning the following question:

I have a list of x and y values. I am trying to make a list of all the numbers (x's and y's) without repeating. I have very crude code, and I was looking for suggestions. It does what I want it to do, but I would like to make it a tad bit more dilagent. I would also like to get rid of the extra entries in the array @line. The final ones that do not get used have the value of zero, and there is not really a need for them. Thanks anyone who can help.
use strict; use warnings; use Data::Dumper; open ( OHA, ">c:/printoutLIST1.txt" ) or die ("could not open file. &! +" ); my @lite; my ($x, $y); # $lite[0][0] = x value # $lite[0][1] = y value # $lite[0][2] = (used?) 0 = not used, 1 = used # $lite[0][3] = x location in grid (lower left corner) # $lite[0][4] = y location in grid (lower left corner) # $lite[0][5] = (rotated?) 0 = not rotated, 1 = rotated $lite[0][0] = 2; $lite[0][1] = 5; $lite[0][2] = 0; $lite[0][3] = 999; $lite[0][4] = 999; $lite[0][5] = 0; $lite[1][0] = 4; $lite[1][1] = 3; $lite[1][2] = 0; $lite[1][3] = 999; $lite[1][4] = 999; $lite[2][0] = 1; $lite[2][1] = 4; $lite[2][2] = 0; $lite[2][3] = 999; $lite[2][4] = 999; $lite[3][0] = 4; $lite[3][1] = 1; $lite[3][2] = 0; $lite[3][3] = 999; $lite[3][4] = 999; my @line; for my $i (0..2*$#lite) { $line[$i] = 0; } for my $j (0..$#lite) { # loop through all possibilities for my $k (0..$#line) { # check if x is already in the list if ($lite[$j][0] == $line[$k]) { # if x is in list-get out goto OUT; } } for my $l (0..$#line) { # find where to place x if ($line[$l] == 0) { $x = $l; goto OUT2; } } OUT2: $line[$x] = $lite[$j][0]; OUT: for my $m (0..$#line) { # check if y is already in list if ($lite[$j][1] == $line[$m]) { # if y is in list-get out goto OUT3; } } for my $n (0..$#line) { # find where to place y if ($line[$n] == 0) { $y = $n; goto OUT4; } } OUT4: $line[$y] = $lite[$j][1]; OUT3: } for my $p (0..$#line) { for my $o (0..$#line) { select OHA; print "LINE $o [$line[$o]]\n"; select STDOUT; }

20040124 Edit by BazB: Changed title from 'there must be an easier way'

Replies are listed 'Best First'.
Re: An easier way to construct lists of numbers?
by Chady (Priest) on Jan 24, 2004 at 14:59 UTC

    Your code does not run and has a lot of errors, anyway, if I understand your code correctly, here's something that will work for you.

    use Data::Dumper; # note that you do not need to explicitly fill in the # elements of the array one by one, make use of # perl's lists. my @lite = ( [qw/2 5 0 999 999 0/], [qw/4 3 0 999 999/], [qw/1 4 0 999 999/], [qw/1 4 0 999 999/], [qw/4 1 0 999 999/] ); my %h; foreach (@lite) { # construct a key made of the X value x the Y # value and count on that being uniq. my $xy = $_->[0] . 'x' . $_->[1]; # hash keys are uniq, # so we make the value an array reference # containing the original x and y values. $h{$xy} = [$_->[0], $_->[1]]; } # just retrieve the values from the hash. my @filtered = values %h; print Dumper(\@filtered);

    He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.

    Chady | http://chady.net/
      I think he just wants to search the numbers and doesn't care whether it's x or y. So your hash asignment becomes:
      ++$h{$_->[0]}; ++$h{$_->[1]};
      and the result is simply printed with:
      print join(' ',sort keys %h),"\n";
      Very nice, Chady.

      I'd like to add a few things:

      First, to filter out the unused ($lite[$something][5]) you can change:

      $h{$xy} = [$_->[0], $_->[1]];
      to:
      $h{$xy} = [$_->[0], $_->[1]] if $_->[5];

      Second, when you write a loop, you should try to include only the essential stuff in it, so your program runs faster and consumes less resources. so this:

      for my $o (0 .. $#line) { select BLA print ... select STDOUT }

      can become:
      select BLA; for my $o (0 .. $#line) { print ... } select STDOUT;
      But it's also possible to give a file handle to print, like so: print HANDLE "Something\n"; and then you don't have to select and reselect at-all.

      perl -e'$b=unpack"b*",pack"H*","59dfce2d6b1664d3b26cd9969503";\ for(;$a<length$b;$a+=9){print+pack"b8",substr$b,$a,8;}'
      My public key
Re: An easier way to construct lists of numbers?
by graff (Chancellor) on Jan 24, 2004 at 15:37 UTC
    Chady's reply (with Skeeve's amendment) basically shows you the code that you're trying for. To elaborate a little more on his brief comment:

    Your code as originally posted contained the start of at "for" loop (near the end) that was never terminated. (That was the only syntax error that made it impossible to run.)

    But apart from that, your use of "select" in the last "for" loop is quite wasteful and unnecessary -- when you want to write to a specific file, just use the file handle in the print statement:

    open( OUT, ">file.name" ) or die "can't write to file.name:$!"; print OUT "Line $_ : [$line[$_]]\n" for (0..$#line); close OUT;
    Your various statement labels and goto's were unnecessary as well -- again, don't use such things when you don't have to.

    Also, as Chady demonstrated, your array initialization does not require all the typing you did in your original code -- and you don't need to do anything to initialize the size of an array -- Perl will grow the array for you as you assign values to array elements, and there are many ways to declare an initial set of array values with relatively little typing.

      Thanks for everyone's help. That is exactly what I was looking for. Thanks for the help with filling arrays and printing to filehandles. Thanks again.