Array of arrays of arrays in qw representation in Perl - perl

Hello I'd like to form a data structure like this:
my #AoAoA = (
[ qw/ [a b] [c d] [e f] / ],
[ qw/ [r t] [m n] [k l] / ],
[ qw/ [z x] [b a] [p u] / ]
);
In this structure which I call array of arrays of arrays in qw representation, AoAoA for short, when I want to access the first array's first array's first value:
my $first_elt = #{$AoAoA[0]}[0];# supposed to be [a b]
my $first_val = #{$first_elt}[0];# supposed to be 'a'
print "$first_val\n";
it prints nothing. How sould I arrange it so I can access that value correctly? Thank you.

qw is short for quote words. It will implicitly quote every non-space substring for you, which is often very useful. But you can't add complex data structures using the same construct
Your assignment
my #AoAoA = (
[ qw/ [a b] [c d] [e f] / ],
[ qw/ [r t] [m n] [k l] / ],
[ qw/ [z x] [b a] [p u] / ]
)
is the same as
my #AoAoA = (
[ '[a', 'b]', '[c', 'd]', '[e', 'f]' ],
[ '[r', 't]', '[m', 'n]', '[k', 'l]' ],
[ '[z', 'x]', '[b', 'a]', '[p', 'u]' ]
)
which is unlikely to be what you want. Instead you must use qw// to define only the strings in your structure, like this
my #AoAoA = (
[ [ qw/ a b / ], [ qw/ c d / ], [ qw/ e f / ] ],
[ [ qw/ r t / ], [ qw/ m n / ], [ qw/ k l / ] ],
[ [ qw/ z x / ], [ qw/ b a / ], [ qw/ p u / ] ]
)
Then you can write
my $first_elt = $AoAoA[0][0]; # ['a', 'b']
my $first_val = $first_elt->[0]; # 'a'
Or you can use just
my $first_val = $AoAoA[0][0][0]; # 'a'

This doesn't work since inside qw you can't use the square brackets. so the elements in your qw are "[a", "b]", ... .
What you probably want is:
my #AoAoA = (
[ [qw/a b/], [qw/c d/], [qw/e f/] ],
[ [qw/r t/], [qw/m n/], [qw/k l/] ],
[ [qw/z x/], [qw/b a/], [qw/p u/] ]
);
Trying to make it shorter:
my #AoAoA = map {[map {[split ' ']} split /,/]} (
"a b,c d,e f",
"r t,m n,k l",
"z x,b a,p u"
);

Related

How do I build a list of lists

So I want to build a list x that looks like this: [[a1 b1][a2 b2][a3 b3]...]. A and b are chosen with one-of listA and one-of listB respectively. I can't find how to build a list easily, I can't just add an item to an empty list?
Check out the dictionary entry for lput for the general syntax for adding to a list, and potentially the programming guide entry and this answer for some more info. For this specific question, have a look at the example below:
to build-lists
ca
let a [ 1 2 3 4 5 ]
let b [ "a" "b" "c" "d" "e" ]
; Unordered version:
let ab []
repeat length a [
set ab lput ( list one-of a one-of b ) ab
]
print "Randomly sampled list:"
print ab
; Ordered version:
set ab ( map [ [ i j ] -> list i j ] a b )
print "Ordered list: "
print ab
reset-ticks
end
Which gives an output something like:
Randomly sampled list:
[[2 a] [2 c] [1 d] [4 d] [1 e]]
Ordered list:
[[1 a] [2 b] [3 c] [4 d] [5 e]]
Slight variation on Luke C's answer, using, respectively, n-values and the concise syntax for anonymous procedures:
let a [ 1 2 3 4 5 ]
let b [ "a" "b" "c" "d" "e" ]
; Unordered version:
print n-values length a [ list one-of a one-of b ]
; Ordered version:
print (map list a b)

NetLogo nested list items to character seperated string

I have a triple nested list:
[
[
[a b]
[c d]
...
]
[
[e f]
[g h]
...
]
]
I want a string with the format a,c,... respectively e,g,..
My current approach is to first make a new list with as much items as the first nested list has and add the first items of the lists within said list.
Afterwards then new list is reduced:
let nl ( n-values ( length ( item 0 list) ) [ i -> ( item 0 ( item i ( item 0 list)) ) ] )
reduce [ [a b] -> (word a "," b) ] nl
Is there a better way to do this, as in this approach it is very difficult to maintain the overview of the "denesting" of the list.
Assuming your list looks like:
let a ( list ( list [ "a" "b" ] [ "c" "d" ] ) ( list [ "e" "f" ] [ "g" "h" ] ) )
I'm not sure that this makes it easier to keep track of the denesting, but you could nest a map within another to get the order you want:
print map [ x -> map [ y -> item 0 y ] x ] a
If you want it as a list of strings with commas, I like the csv:to-row primitive from the csv extension:
print map [ x -> csv:to-row map [ y -> item 0 y ] x ] a
Again, not sure that it's more straightforward but it's an alternative approach!
Edit:
As Seth pointed out, map [ y -> item 0 y ] can be replaced with map first- so the modified versions would look like:
print map [ x -> map first x ] a
and
print map [ x -> csv:to-row map first x ] a
Thanks Seth!

