in reply to Re^2: Creating a random generator
in thread Creating a random generator

graff;

Every time I look at this I am loving it more, but just for the static data. Since the dynamic data might only be used here, let's not transfer them to the text file right now. That is one headache we don't need.

The first section of the code you gave me seems a bit straight forward. I altered it to fit the current naming scheme for the HoA.

my %Data; open( IN, "<", "data.txt" ) or die "data.txt: $!"; while (<IN>) { chomp; my ( $key, @values ) = split( /\s*[:|]\s*/ ); $Data{$key} = [ @values ]; } close IN;

Since we are not moving any dynamic content over, is the second bit of code necessary?

Now, the third section is the one that could get me into real trouble. I removed a lot of the spacing, but still indented a bit here and there, I hope you don't mind. I changed all that I could find to the current setup as well.

for my $key ( keys %Data ) { my @replace_vals = (); for my $val ( @{$Data{$key}} ) { if ( $val =~ /[@&]_\S+/ ) { # need to split and process my @words = split ' ', $val; $val = ''; for my $wrd ( @wrds ) { if ( $wrd =~ /\@_(\S+)/ ) { my $refkey = $1; if ( exists( $Data{$refkey} )) { my $n = @{$Data{$refkey}}; $wrd = $Data{$refkey}[rand $n]; } else { warn "No $refkey in Data for $key\n"; } } elsif ( $wrd =~ /\&_(\S+)/ ) { my ( $refkey, @args ) = split /[:,]/, $1; if ( exists( $init_func{$refkey} )) { $wrd = $init_func{$refkey}->(@args); } else { warn "No $refkey function for $key\n"; } } $val .= "$wrd "; } } push @replace_vals, $val; } $Data{$key} = [ @replace_vals ]; }

Now, what will get me into the most trouble is all of the regular expressions used. At least that is what I think all of those strings of punctuation are. I rarely use them in the find/replace in my text editor. When I do use them, it is with extreme caution. I will read up on them, but the docs are hard going for me. If you could explain a few of them in the code above, that would help me get a much better grasp on them.

Leaving the dynamic portions of the script in it, there shouldn't be any problems with dependency as far as I can tell.

Lady Aleena

"An it harm none, do as ye will."

Replies are listed 'Best First'.
Re^4: Creating a random generator
by graff (Chancellor) on Oct 10, 2007 at 05:54 UTC
    Since we are not moving any dynamic content over, is the second bit of code necessary?

    If you just want to stick with nothing but static data in the data file, the first little loop that reads from the data file is all you need. The rest of it is for applying dynamics to the file content, and would only be needed if you want the file to include specs for the dynamic settings.

    The second loop loads a separate hash of code refs, keyed by the names used in the data file in order to invoke one or another function to fill a given data field (i.e. "roll()", "color()", etc). The third loop goes back over the data loaded from the file, and does replacements of "special" field values with suitable strings, based either on a random selection from a given array, or on the output of a given function (from the hash of code refs).

    Leaving the dynamic portions of the script in it, there shouldn't be any problems with dependency as far as I can tell.

    If you mean "leaving all the dynamic assignments to be done individually, in proper sequence, in the script", then you're right, there are no dependencies involving the data file -- just make sure all the "primary" (static) arrays are set, and make sure the script puts the dynamic assignments in proper order.

    But it is still possible for the data file to do more work, and for the perl script to be shorter and simpler, making the system easier to cope with. I've tried studying the other replies (esp. this one by shmem, which taught me a lot), in order to understand how the finished system ought to behave, and here's an approach that simply loads the data, with its various directives for dynamic (randomized) results at run time, and leaves all the random selections to be done at the end, via a simple recursive function that operates on those directives:

    That has just a small sample of data to demonstrate how it works -- you can add more data as you see fit. If you actually want to have two data files -- one for static strings and one for "cross-referenced dynamic" strings, that would be fine -- just read both files the same way, into the same %Data hash.

    Note that the file format is pretty flexible as to white-space formatting: the essential feature is that all arrays start with "Name:", have pipes "|" between elements, and are separated from other arrays by at least one blank line (no blank line within an array), because the "paragraph mode" input will read multi-line records up to and including 2 or more line-feeds. Comment "paragraphs" are allowed (ignored).

    You'll probably find that it's pretty easy to make mistakes in the data file (misspellings, inconsistent capitalization, missing names, wrong sigils for function vs. array, missing pipe symbols between array elements), but a data file tends to be easier to fix than code, and it wouldn't be very hard to come up with a reasonable "validator" script, to check for common mistakes in the data, and/or add some sensible error checking to the above script.

    (UPDATE: One mistake you must be very careful to avoid is a circular reference among two or more arrays -- i.e. an element in List1 uses a reference to @_List2, which in turn points back to that same element in List1. Chains like List1 - List2 - List3 - ... - List1 are nastier in the sense of being harder to track down, but the script will blow up in either type of case.)

    Regarding the "%field_func" hash, note that the "radius" function doesn't really need to be there -- it can be defined in the data file as well -- but you can/should put a code ref in there for your "insanity" function (make sure the function returns the string you want, rather than just printing the string to stdout); then include suitable references to it in the data file (" &_insanity ").

    The core of the approach is the DataString function, which has two important features: (1) it uses a while loop over a given string to make sure all "sigil" replacements are done in turn, and (2) it uses recursion (calls itself) each time it has to do an embedded Random selection. This makes it possible to lay out the data file without worrying about the relative ordering of dependent arrays; so long as all the data are loaded into %Data before going to work, everything should work.