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

I have the following data from a file, list.txt, where each element in the list has a non-numerical value associated with it, like so:

A X B Y C Z D X E X

I want to create a matrix where if any pair of elements have the value X (XX, XY, XZ) the code assigns 0 to that spot in the matrix, or otherwise it assigns 2 to it.

I realize I need to set arrays first, so I started with this (NB: code is incomplete, so don't expect it to run):

use strict; use warnings; open(IN, "list.txt") or die; chomp($line = <IN>); ($a, @ss) = split /\s+/, $line; while (chomp($line = <IN>)) { ($aa, @prs) = split /\s+/, $line; for ($i=0; $i<@prs; $i++) { $s{$aa}{$ss[$i]} = $prs[$i]; } } close(IN);

But I would appreciate any input on 1) code correctness, and 2)assigning numerical values to non-numerical pairs.

The expected output is a matrix with 0s and 2s, like so

A B C D A 0 0 0 0 B 0 2 2 0 C 0 2 2 0

Replies are listed 'Best First'.
Re: matrices and non-numerical values
by GrandFather (Saint) on Apr 30, 2012 at 01:08 UTC

    Just two things I can be bothered commenting on:

    Prints:

    1. while (chomp($line = <IN>)) will not work as you expect. Read the chomp documentation
    2. use sensible identifiers and at least fix the strictures errors before you post.
    True laziness is hard work
      use strict; use warnings; open(IN, "list.txt") or die; chomp($line = <IN>); ($element, @abc) = split /\s+/, $line; while (chomp($line = <IN>)) { ($values, @xyz) = split /\s+/, $line; for ($i=0; $i<@xyz; $i++) { $s{$values}{$abc[$i]} = $xyz[$i]; } } close(IN);

      I made the identifiers sensible, but I am not sure what to assign as a explicit package for i, lines 6-10. My understanding is that it should get that information from the file being invoked.

        Your code and logic is rather convoluted.

        I offer:

        use strict; use warnings; my %element; while (<DATA>){ my ($elem, $value) = split; $element{$elem}= $value; } print " "; print "$_ " for sort keys %element; print "\n"; for my $k1(sort keys %element){ print "$k1 "; for my $k2(sort keys %element){ my $val = $element{$k1} eq "X" ? 0 : $element{$k2} eq "X" ? 0 : 2; print $val . " "; } print "\n"; } __DATA__ A X B Y C Z D X E X

                     I hope life isn't a big joke, because I don't get it.
                           -SNL

        Can you please show the output you get when you run this code?

Re: matrices and non-numerical values
by stevieb (Canon) on Apr 30, 2012 at 02:39 UTC

    I didn't test NetWallah's solution, but I thought I'd take a sec to try to help you understand why we know you didn't test your own code, and show you a couple more updated approaches to writing Perl.

    You should use the three argument form when opening a file, and be ready to spit the reason if the open fails, like this:

    open my $fh, '<', 'list.txt' or die "Can't open list.txt: $!";

    Note that I used a scalar ($fh) to accept the File Handle instead of a bareword ('IN', in this case). The '<' signifies that the file is to be opened in read-only mode.

    Also, if you ran your code, you would have found that under the 'strict' pragma, you need to make your declared/defined variables lexical. Most commonly, we'd use 'my' to do so:

    my ( $a, @ss ) = split ...

    One last thing... within a while loop when iterating through a file, it is idiomatic to write it like this (note the 'my'):

    while ( my $line = <$fh> ){ chomp $line; print "$line\n"; }
      I didnt get your question in the first place. The confusion here is the data you gave is
      A X B Y C Z D X E X
      where A has X, B has Y, C has Z .... But, the output you showed is a 3x4 matrix. Can you elaborate the question? (especially the correlation between the given data and the output you desired)
Re: matrices and non-numerical values
by jwkrahn (Abbot) on Apr 30, 2012 at 06:20 UTC

    I would appreciate any input on 1) code correctness

    use strict; use warnings; open(IN, "list.txt") or die; chomp($line = <IN>); ($a, @ss) = split /\s+/, $line; while (chomp($line = <IN>)) { ($aa, @prs) = split /\s+/, $line; for ($i=0; $i<@prs; $i++) { $s{$aa}{$ss[$i]} = $prs[$i]; } } close(IN);

    I would write that code as:

    use strict; use warnings; open my $IN, '<', 'list.txt' or die "Cannot open 'list.txt' because: $ +!"; my ( undef, @ss ) = split ' ', <$IN>; my %s; while ( <$IN> ) { my ( $aa, @prs ) = split; @{ $s{ $aa } }{ @ss } = @prs; } close $IN;