http://www.perlmonks.org?node_id=1081335

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

Hey, So I'm trying to make a lower triangular distance matrix in an itterative fashion. I had a function hd(a,b) which returns the distance between two objects a and b. I then want to iteratively add rows to this matrix as more objects are added, but I want to only add new rows, not recalculate the old ones. so I wrote this
$hd::size1 = scalar @{\@{$main::M[0]}}; $hd::size = $hd::size1 - 1; for ($hd::i=$hd::save; $hd::i<= $hd::size; $hd::i++) #this creates + and adds to the hd array { $hd::store= []; for ($hd::j=0; $hd::j < $hd::i; $hd::j++) { $hd::temp = &hd($main::M[0][$hd::i], $main::M[0][$hd::j]); print $hd::temp; print "\n"; push $hd::store, $hd::temp; } push @{$main::hd}, $hd::store; } $hd::save = $hd::size
for reference, main::M stores all the objects, and $main::hd is initialized as [];, and hd::save is initialized at 1 and updated each time the loops completes with hd::save = hd::size; Whenever I follow this up with a module, it says the the first row of my matrix has no columns. but, when I use the following code (something I wrote for myself for self-reference)
#!/usr/bin/perl #ex2 use warnings; use strict; sub clust; $main::hd=[[]]; $main::a = [0.25]; $main::b = [0.25,0.5]; $main::C = [1,0.75,0.75]; $main::hdt = 0.4; $main::c = []; $main::c1 = 1; $main::c2 = 0.75; $main::c3 = 0.75; #how to add elements to an array push $main::c, $main::c1; push $main::c, $main::c2; push $main::c, $main::c3; #how to add arrays to arrays push @{$main::hd}, $main::a; push @{$main::hd}, $main::b; push @{$main::hd}, $main::c; &clust; for ($loop::i=0; $loop::i<=3; $loop::i++) { print $clust::cluster_ids->[$loop::i], "\n"; } sub clust { use Algorithm::Cluster::Thresh; use Algorithm::Cluster qw/treecluster/; $clust::tree = treecluster(data=>$main::hd, method=>'a'); $clust::cluster_ids = $clust::tree->cutthresh($main::hdt); }
The module works perfectly. I think the error is how I'm iteratively storing things. Any ideas? EDIT: The error was in my definition of hd::save. I orginally defined it as $hd::size, but that causes it to recalculate the last object from the previous time step. Changing it to $hd::save=$hd::size +1; fixed everything. This meant the matrix was not diagonal, it had weird kinks in it. Thanks everyone who helped!!!!!

Replies are listed 'Best First'.
Re: Array storage issue
by hippo (Bishop) on Apr 06, 2014 at 21:44 UTC

    One obvious question: why do you have this pathological aversion to lexicals?

      Even if he doesn't like lexicals, using package and our would facilitate reading his code.

      I suppose he saw strict complaining about undeclared variables and took it literally. :(

      like:

      > perl use strict; $x=23; __END__ Global symbol "$x" requires explicit package name at - line 2.

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        My issue isn't strict, I don't know why everyone says that. I have a small sample program that works perfectly, but I'm trying to merge something like it into the main code I'm working on. I think my problem is in the hd::store=[]; statement. When I push things into another array, does it push the values or just a reference to the values? Can I make it push just the values?
      Sorry, I don't know what that is. All the perl I know I've self-taught from online function guides. I write things this way because it works, I'm not great at it.

        The unwisdom of your beguilement with package globals may be demonstrated by the following code. Can you explain the difference between the outputs of the two loops?

        c:\@Work\Perl\monks>perl -wMstrict -e "for ($loop::i = 0; $loop::i < 3; $loop::i++) { clust($loop::i) } print qq{\n}; ;; for (my $i = 0; $i < 3; $i++) { clust2($i); } ;; ;; sub clust { my ($n) = @_; ;; for ($loop::i = 0; $loop::i < 3; $loop::i++) { my $m = $n * $loop::i; print qq{= $m }; } print qq{\n}; } ;; sub clust2 { my ($n) = @_; ;; for (my $i = 0; $i < 3; $i++) { my $m = $n * $i; print qq{- $m }; } print qq{\n}; } " = 0 = 0 = 0 - 0 - 0 - 0 - 0 - 1 - 2 - 0 - 2 - 4

        Global data is, IMHO, always problematic. If you are looking for up-to-date on-line general introductory tutorials, I would suggest perlintro, perhaps followed by chromatic's freely downloadable Modern Perl.

        Sorry, I don't know what that is. All the perl I know I've self-taught from online function guides. I write things this way because it works, I'm not great at it.

        When you get the time :) Tutorials has a bunch

        Modern Perl by chromatic a loose description of how experienced and effective Perl 5 programmers work....You can learn this too.

        Learn Perl in about 2 hours 30 minutes a tutorial reviewed and recommended by Perl Tutorial Hub

        All of these, like perlintro, teach about Coping with Scoping , using lexical variables, because when programs grow beyond one screen , you'll want and argument passing

      Oh, if you mean why do I define all my variables with packages like main? Because when I first started perl, whenever I tried to use "my" my program wouldn't work. So I just started giving things packages, and haven't stopped. It just seems more stable.
        ... when I first started perl, whenever I tried to use "my" my program wouldn't work.

        It would have been very useful to have explored the reasons why your use of lexical variables wouldn't work. It's not too late to begin!

        ... packages ... just [seem] more stable.

        Package data is global data. Global data is, IMHO, always problematic.

        The story goes that during one of the Pearl Harbor attacks on December 7, 1941, General Walter Short, commander of the Army forces in Pearl, was struck in the chest by a spent 50-caliber machinegun round and knocked to the ground. The bullet's impact didn't even break the skin, but when someone picked it up and showed it to Short, he said "It would have been better if it had killed me."

        When the day comes (and come it will if it has not already) that you are knocked on your ass by a chunk of global data at the end of a long trajectory from its point of origin, you may have some of the same feelings.

        > It just seems more stable.

        no they are definitely not.

        Package variables should be the well chosen exception, because they are global.

        And sorry your code is unreadable for me... so I can't help.

        Cheers Rolf

        ( addicted to the Perl Programming Language)

