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

First off I'm a beginner in perl
I have successfully read in the files into what I called: @List1 & @List2.

Now I need to take each element in List1 and subtract it from each element in List2 and count the number of times the abs of the difference is <= .2

For example
List1:
.2
.3
.4


List2:
.4
.5
.6


The count would be 6 (see below)
.2-.4=-.2
.2-.5=-.3
.2-.6=-.4

.3-.4=-.1
.3-.5=-.2
.3-.6=-.3

.4-.4= 0
.4-.5=-.1
.4-.6=-.2


Thanks for any help!

Replies are listed 'Best First'.
Re: Comparing masses in two txt files
by ikegami (Patriarch) on Nov 05, 2008 at 19:11 UTC
    You want to do something to every item in @List1 with every item in @List2, so you want nested loops.
    my $count = 0; for my $list1 (@List1) { for my $list2 (@List2) { if (abs($list1 - $list2) <= .2) { $count++ } } } print("$count\n");

    But beware that checking floating point numbers for equality is basically impossibe — Does anyone have a good link they can provide here? — so you might not get the results you want. .2, for example, is a periodic number in binary (just like 1/3 is a periodic number in decimal). You might need to introduce a tolerance.

    my $tol = 2**(-30); # ~ 0.000000001 my $count = 0; for my $list1 (@List1) { for my $list2 (@List2) { if (abs($list1 - $list2) - $tol <= .2) { $count++ } } } print("$count\n");
      But beware that checking floating point numbers for equality is basically impossibe
      But even if some real numbers cannot be exactly represented in binary, at least they are both exactly misrepresented in the same way, so checking for equality should still work.

      I thought that this was only a problem if you were calculating with them:

      print .2 == .2 ? 'equal' : 'not equal'; equal print .2 *10 == 2 ? 'equal' : 'not equal'; equal print .1999999999999999 *10 == 2 ? 'equal' : 'not equal'; not equal print .19999999999999999 *10 == 2 ? 'equal' : 'not equal'; equal print .20000000000000001 *10 == 2 ? 'equal' : 'not equal'; equal print .2000000000000001 *10 == 2 ? 'equal' : 'not equal'; not equal
      So on the Perl on my machine (AS Perl 5.8.8) .2 seems to be anything between .19999999999999999 and .20000000000000001.

      CountZero

      A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Comparing masses in two txt files
by GrandFather (Saint) on Nov 05, 2008 at 19:42 UTC

    In Perl it is very easy to 'slurp' files, and it is a really bad habit to get into. In this case one of the files needs to be slurped so try to make it the smallest. There is actually a hidden loop in the code below - the grep, but if you understand grep, then it does make the code much clearer.

    As a Perl beginner you should note that strictures should always be used (use strict; use warnings;), file opens should use the three parameter version and are checked, and that some checking (but not as much as there ought to be) is made on the input data.

    use warnings; use strict; my $List1 = <<DATA; .2 .3 .4 DATA my $List2 = <<DATA; .4 .5 .6 DATA open my $in1, '<', \$List1 or die "Failed to open List1: $!"; my @data1 = grep {chomp; length} <$in1>; close $in1; my $hitCount = 0; open my $in2, '<', \$List2 or die "Failed to open List2: $!"; while (<$in2>) { chomp; next unless length; my $data2 = $_; $hitCount += grep {abs ($data2 - $_) <= 0.2} @data1; } close $in2; print "Found $hitCount matches\n";

    Prints:

    Found 6 matches

    Perl reduces RSI - it saves typing
      I'm a bit puzzled by the use of length in this line.

      my @data1 = grep {chomp; length} <$in1>;

      I would have though you want the chomped data so just pass out $_ instead.

      my @data1 = grep {chomp; $_} <$in1>;

      Maybe I'm missing something obvious.

      Cheers,

      JohnGG

        The grep is there to skip blank lines hence the length. Putting the chomp in the grep in that fashion avoids a map:

        my @data1 = grep length, map chomp, <$in1>;

        You don't "pass out" the value from grep. It tests the result returned for each element of the source list to determine if the element is placed in the output list.


        Perl reduces RSI - it saves typing
Re: Comparing masses in two txt files
by jethro (Monsignor) on Nov 05, 2008 at 19:09 UTC

    Sounds like a homework assignement to me. I'll give you a hint:

    Use a foreach loop inside another foreach loop. One loop loops over @List1, the other over @List2

Re: Comparing masses in two txt files
by SuicideJunkie (Vicar) on Nov 05, 2008 at 19:08 UTC
    That sounds like a job for nested for loops.

    What code have you got so far?
Re: Comparing masses in two txt files
by CountZero (Bishop) on Nov 05, 2008 at 19:11 UTC
    For example:
    use strict; use warnings; my @firstlist = qw/.2 .3 .4/; my @secondlist = qw/.4 .5 .6/; my $hits; for my $first (@firstlist) { $hits += grep {abs($_ - $first) <= .2} @secondlist; } print "Result : $hits";

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James