Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

General question about a haiku program

by telemachus (Friar)
on May 06, 2008 at 00:05 UTC ( #684785=perlquestion: print w/replies, xml ) Need Help??
telemachus has asked for the wisdom of the Perl Monks concerning the following question:

Hi, Monks.

I've written a small program to print out haikus from a shell, and although the program works I have this vague sense that it could be better. So my question (I think) is more along the lines of "How could this be better? (more efficient? more Perlish?)" than "How do I do this?"

Just to clarify the idea: the program is like fortune, but much simpler. The user types haiku at a prompt and is rewarded with a (pseudo-)randomly selected three-line poem and its author. Here's my version:

#!/usr/bin/perl use strict; use warnings; my $number = int(rand(5)) + 1; my %poems = ( 1 => "Even in Kyoto:hearing the cuckoo's cry--:I long for Kyoto.:Basho", 2 => "In this world:We walk on the roof of hell:Gazing at flowers.:Issa", 3 => "The old pond--:a frog jumps in,:sound of water.:Basho", 4 => "In the white plum blossoms:night to next day:just turning.:Buson", 5 => "Sick on a journey,:my dreams wander:the withered fields.:Basho", ); my ($line1, $line2, $line3, $author) = split ":", $poems{$number}; print " $line1\n$line2\n $line3\n\t$author\n";

The real version has just over 80 haikus, but I figure we all get the point. (Though I'm happy to post the whole thing on my scratchpad, if anyone wants to play with the poems and avoid typing them.)

So I'm curious: is there some much better way to do it?

Replies are listed 'Best First'.
Re: General question about a haiku program
by almut (Canon) on May 06, 2008 at 00:12 UTC

    If your keys/indices are just consecutive numbers, an array (instead of the hash) might be a slightly more "natural" implementation... (not much of an issue, though).

    ... my $number = int(rand(5)); my @poems = ( "Even in Kyoto:hearing the cuckoo's cry--:I long for Kyoto.:Basho", "In this world:We walk on the roof of hell:Gazing at flowers.:Issa", "The old pond--:a frog jumps in,:sound of water.:Basho", "In the white plum blossoms:night to next day:just turning.:Buson", "Sick on a journey,:my dreams wander:the withered fields.:Basho", ); my ($line1, $line2, $line3, $author) = split ":", $poems[$number]; ...
      A funny example of how stuck you can get seeing something from a particular angle. The hash is a relic of an earlier, more complex attempt, but once I settled on a hash, that part never changed back to an array. Thanks.
Re: General question about a haiku program
by mscharrer (Hermit) on May 06, 2008 at 01:06 UTC
    I would in any case use an array. An array of arrays would be remove the need of split.

    Do you need the three lines as single variables? Otherwise just put them as one string and mark the line endings with "\n".

    my $number = int(rand(5)) + 1; should be better my $number = int(rand(@poems)) + 1; and placed after my @poems = ( ... ).

    Also putting the peoms in an extra file or in the __DATA__ section in an human readable format and then parsing them would make the code more maintainable and extentable.
    You could use a format like this:

    Line 1 Line 2 Line 3 Author Line 1 Line 2 Line 3 Author ...
    then say $/ = '' to enable paragraph, not line, reading mode: (untested)
    local $/ = ''; my @peoms = <DATA>; my $number = int(rand(@poems)) + 1; print $peoms[$number]; # or my ($line1, $line2, $line3, $author) = split "\n\s*", $poems[$number]; print " $line1\n$line2\n $line3\n\t$author\n"; __DATA__ Line 1 Line 2 Line 3 Author ...
      According to TAOUP, the record seperator is %%. We should stick to conventions for reasons outlined in the same book.
Re: General question about a haiku program
by roboticus (Chancellor) on May 06, 2008 at 01:56 UTC
    telemachus:

    Since "laziness is a virtue", I let Tie::File do the file I/O:

    #!/usr/bin/perl use strict; use warnings; use Tie::File; tie my @haiku, 'Tie::File', $0 or die "FOO!"; my $number = 4 * int(rand(@haiku/4 - 2) + 2); print join "\n", @haiku[$number..$number+3]; __END__ Even in Kyoto hearing the cuckoo's cry-- I long for Kyoto. Basho In this world We walk on the roof of hell Gazing at flowers. Issa The old pond-- a frog jumps in, sound of water. Basho In the white plum blossoms night to next day just turning. Buson Sick on a journey, my dreams wander the withered fields. Basho
    ...roboticus
Re: General question about a haiku program
by Your Mother (Chancellor) on May 06, 2008 at 01:28 UTC

    Bit like mscharrer's, this approach can handle a very large set of records pretty efficiently.

    $/ = "\n\n"; rand($.) < 1 && ($haiku = $_) while <DATA>; $haiku =~ s/\s+\z//; # instead of chomp, accounts for uneven final \n print $haiku, "\n"; __DATA__ Even in Kyoto hearing the cuckoo's cry-- I long for Kyoto. Basho In the white plum blossoms night to next day just turning. Buson

    Other advantage is it's basically WYSIWYG.

Re: General question about a haiku program
by tilly (Archbishop) on May 06, 2008 at 01:56 UTC
    Here is a cheating solution for generating haiku using Coy from TheDamian:
    use Coy; die;
    You could clean up the output with a hack like this:
    use Coy; eval{die}; $@ =~ /-----\s*(.*)\s*(.*)\s*(.*)\s*-----\s*(.*)\.\.\./; print "$1\n$2\n$3\n - $4\n";
    (Yeah, not your original question. Still Coy is a cool module to play with.)
Re: General question about a haiku program
by apl (Monsignor) on May 06, 2008 at 01:10 UTC
    Instead of separating the lines of the poem (and the author) by colons, separate them with \n\t.

    This way, you can replace

    my ($line1, $line2, $line3, $author) = split ":", $poems{$number}; print " $line1\n$line2\n $line3\n\t$author\n";
    with
    print " ",$poems{$number},"\n";
Re: General question about a haiku program
by telemachus (Friar) on May 06, 2008 at 14:40 UTC
    Thanks all for the suggestions; thanks also to the Monks in Chatterbox who explained what the @$#! this line says:

    rand($.) < 1 && ($haiku = $_) while <DATA>;

    I was glad to learn that trick and also to think more about Tie::File.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://684785]
Approved by almut
help
Chatterbox?
[choroba]: Saturday, right?

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (9)
As of 2017-10-21 11:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My fridge is mostly full of:

















    Results (269 votes). Check out past polls.

    Notices?