Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

Recursive Class:Struct syntax error?

by ibm1620 (Monk)
on Dec 03, 2018 at 15:13 UTC ( #1226664=perlquestion: print w/replies, xml ) Need Help??
ibm1620 has asked for the wisdom of the Perl Monks concerning the following question:

Hi --

I could use a little help learning Class:Struct. I want to traverse a directory, visiting only certain subdirectories to find files that need to be documented. The directories I want to visit are:

bin js js/WATS php php/web_crawler
(There will be other sub-subdirectories added later)

Referring to perldoc for Class:Struct, I tried to create such a tree at compile time:

use Class::Struct DOCUMENTABLE_PATHS => { bin => { }, js => { WATS => { } }, php => { }, scripts => { web_crawler => { } }, };
The compile-time error I receive points to the use line:
'HASH(0x24a2fd8)' is not a valid struct element type
I'm guessing it doesn't like the way I'm recursing, but I'm stuck.

Maybe this isn't the best way to solve this problem, but I would like to add Class:Struct to my toolbelt. Thanks for any help.


Replies are listed 'Best First'.
Re: Recursive Class:Struct syntax error?
by bliako (Deacon) on Dec 03, 2018 at 16:05 UTC

    The man page states the valid ways to create such a struct. They all involve an ELEMENT_LIST which has the form NAME => TYPE, ...

    Your mistake is that instead of TYPE, you give data. Instead of '*%' you give {}. So, for the non-recursive elements do something like this bin => '*%'

    Now, for the recusrive parts. The obvious would be to use a hash-of-hashes - my above suggestion suffices ... But I get the impression you would like to use Class::Struct so that your toolbelt looks tres chic, so to speak. I can't help with that without my mind constantly drifting to either a simple hash-of-hashes-of-hashes-of-hashes... OR creating a class yourself.

    bw, bliako

      Agreed. I eventually just built a hash of hashes. This was certainly not a good application for Struct.

        markong's suggestion to use Tree-Simple (instead of h-o-h) looks like a very good idea to me because it will easily allow you to traverse your file-system backwards - from the children back to the parent - (and forwards of course). Plus you can do all sorts of sorting and searching on filenames. Lastly, keys %hash returns keys in random order. So each run will not be consistent unless you always do sort keys %hash or use arrays. So, yes hash-of-hashes is a basic idea, but Tree-Simple will make it easier for you and it is a good thing to have in one's toolbelt.

Re: Recursive Class:Struct syntax error?
by markong (Scribe) on Dec 03, 2018 at 15:49 UTC

    I hope to understand what you really want to achieve here: I have no experience with that module but skimming *very* rapidly the docs it seems that you are using a wrong syntax to define the struct members, i.e.: to have an hash member you should write  bin => '%',
    That's my guess at interpreting that error message.

    That said, while you could use Class::Struct as data structure to familiarize with it, remember that file systems are essentially tree based data structures and as soon as those folder will contain nested folders, you are going to have problems and struggle hard to correctly record the details of the files you want to save for post-processing.

    If you really want to mimics a file-system like data structure, I would recommend using something like Tree-Simple or otherwise deeply nested hash (refs) could also serve you well. In any case you should look at one of the plethora of recursive file system search modules to scan your directories.

Re: Recursive Class:Struct syntax error?
by ikegami (Pope) on Dec 03, 2018 at 18:02 UTC

    One wouldn't normally create a class per directory. One would normally create a class for directories, and create an instance of the class for each directory.

    With Class::Struct, you'd get something like the following:

    use Class::Struct File => { name => '$', }; use Class::Struct Directory => { name => '$', children => '@', # An array of File and Directory objects. };

    But a structure is rather limiting. For starters, creating an object requires having created an object for each of its children first, meaning the entire directory subtree would need to be read whether its needed or not. In practice, reading the entries of the directory needs be done in demand, like Path::Class::Dir's children does.

Re: Recursive Class:Struct syntax error?
by 1nickt (Abbot) on Dec 04, 2018 at 01:46 UTC

    Hi, (I waited until you reached your own conclusions about Class::Struct ...) I recommend Path::Iterator::Rule for this kind of work.

    Here I am looking at the shebang, mostly because it's cool, but you can of course use file extension, or file contents, or a custom routine, to find the files, and additionally you can pass each one found to a callback using the visitor option.

    The chained methods allow the syntax to be rather concise.

    use strict; use warnings; use feature 'say'; use PIR; my @dir = qw( bin js js/WATS php php/web_crawler ); say for PIR->new ->max_depth(0) ->file ->shebang(qr/#!.*(?:perl|php|node)\b/) ->all(@dir); __END__

    Hope this helps!

    The way forward always starts with a minimal test.
Re: Recursive Class:Struct syntax error?
by ibm1620 (Monk) on Dec 03, 2018 at 19:18 UTC
    What I was trying to do was to define a constant structure containing hard-coded directory and subdirectory names, that would allow me to write nested literal strings. The code would walk this structure forming paths to directories from which it would readdir() the file names. It sounds like Struct doesn't easily support this kind of initialization. Clearly, this can be done by writing a hash-of-hashes-of-hashes as others have suggested, and I eventually used.

    This was obviously not a perfect situation in which to use Struct. :-)

      It sounds like Struct doesn't easily support this kind of initialization.

      Put simply, in OO parlance, a Struct is a class without methods. Thus you can only define members ("attributes"). To work with structs, as with objects you need to create them, and (maybe contestually) initialize its members.

      Your original code was defining a struct *and* trying to initilize its members, which is not supposed to work: using a class you would use a constructor for that task.

      Clearly, this can be done by writing a hash-of-hashes-of-hashes as others have suggested, and I eventually used.

      Mind that HoH it's not the "perfect" data structure for the job: as bliako has already noted you won't get elements back in any predictable order without counting that now its up to you to walk back and forth the nested maze of elements and this will get surprisingly complex if you need to address the general case: take a look at Data::Walk.

      This was obviously not a perfect situation in which to use Struct. :-)

      Because you were using the struct as a sort of "root hash element" which does not make much sense: if you do want to learn it, why not use a Tree-Simple whose nodes are Structs?

      I bet that, if you are interested in programming, won't be that easy to avoid trees ;).

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1226664]
Approved by marto
Front-paged by Corion
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2018-12-14 03:01 GMT
Find Nodes?
    Voting Booth?
    How many stories does it take before you've heard them all?

    Results (63 votes). Check out past polls.