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

I was so excited, I found out about exists so I was going to apply it here. Now I can't figure out how to use it, or even if it will work with this.

#!/usr/bin/perl -w use strict; use warnings; use diagnostics; use CGI; use CGI::Carp qw(fatalsToBrowser); my %Data; #This sub will eventually go into a module. sub random { my ($name) = @_; my $array = $Data{$name}; my $pick = $array->[rand @$array]; if (ref($pick) eq 'CODE') {return $pick->();} else {return $pick;} } $Data{gen} = [ 'beholder' => {b => 1,pg => 21}, 'death kiss' => {pg => 21}, 'eye of the deep' => {pg => 21}, 'gauth' => {pg => 21}, 'spectator' => {pg => 21}, 'undead' => {pg => 21}, 'hive mother' => {pg => 25}, 'director' => {pg => 25}, 'examiner' => {pg => 25}, 'lensman' => {pg => 25}, 'overseer' => {pg => 25}, 'watcher' => {pg => 25}, ]; print random("gen") exists ($gen{$b}) ? '' : ' beholder') exists ($gen{$bk})||exists ($gen{$pg}) ? ( ' ('. exists ($gen{$bk}) ? ($gen{$bk}) : 'Monstrous Manual') exists ($gen{$pg}) ? ', page '.($gen{$pg})) .')' ) ) keys %gen(@_)

First, the item from the hash needs to be randomly selected.

Then if the variable b is in the key's hash, then print nothing. If the variable b is not in the hash, print ' beholder'.

Then if either bk or pg is in the key's hash, then print ' (' and check the following.

If bk is in the hash, print it's value. If bk is not in the hash, print 'Monstrous Manual'.

If pg is in the hash, print ', page ' and it's value. If pg is not in the hash, print nothing.

After those two are done, print a final ')'.

If neither bk nor pg are in the key's hash, then print nothing after the key.

Replies are listed 'Best First'.
Re^3: Creating a random generator
by graff (Chancellor) on Oct 15, 2007 at 08:07 UTC
    I was so excited, I found out about exists so I was going to apply it here. Now I can't figure out how to use it, or even if it will work with this.

    Your problem is not with "exists()" per-se. At the surface, your problem involves punctuation that is incomplete or incorrect, along with insufficient use of parens, when trying to glom together all the steps of your algorithm into a single concatenated string for a "print". Slow down.

    Also, you are getting mixed up about how your data structure is organized. You initialize %Data{gen} with square brackets, which creates a reference to an anonymous array, and the "random" function is set up to get an array ref from this hash element -- that part of the code says "%Data is a HoA structure".

    But the elements of data being assigned to %Data{gen} are laid out as a hash, with the value of each element being another hash, which says "%Data is a HoHoH structure" (e.g.  $Data{gen}{beholder}{pg} == 21). If you want HoHoH, the initialization would use curlies instead of square brackets:

    $Data{gen} = { 'beholder' => {b => 1, pg => 21}, 'death kiss' => {pg => 21}, ... };
    And you would need a different kind of "random()" function in order to select one of those sub-keys ("beholder, "death kiss", etc) at random -- maybe something like:
    sub random { my ( $key ) = @_; my @choices = keys %{$Data{$key}}; my $chosen = $choices[rand @choices]; return $Data{$key}{$chosen}; # or maybe just return $chosen ? }
    So the first thing to do is decide what sort of data structure you really want, and stick with that.

    But let's look at the overall task in terms of the way you describe your algorithm:

    First, the item from the hash needs to be randomly selected.
    my $output = my $randomkey = random( "gen" );
    Then if the variable b is in the key's hash, then print nothing. If the variable b is not in the hash, print ' beholder'.
    $output .= " beholder" unless ( exists( $Data{gen}{$randomkey}{b} ));
    Then if either bk or pg is in the key's hash, then print ' (' and check the following.

    If bk is in the hash, print it's value. If bk is not in the hash, print 'Monstrous Manual'.

    If pg is in the hash, print ', page ' and it's value. If pg is not in the hash, print nothing.

    After those two are done, print a final ')'.

    If neither bk nor pg are in the key's hash, then print nothing after the key.

    That's a bit of tough going, and your sample data doesn't seem to exercise all the contingencies. Still, let's see if I can rephrase it without damaging the intent:

    If either "bk" or "pg" exists in the key's hash, add a parenthesized string containing a book name, with or without a page number; for a "pg" with no "bk", the default book name is "Monstrous Manual":

    my $addon = ''; if ( exists( $Data{gen}{$randomkey}{bk} ) or exists( $Data{gen}{$randomkey}{pg} )) { $addon = $Data{gen}{$randomkey}{bk} || 'Monstrous Manual'; $addon .= ", page $Data{gen}{$randomkey}{pg}" if ( exists( $Data{gen}{$randomkey}{pg} )); } $output .= " ($addon)" if ( $addon );
    The rules for use of brackets (square and/or curly) when working with data structures require careful attention to detail, and a clear understanding of what your particular data structure is. Data::Dumper can help with that, but you need to need to start with a structure that makes sense to you, and organizes the data nicely for the things you need to do with it. When in doubt, go back to perlreftut, perlref and perldsc (Data Structures Cookbook) -- I've done that quite often, and it helps.

    (This thread is getting rather long in the tooth (and just long)... if you continue hitting walls with this pursuit, you might consider focusing on some relatively self-contained issue that is causing trouble, isolate it as a stand-alone problem, and post it as a new thread.)