in reply to Re^5: Combinations of lists, etc
in thread Combinations of lists to a hash

Thanks LanX.

I'm trying to modify your code to handle update #2 in my original post, i.e. this line:

@hash{@keys} = ('value2') x @keys
This test seems to work as I want it to:
perl -MData::Dump -e '@hash{(key1,key2)} = ({a=>1, b=>2},{a=>1,b=>2}); +dd \%hash' { key1 => { a => 1, b => 2 }, key2 => { a => 1, b => 2 } }
But when I try to multiply the values out, it doesn't:
perl -MData::Dump -e '@hash{(key1,key2)} = ({a=>1, b=>2}) x 2;dd \%has +h' do { my $a = { key1 => { a => 1, b => 2 }, key2 => 'fix' }; $a->{key2} = $a->{key1}; $a; }
And the debugger didn't shed any light for me, either.
What am I doing wrong, please?  And what's that 'fix' thing about?

Replies are listed 'Best First'.
Re^7: Combinations of lists, etc
by AnomalousMonk (Archbishop) on Oct 07, 2019 at 07:40 UTC

    Further to LanX's reply:   Consider the two statements
        @ra = ({a=>1, b=>2}, {a=>1, b=>2});
    and
        @ra = ({a=>1, b=>2}) x 2;
    In the first, two different anonymous array references are being constructed. In the second, a single anonymous array reference is constructed and then repeated twice.

    c:\@Work\Perl\monks\tel2>perl -wMstrict -MData::Dump -le "my @ra = ({a=>1, b=>2}, {a=>1, b=>2}); print qq{@ra}; ;; @ra = ({a=>1, b=>2}) x 2; print qq{@ra}; " HASH(0x1555cdc) HASH(0x1555e50) HASH(0x1555e20) HASH(0x1555e20)

    In

    c:\@Work\Perl\monks\tel2>perl -wMstrict -MData::Dump -le "my %hash; ;; @hash{ qw(key1 key2) } = ({a=>1, b=>2},{a=>1,b=>2}); dd \%hash; ;; @hash{ qw(key1 key2) } = ({a=>1, b=>2}) x 2; dd \%hash; " { key1 => { a => 1, b => 2 }, key2 => { a => 1, b => 2 } } do { my $a = { key1 => { a => 1, b => 2 }, key2 => 'fix' }; $a->{key2} = $a->{key1}; $a; }
    the  do { ... } business with the  'fix' in the second dd instance just reflects the fact that a hash reference  $a is built that needs to have its  'key2' key "fixed" later by a  $a->{key2} = $a->{key1}; statement that makes the value of 'key2' the same as 'key1': they're the same reference. (Update:  dd works by generating source code that | source code text that, when run thru eval, will reproduce the structure of the data being dumped.)

    Update: Clarified the update remark about  dd per LanX's comment here. Thanks, LanX!


    Give a man a fish:  <%-{-{-{-<

      > Further to LanX's reply:

      And further to your reply ... ;-)

      plain printing of the references reveals that the last are identical objects

      DB<61> @ra = ({a=>1, b=>2}, {a=>1, b=>2}); DB<62> print $_ for @ra HASH(0x35a4090)HASH(0x35a3ae8) # different IDs DB<63> @ra = ({a=>1, b=>2}) x 2; DB<64> print $_ for @ra HASH(0x35a3eb0)HASH(0x35a3eb0) # same IDs DB<65>

      > Update: dd works by generating source code that will reproduce the structure of the data being dumped.

      ... when run thru eval .

      There seems to be no better generic syntax to reproduce the case of duplicated references, than stuffing a fix placeholder string into it and fixing it later.

      An for completeness, here code to clone the hashes instead of copying their refs

      my $value = { a=>1, b=>2 }; # curly brackets to assign hashref! $hash{$_} = { %$value } for @keys; ### or alternatively my %value = ( a=>1, b=>2 ); # round brackets to assign list! $hash{$_} = { %value } for @keys;

      this kind of cloning works because % returns a list in listcontext, which is packed again into a new { hash }

      DB<78> x %$value 0 'a' 1 1 2 'b' 3 2 DB<79>

      debugger demo

      DB<72> x @keys 0 'Prefix1=A:b:1' 1 'Prefix1=A:b:2' 2 'Prefix1=A:c:1' 3 'Prefix1=A:c:2' DB<73> x $value = {a=>1, b=>2}; 0 HASH(0x35a48e8) 'a' => 1 'b' => 2 DB<74> $hash{$_} = { %$value } for @keys; DB<75> x \%hash 0 HASH(0x3598330) 'Prefix1=A:b:1' => HASH(0x35a50b8) 'a' => 1 'b' => 2 'Prefix1=A:b:2' => HASH(0x35a4c18) # <--- differing IDs 'a' => 1 'b' => 2 'Prefix1=A:c:1' => HASH(0x35a46a8) 'a' => 1 'b' => 2 'Prefix1=A:c:2' => HASH(0x35a4a50) 'a' => 1 'b' => 2 DB<77>

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

        And further to your further to my further to... wait... what?!? :)

        $hash{$_} = { %$value } for @keys;
        ...
        $hash{$_} = { %value } for @keys;

        I always think it's worth pointing out in discussions on this topic that something like  { %$value } or  { %value } only does a shallow copy.


        Give a man a fish:  <%-{-{-{-<

      Thanks for that, AnomalousMonk.

      "In the first, two different anonymous array references are being constructed. In the second, a single anonymous array reference is constructed and then repeated twice."

      So how can we use this concise & convenient "x 2" syntax to generate the former?  I've just proposed a workaround here, but maybe there's a cleaner method.

      Update: It looks as if I don't actually need an alternative, because the later should work for my needs, since I'll only be reading it, according to LanX.  But I'm still interested if you know of a concise efficient alternative, which would allow reading & writing.

        "In the first, two different anonymous array references are being constructed. In the second, ..."

        So how can we use this concise & convenient "x 2" syntax to generate the former?

        I think you understand this already, so I'll answer just for the sake of completeness: You can't. The  x operator (see perlop) makes exact copies either of the elements of a list, the copies of which form another list, or of a string | a scalar, which is a string or stringized "thing" (number, reference, whatever), the copies of which are then contatenated together. And it's not possible to get different things if you're making exact copies of a thing (if you're doing it right).


        Give a man a fish:  <%-{-{-{-<

        > But I'm still interested if you know of a concise efficient alternative, which would allow reading & writing.

        We already showed you:

        $hash{$_} = { %$value } for @keys;

        or

        $hash{$_} = { %value } for @keys;

        The only concise alternative which comes to mind is

        @hash{ @keys } = map { { %value } } @keys

        But it's neither shorter nor easier to read.

        Cheers Rolf

        update

        But honestly you should stick to syntax you understand, even if it takes some lines more.

Re^7: Combinations of lists, etc
by LanX (Saint) on Oct 07, 2019 at 07:26 UTC
    > What am I doing wrong, please?

    The output looks right to me. You are assigning the same hashref again.

    Do you need the hashes to different instances with same content?

    Then you need to loop over all keys and always assign a new copy { %$hashref }

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      Thanks for that, LanX,

      "Do you need the hashes to different instances with same content?"
      If I understand your question correctly, then yes.  See example in update #2 of my original post.  It's a hash of a hash and the inner hashes have the same keys and values (for a given line of input data).

      After some more experimenting, since multiplying the right hand side (e.g. with 'x2') is not going to work (the reason for which still isn't clear to me), then what would be wrong with this kind of strategy: Multiply a string first then eval it:

      perl -MData::Dump -e '$val="(".("{(a,1),(b,2)},"x2).")";@hash{(key1,ke +y2)} = eval $val;dd $val;dd \%hash' "({(a,1),(b,2)},{(a,1),(b,2)},)" { key1 => { a => 1, b => 2 }, key2 => { a => 1, b => 2 } } # Or more concisely: perl -MData::Dump -e '@hash{(key1,key2)} = eval "(".("{(a,1),(b,2)},"x +2).")";dd \%hash'
      At least it seems to do what's required.  I know there's a trailing ',' in $val which could easily be removed, but doesn't seem to be causing a problem with the resulting hash.

        The only way I can see right now to deal with * vis-a-vis glob is to replace it with a character that occurs nowhere else in your data. So maybe (if tybalt89 hasn't already solved this):

        c:\@Work\Perl\monks\tel2>perl -wMstrict -MData::Dump -le "use constant NEVER_PRESENT => qq{\x01}; ;; my $s = 'Prefix5,Prefix6:A,B:*:1,23 value7=10|value88=123'; ;; $s =~ s{ [*] }{${ \NEVER_PRESENT }}xmsg; ;; my ($keyule, $valule) = split ' ', $s, 2; ;; $keyule =~ s{ : }[}:{]xmsg; $keyule = qq[{$keyule}]; print qq{'$keyule'}; $valule =~ s{ [|] }{,}xmsg; $valule = qq[{$valule}]; print qq{'$valule'}; ;; my %hash; for my $v (glob $valule) { my ($vk, $vv) = split '=', $v; for my $k (glob $keyule) { $k =~ s{ ${ \NEVER_PRESENT } }{*}xmsg; $hash{$k}{$vk} = $vv; } } dd \%hash; " '{Prefix5,Prefix6}:{A,B}:{&#9786;}:{1,23}' '{value7=10,value88=123}' { "Prefix5:A:*:1" => { value7 => 10, value88 => 123 }, "Prefix5:A:*:23" => { value7 => 10, value88 => 123 }, "Prefix5:B:*:1" => { value7 => 10, value88 => 123 }, "Prefix5:B:*:23" => { value7 => 10, value88 => 123 }, "Prefix6:A:*:1" => { value7 => 10, value88 => 123 }, "Prefix6:A:*:23" => { value7 => 10, value88 => 123 }, "Prefix6:B:*:1" => { value7 => 10, value88 => 123 }, "Prefix6:B:*:23" => { value7 => 10, value88 => 123 }, }
        Ok, now what's the next phase in the dance-of-the-seven-veils requirement disclosure?

        Update: BTW: I'm still not convinced that glob is the way to go with this application. An algorithmic permuter (update: such as haukex describes) or parser would be more to my liking, but it's your project.


        Give a man a fish:  <%-{-{-{-<

        > If I understand your question correctly, then yes.

        I'm afraid you don't.

        The old approach is fine as long as you only read from the data structure without changing it...

        (The fact that the output of Data::Dump is confusing you doesn't mean it doesn't "work".)

        DB<82> $hash{$_} = $value for @keys; DB<83> x \%hash 0 HASH(0x3598330) 'Prefix1=A:b:1' => HASH(0x35a48e8) 'a' => 1 'b' => 2 'Prefix1=A:b:2' => HASH(0x35a48e8) -> REUSED_ADDRESS 'Prefix1=A:c:1' => HASH(0x35a48e8) -> REUSED_ADDRESS 'Prefix1=A:c:2' => HASH(0x35a48e8) -> REUSED_ADDRESS DB<84> p $hash{'Prefix1=A:b:1'}{a} 1 DB<85> p $hash{'Prefix1=A:b:1'}{b} 2 DB<86> p $hash{'Prefix1=A:c:2'}{b} # reading works! 2 DB<87>

        but once you try to add more data it'll be mirrored, because all values of the outer hash point to the same inner hash.

        (Like a symlinked directory if this helps)

        DB<87> p $hash{'Prefix1=A:c:2'}{X}=42 # new entry 42 DB<88> x \%hash 0 HASH(0x3598330) 'Prefix1=A:b:1' => HASH(0x35a48e8) # appears everywhere 'X' => 42 'a' => 1 'b' => 2 'Prefix1=A:b:2' => HASH(0x35a48e8) -> REUSED_ADDRESS 'Prefix1=A:c:1' => HASH(0x35a48e8) -> REUSED_ADDRESS 'Prefix1=A:c:2' => HASH(0x35a48e8) -> REUSED_ADDRESS DB<89>

        So in case you want to store more data inside the inner hashes, which are supposed to be different, you have to clone the hashes.

        > what would be wrong with this kind of strategy:

        Sorry, I don't have even time to discuss all the traps here.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice