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

This could very obviously be done with loops, but I'd like to know if there's a way in Perl to avoid that. My first attempt was this:

@interestingThings = ($root[2][0..3][3]); # Should print an array of interesting scalar values. print Dumper(\@interestingThings);

For those who understand a bit of CSS, I basically want to do something like #aUniqueElement p .anotherElement:;, where I describe sets of elements with a selector the describes their place in the DOM.

But if you're not familiar with CSS, here's a more detailed description of my problem:

Suppose I have a complex tree structure, comprised of arrays and hashes and references to tie them together. The structure has this property:

For any subtree rooted at N, any subtree rooted at X, such that X is a sibling of N, has the same shape as N.

This property means that we could talk about sets of nodes or subtrees by describing patterns of paths to them. For example, we could describe the set of nodes that are the third child of any of the children of the second child of the root. Or, succinctly: Tree->2nd Child->Nth Child->3rd Child, where N is in the range of Tree->2nd Child.

I want to collect such a set, though I'm actually interested in the values of nodes I'm describing.

Thanks very much!

  • Comment on How I can collect values out of complex tree structure that are located at chained keys matching a pattern? Without using a loop, that is.
  • Download Code

Replies are listed 'Best First'.
Re: How I can collect values out of complex tree structure that are located at chained keys matching a pattern? Without using a loop, that is.
by 1nickt (Canon) on Nov 07, 2015 at 05:01 UTC
    When you want to create an array from certain elements of another array, think map:
    @InterestingThings = map { $root[2][ $_ ][3] } ( 0 .. 3 );
    Hope this helps!
    The way forward always starts with a minimal test.
      Of course map is for all intents and purposes just a loop, only it is written somewhat more compact than your more obvious for loop.

      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

      My blog: Imperial Deltronics

      That's done it, thank you! You're absolutely right, I don't know how map didn't occur to me! I guess I've only ever thought of using it for whatever data my program is working on, not for data about the program itself.

Re: How I can collect values out of complex tree structure that are located at chained keys matching a pattern? Without using a loop, that is.
by stevieb (Canon) on Nov 07, 2015 at 03:58 UTC

    What does $root look like? Show us some input data, and how you're getting to that division of the variable.

      1nickt solved my problem here, but I'll supply this anyway for the benefit of future readers.

      $root is actually @child_tokens, an array of hashes describing the tokens which have been produced from the present token and may still need expanding. This is, as you may have guessed, part of a recursive-descent parser I'm writing by hand (it's my first, I'm giddy).

      Each hash in @child_tokens looks like this before it's been expanded:

      { 'kind' => 'token', 'type' => 'command', 'children' => undef, 'location' => undef, 'length' => undef, 'state' => 'unexpanded', }

      What I'm working on is filling in 'location' and 'length'. The idea is that a token learns its location just before it is expanded, and learns its length after all its children have been expanded. The token's location is the sum of its parent's location with the lengths of each sibling token that comes before it (each of which, having been expanded, knows its length).

      This question was about getting the sum of those siblings' lengths. Each length, from my current scope, would be accessed with ${$child_tokens[$index]}{'length'}. The trick was to get $index to range from 0 to the index of the previous token. And map has indeed done just that!