Re: Array storage issue
by Anonymous Monk on Apr 06, 2014 at 22:52 UTC

    You can use ddumperBasic debugging checklist to visualize the data structure you have (lesson courtesy of Basic debugging checklist and brian's Guide to Solving Any Perl Problem )

    If I do that I see that the first row is indeed empty [[], [0.25], [0.25, 0.5], [1, 0.75, 0.75]];

    Try to follow https://metacpan.org/source/MDEHOON/Algorithm-Cluster-1.52/perl/examples/ex5_treecluster

    This is how you should write that, pass arguments (often references to arrays), return values, meaningful names

    This is your code cleaned up, some comments, but the data unfixed (I would use ex5_treecluster data as starting point )

    #!/usr/bin/perl -- #~ clust-brain.pl #~ 2014-04-06-15:43:25 #~ #~ perltidy -olq -csc -csci=10 -cscl="sub : BEGIN END if " -otr -opr +-ce -nibc -i=4 -pt=0 "-nsak=*" #!/usr/bin/perl -- use strict; use warnings; use Data::Dump qw/ dd /; Main( @ARGV ); exit( 0 ); sub Main { ## compact notation ## first stored in an arrayref # my $hd = [[], [0.25], [0.25, 0.5], [1, 0.75, 0.75]]; ## then stored in a named array # my @the_hd = ([], [0.25], [0.25, 0.5], [1, 0.75, 0.75]); ## same thing more verbose notation ## array ref first my $hd_arrayref; $hd_arrayref->[1][0] = 0.25; #d1 $hd_arrayref->[2][0] = 0.25; #d1 $hd_arrayref->[2][1] = 0.5; #d1 $hd_arrayref->[3][0] = 1; #d1 $hd_arrayref->[3][1] = 0.75; #d1 $hd_arrayref->[3][2] = 0.75; #d1 ## named array second my @hd_array; $hd_array[1][0] = 0.25; #d1 $hd_array[2][0] = 0.25; #d1 $hd_array[2][1] = 0.5; #d1 $hd_array[3][0] = 1; #d1 $hd_array[3][1] = 0.75; #d1 $hd_array[3][2] = 0.75; #d1 ## putting values in an array ref without loops # my $cluster_ids = ( 0, 1, 2, 3 ); # my $cluster_ids = [ 0 .. 3 ]; my $hdt = 0.4; ## call clust_brain() function ... no need for short names #~ my $cluster_ids = clust_brain( $hd_arrayref, $hdt ); my $cluster_ids = clust_brain( \@hd_array, $hdt ); ## because we "import"ed dd from Data::Dump we don't have to type full + name # Data::Dump::dd( $cluster_ids ); dd( $cluster_ids ); } ## end sub Main sub clust_brain { ## ARGUMENT PASSING, first arg reference, second arg number my( $hd_ref, $some_hdt ) = @_; use Algorithm::Cluster::Thresh; use Algorithm::Cluster qw/treecluster/; ## you my $varname ONCE to make it my $tree = treecluster( data => $hd_ref, method => 'a' ); ## then you call method cutthresh on $varname with argument my $var_i_call_cluster_ids_also = $tree->cutthresh( $some_hdt ); ## you return the value return $var_i_call_cluster_ids_also; } ## end sub clust_brain __END__
      I'm going to go try this out now. I'll let you know how it turns out. A hopeful thanks in advance!!