Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

Re^2: General pattern for interactive text-mode script?

by Anonymous Monk
on Jul 03, 2008 at 18:09 UTC ( #695416=note: print w/replies, xml ) Need Help??

in reply to Re: General pattern for interactive text-mode script?
in thread General pattern for interactive text-mode script?

Could you please give an example of such a data structure? Is it anything like the following? :

#!/usr/bin/env perl use strict; use warnings; #... sub task_1_a_1 { print "Performing task 1 a 1 ...\n"; } #... my %stuff = ( 'Major task to perform?' => ['task_1','task_2','task_2'], 'task_1' => {'Sub-task for task_1?' => ['task_1_a','task_1_b','tas +k_1_c']}, 'task_2' => {'Sub-task for task_2?' => ['task_2_a','task_2_b','tas +k_2_c']}, 'task_3' => {'Sub-task for task_3?' => ['task_3_a','task_3_b','tas +k_3_c']}, 'task_1_a' => {'Sub-sub-task for task_1_a?' => ['task_1_a_1','task +_1_a_2']}, #... 'task_1_a_1' => \&task_1_a_1, ); $stuff{task_1_a_1}->();

Incidentally, if you happen to know of a simple example of a FSM (preferably implemented in Perl), I'd love to see it.

Replies are listed 'Best First'.
Re^3: General pattern for interactive text-mode script?
by apl (Monsignor) on Jul 03, 2008 at 23:19 UTC
    You have the sense of it, though I'd change:
    'Major task to perform?' => ['task_1','task_2','task_2'],
    to something like
    'Major task to perform?' => { answers => [ 'Add', 'Delete', 'Rename' ], keys => [ 'task_1','task_2', 'task_2' ], },
    This way if the user enters 'Add'. the next hash key you'd use is 'task_1'. (Make certain to programmatically test that there are as many keys as there are answers.)

    This handles compressing all of the prompts you were talking about. The next problem is: what happens when you get to the end of the user input? You always need to keep track of the last key you used, and you need some way to indicate that you're at a terminal node. (Perhaps a blank key could indicate this.)

    Your example would then become:

    my $current_key = 'Major task to perform?'; while ( my $choice = prompt( $current_key, ...)) { # set INDEX to offset of $choice in $structure{ $current_key }{ans +wers} # if $structure{$current_key}{keys}[INDEX] is empty # break out of loop; you;'re in a terminal state # else $current_key = $structure{$current_key}{keys}[INDEX] } <p>A lot of the code is left as an exercise to the reader. Good luck w +ith your class.

      Minor nit, but I prefer not to use indices whenever possible. In C, they're basically required. That's fine. But you shouldn't need them in C++ or Perl where we have proper iterators.

      So, instead of what you have, I'd combine them into a single list:

      'Major task to perform' => [ { input => 'Add', next => 'task_1' }, { input => 'Delete', next => 'task_2' }, { input => 'Rename', next => 'task_3' }, ],
      This should perform about as well as your method, but without the need for an index. As you loop through the hashrefs, once you find the input given, you already have the hashref with the details about whatever else you want. Further, you can very easily add additional fields if you find the need to. And if there is a long list of possible inputs, lining up the input with the associated task or next key or whatever will be trivial - not so with multiple array refs where you will have to manually ensure everything lines up even when you have 30 or 40 possible next states.

      Or, if I was going to be case sensitive anyway (or I was willing to lc everything), set it up in a hash based on the inputs and be even faster:

      'Major task to perform' => { 'add' => { next => 'task_1', help => 'Add something to the intern +al list' }, 'delete' => { next => 'task_2', help => 'Rename something (didn't ex +pect that, did you)' }, 'rename' => { next => 'task_3', help => 'Move something to a new loc +ation (didn't expect that, either, huh?)' }, },
      When you get an input word, just lc it, and look it up for what the next state is. As you can see, I've gone and added extra info, which is trivial to do in hashes: if you get the 'help' word, you could loop through the hash (possibly by sorting on the key first, or maybe sorting based on a key in the value hash, e.g., "sort_order => $n"), printing the key and the help (Text::Table may help here). All the information is co-located. (In a larger system, it's arguable that the help should go with 'next' rather than 'this', but that's merely implementation details - I'm talking about a design that is more flexible and easier to maintain.)

        Excellent suggestions. (I am, self-admittedly, a dinosaur. While I love Perl and like C++, I generally think in Fortran...)

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://695416]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (5)
As of 2018-04-23 06:48 GMT
Find Nodes?
    Voting Booth?