function which return s-tuples with non-negative integers and a given sum $n$

I am trying to write a function which return s-tuples with non-negative integers and a given sum $n$ (the sum of each tuple is $n$). In the program, I need to use s nested loops:
for i1 from 0 to n do
for i2 from 1 to n do
...
for is from 1 to n do
end for;
end for;
end for;
How could I use only a few loops instead of s loops? Thank you very much.
I suggest the combinat:-composition() command. On its own, the command won't include zero terms, but you can instead partition n+s and remove 1 from each term at the end:
restart;
partitions := proc( n :: posint, s :: posint, { allowzero :: truefalse := false } )
local P, u:
if allowzero = false then
P := convert( combinat:-composition( n, s ), 'list' ):
return select( u -> numelems(u) = s, P ):
else
P := procname( n + s, s, ':-allowzero'=false ):
return map( u -> u -~ 1, P ):
end if:
end proc:
partitions( 5, 2, ':-allowzero'=false ); # [ [1,4], [2,3], [3,2], [4,1] ]
partitions( 5, 2, ':-allowzero'=true ); # [ [0,5], [1,4], [2,3], [3,2], [4,1], [5,0] ]

How do I pass a variable number of arrays in a hash to the List::Compare perl module?

I know this question has been asked before here (compare multiple hashes for common keys merge values). As far as I can tell, it went unanswered. If you answer, please include an example that uses the List::Compare->new() constructor.
List::Compare has the ability to accept multiple arrays as input. However, there are no examples that explain how to do so if you do not know in advance how many there will be passed to the constructor.
Example from the man page:
$lcm = List::Compare->new(\#Al, \#Bob, \#Carmen, \#Don, \#Ed);
or...
You may use the 'single hashref' constructor format to build a
List::Compare object to process three or more lists at once:
$lcm = List::Compare->new( { lists => [\#Al, \#Bob, \#Carmen, \#Don, #Ed], } );
or
$lcm = List::Compare->new( {
lists => [\#Al, \#Bob, \#Carmen, \#Don, \#Ed],
unsorted => 1, } );
I need to use this 'single hashref' constructor above, because I don't know how many lists (arrays) will be passed to the constructor. The closest I come is this:
my %l;
my #a = ("fred", "barney", "pebbles", "bambam", "dino");
my #b = ("george", "jane", "elroy", "judy");
my #c = ("homer", "bart", "marge", "maggie");
my #d = ("fred", "barney", "pebbles", "bambam", "dino");
my #e = ("fred", "george", "jane", "elroy", "judy", "pebbles");
$l{'lists'}{'a'} = [ #a ];
$l{'lists'}{'b'} = [ #b ];
$l{'lists'}{'c'} = [ #c ];
$l{'lists'}{'d'} = [ #d ];
$l{'lists'}{'e'} = [ #e ];
my $lc = List::Compare->new(\%l);
my #intersection = $lc->get_intersection;
print #intersection . "\n";
I am getting:
Need to define 'lists' key properly: at /usr/local/share/perl5/List/Compare.pm line 21.
The Compare.pm code (line 21) is:
die "Need to define 'lists' key properly: $!"
unless ( ${$argref}{'lists'}
and (ref(${$argref}{'lists'}) eq 'ARRAY') );
Can someone tell me how to construct and name this hash from simple arrays? I need to be able to process a wide and varied number of them continuously. There could possibly be hundreds of arrays involved.
Update
#Borodin's answer was exactly what I needed. I apologize for the bad data, was trying to come up with something concise. Here is what derived from that code
my #sets_to_process = qw( DOW SP500 has_trend_lines_day );
my #sets;
my $num_sets = $#sets_to_process;
for my $i (0 .. $num_sets) {
my #set = get_ids_in_list( $dbh, $sets_to_process[$i] );
push #sets, \#set;
}
my $lc = List::Compare->new(#sets);
my #intersection = $lc->get_intersection;
print "Sets:\n";
printf " %s\n", join ', ', #$_ for #sets;
print "\n";
print "Intersection:\n";
printf " %s\n", #intersection? join(', ', #intersection) : 'None';
print "\n";
The problem with your parameter construction is that you are defining %l (a dreadful identifier, by the way) as a hash containing a hash of arrays, like this
(
lists => {
a => ["fred", "barney", "pebbles", "bambam", "dino"],
b => ["george", "jane", "elroy", "judy"],
c => ["homer", "bart", "marge", "maggie"],
d => ["fred", "barney", "pebbles", "bambam", "dino"],
e => ["fred", "george", "jane", "elroy", "judy", "pebbles"],
},
)
but the documentation is clear that it should be a simple hash containing an array of arrays
(
lists => [
["fred", "barney", "pebbles", "bambam", "dino"],
["george", "jane", "elroy", "judy"],
["homer", "bart", "marge", "maggie"],
["fred", "barney", "pebbles", "bambam", "dino"],
["fred", "george", "jane", "elroy", "judy", "pebbles"],
],
)
Furthermore, it doesn't help your problem that “[You] don't know how many lists (arrays) will be passed to the constructor” because all that you are doing is pushing the problem inside a data structure instead of keeping it at the parameter level
It is difficult to help you with the data you have given because the intersection is the empty set, so here's a sample program you can experiment with that generates between five and ten sets of sixteen random alphabet letters. It creates different data to work with each time it is run, and passes the list of array references directly as parameters to the new constructor instead of using a reference to a hash with a single lists element
use strict;
use warnings;
use List::Util 'shuffle';
use List::Compare;
my #sets;
my $num_sets = 5 + rand(6);
for (1 .. $num_sets) {
my #set = (shuffle 'A' .. 'Z')[0..16];
push #sets, [ sort #set ];
}
my $lc = List::Compare->new(#sets);
my #overlap = $lc->get_intersection;
print "Sets:\n";
printf " %s\n", join ', ', #$_ for #sets;
print "\n";
print "Intersection:\n";
printf " %s\n", #overlap ? join(', ', #overlap) : 'None';
print "\n";
sample outputs
Sets:
B, C, D, E, F, G, K, L, M, O, P, Q, S, U, V, W, X
B, C, D, F, G, I, J, L, M, P, R, T, U, V, W, X, Y
A, B, C, D, F, G, H, K, L, M, O, R, T, U, V, W, Y
A, B, D, G, H, I, K, L, M, O, R, T, U, V, W, Y, Z
A, B, C, D, E, F, H, J, K, L, M, P, Q, S, U, V, Z
Intersection:
B, D, L, M, U, V
Sets:
A, B, C, D, F, J, K, L, M, N, Q, R, U, V, W, X, Y
A, E, F, G, H, I, J, L, O, P, Q, R, S, T, V, X, Z
B, E, G, H, J, K, L, M, N, P, S, T, U, V, W, Y, Z
B, C, D, E, F, G, H, I, J, N, O, Q, R, T, V, W, Z
A, B, C, E, F, G, H, I, L, N, O, Q, T, U, W, X, Y
Intersection:
None
Update
With regard to the updated code in your question, your identifier $num_sets is wrongly-named as it is the index of the final element in #sets, or one less than the actual number of sets
If you want to use a variable then you should say
my $num_sets = #sets_to_process;
and then loop like this
for my $i ( 0 .. $num_sets-1 ) { ... }
But in this case you don't need the indices at all, and it's probably best to forget about $num_sets and write just this
for my $set ( #sets_to_process ) {
my #set = get_ids_in_list($dbh, $set);
push #sets, \#set;
}
or even just use map like this
my #sets = map [ get_ids_in_list($dbh, $_) ], #sets_to_process;

Perl dynamic hash traversal

A hash of (0 or more levels of hash refs of) array refs of hash refs. Note that the level above the leaf nodes will always be array refs, even if they only have one element.
I need to fetch the aggregate sum of VALUE (in an array of array ref) by preserving the order of the hash refs (In the order of insertion).
Examples :
1)
(
A => {
A1 => [
{ VALUE => 10 },
{ VALUE => 20 }
],
B1 => [
{ VALUE => 30 }
],
},
B => {
A1 => [
{ VALUE => 10 }
],
B1 => [
{ VALUE => 5 }
],
},
C => {
A1 => [
{ VALUE => 100 }
],
},
)
The required output of the above structure will be -
(
[A, A1, 30],
[A, B1, 30],
[B, A1, 10],
[B, B1, 5],
.
.
.
.
)
2)
(
A => [
{ VALUE => 10 },
{ VALUE => 20 }
],
B => [
{ VALUE => 30 }
],
)
The required output of the above structure will be -
(
[A, 30],
[B, 30]
)
You need to write a function that will walk your hash structure and compute the necessary sums. For each key in the hash, it needs to make this decision:
If the value of this key is a list ref, then sum up the VALUE elements in the hashes in this list and return [key, sum]
If the value of this hash is a hash ref, then recurse into that hash. If we got a list back from it, append it to our current output and continue.
At the top level (depth 0), print out each list that's returned.
There are a number of details that still need to be resolved, but that ought to get you started on the right track.