http://qs1969.pair.com?node_id=60839

Think you know perl? How well do you know your perlfuncs?

"To understand it, one must live it..." But do not underestimate the power of drilling, newly shaven one.

yapat is a program which uses definitions of 206 perlfuncs taken from the perlfunc page of PerlMonks.com to drill the disciple, um, user in the names of the perl functions.

yapat uses a weighting system to determine how often the user will see a definition... if the user misses a definition a few times, they will see it a lot more often. If they get it right enough, they'll never see it again!

New Version: Changed the code to prevent seeing the same function within 20 turns. Fixed some spelling errors/unclear definitions. Added a help function. (28-feb-2001)

Update: Thanks to epoptai and OeufMayo for helping me streamline the user interface on this and general sanity checking.

The "weight" of a function goes up by three for wrong answers, and by five if the user gives up and asks for the answer. Right answers decrease weight by 1. When weight reaches zero, the function is removed from the list of questions.

In a typical session, the user is presented with a series of definitions of Perl functions. He or she responds (hopefully) with the name of the function. If the answer is right, great! But if not, the user gets to try again. If they really can't get it, they can ask for the right answer by hitting "?".

The program saves a "gamefile" for the user so they will be able to pick up where they left off.

NOTE: some users (especially on Linux machines) have had problems with linefeeds in the code causing errors. To fix this, you may have to run this code to fix it after downloading the code:
perl -p -ibak -e 's/\r//g' yapat.pl

MAJOR UPDATE: Removed problems with newlines, will work on any machine now (I hope!)

#!/usr/bin/perl -w =head1 NAME yapat - Yet Another Perl Aptitude Test A perlfunc driller/teacher which uses weighting to customize the lesson =head1 SYNOPSIS yapat [-w weight] [-f gamefile] =head1 ARGUMENTS -w Initial weight. By default, this is 1. -f Name of saved gamefile. By default, yapat.data. =head1 DESCRIPTION You're pretty good at Perl. But how's your perlfunc knowledge? "To understand it, one must live it..." But do not underestimate the power of drilling, newly shaven one. yapat is a program which uses definitions taken from the perlfunc page of PerlMonks.com to drill the disciple, um, user in the names of the perl functions. yapat uses a weighting system to determine how often the user will see a definition... if the user misses a definition a few times, they will see it a lot more often. If they get it right enough, they'll never see it again! The "weight" of a function goes up by three for wrong answers, and by five if the user gives up and asks for the answer. Right answers decrease weight by 1. When weight reaches zero, the function is removed from the list of questions. In a typical session, the user is presented with a series of definitions of Perl functions. He or she responds (hopefully) with the name of the function. If the answer is right, great! But if not, the user gets to try again. If they realy can't get it, they can ask for the right answer by hitting "?". The program saves a "gamefile" for the user so they will be able to pick up where they left off. =head1 COMMANDS In place of typing an answer, the user also may type: score - see current score and functions which are "troubling" them ? - to give up and see the correct function save - save the current gamefile without quitting quit - to quit and save the current gamefile =head1 AUTHOR zeno =head1 BUGS I understand that these perlfuncs are not up to date with 5.6. I want to fix this, but I haven't found a short version of the 5.6 perlfuncs (and I don't feel qualified to paraphrase the long ones!) =head1 UPDATES 28-feb-2001 Added a twenty function queue to prevent seeing the same q +uestions repeatedly. Fixed some spelling errors and unclear definitions in the +perlfuncs. Added a help system 02-mar-2001 Fixed newline problem for linux machines 03-mar-2001 Really fixed newline problem (check out line 151) =cut use strict; use Data::Dumper; use Getopt::Std; my %dict; my %weight; my ($max,$score, $tries, $correct)=(0,0,0,undef); my @funcqueue = (1) x 20; # A queue of twenty last funcs # use Getopt::Std to get command line args (p,516, Perl Cookbook) my %option = (); getopts("w:f:",\%option); my $gamefile = $option{f} || "yapat.data"; my $default_weight = $option{w} || 1; sub by_weight { $weight{$a} <=> $weight{$b}; } sub hmax { # return the maximum value of a hash my ($hash) = @_; my $max = 0; foreach (keys %$hash) { if ($$hash{$_} > $max) { $max = $$hash{$_}; } } return $max; } sub save { open(DUMPFILE, ">$gamefile") or die "can't open $gamefile:$!"; print DUMPFILE Data::Dumper->Dump([\%weight], ['*weight']); print DUMPFILE Data::Dumper->Dump([\@funcqueue], ['*funcqueue']); print DUMPFILE "\$score = $score;\n"; print DUMPFILE "\$tries = $tries;\n"; close DUMPFILE; print "Game saved to $gamefile\n"; } sub help_message { print << "helptext"; yapat gives you the definition of a perlfunc and asks you to type in the function name. You can't mispell it. You can't put it in the wrong case. You have to get it just right! (Kind of like Perl). The more you get one wrong, the more often you see it again. You can also use these commands: ?\tWhen you give up and want to see the right answer help\tTo see this menu score\tTo see your score and a list of commands you're having trouble +with save\tTo save your game file quit\tTo save your game file and quit Good luck. Don't worry, it's not supposed to be easy. But when you get through, you'll know more about Perl! helptext } print "Welcome to Yet Another Perl Aptitude Test (yapat)!\n"; print "Do you want instructions (Y/n)? "; $_ = <STDIN>; if (!m/^n/i) { help_message; } # read dictionary into a hash foreach (<DATA>) { if (m/^[\s\r]+$/) {next} chomp; my ($func,$defn) = m/([^:]+):(.*)/; $dict{$func}=$defn; # this to avoid \r problems in linux machines chop($dict{$func}); $weight{$func}=$default_weight; } # if a previous dumpfile exists, eval it # so the user is haunted by previous results! $/="\n"; if (-e $gamefile) { print "Continue previous game (Y/n)? "; my $ui = <STDIN>; if ($ui !~ m/^n/i) { open (DUMPFILE,'<'.$gamefile) ; undef $/; eval <DUMPFILE>; close DUMPFILE; print "Game loaded\n"; } } $/="\n"; # This loop is where the actual quiz happens while (1) { # Determine which function to ask about, # based on weight. $correct = undef; # pass that fat hash by reference $max = hmax(\%weight); # if the max is zero, the user has answered # correctly for all functions, compensating # for previous errors. if ($max == 0) { print "Astounding. You've answered everything correctly.\n"; # delete the gamefile unlink $gamefile; last; } foreach(reverse sort by_weight keys %weight) { if (rand($max*10) < $weight{$_}) { my $candidate=$_; # make sure we haven't seen this in the last 20 turns if (!map {(m/^$candidate$/)} (@funcqueue)) { shift(@funcqueue); push(@funcqueue,$candidate); # shorten the queue if we are under twenty if ((scalar keys %weight) < (@funcqueue+1)) { shift(@funcqueue); } $correct = $candidate; last; } } } # avoid error if we make it through the list # without choosing a function. if (!defined $correct) {next}; # Show the user the definition of the perl function print "$dict{$correct} (weight: $weight{$correct})\n"; print "(type an answer, '?' to see the answer, 'help', 'score', 'sav +e', or 'quit')\n"; # Get the user's input $_=<STDIN>; chomp; # Wrong answer, or choices which require the same # question again (save, score) while ( !m/^$correct$/ and !m/^quit$/i and !m/\?/) { if (m/^save$/) { save(); } elsif (m/^score$/i) { # User asks for score. if (!$tries) {$tries++}; printf "Score: $score/$tries = %6.2f%%\n",100*($score/$tries); print "There are ".(scalar keys %weight)." functions left on you +r question list.\n"; # tell the user what functions seems to be eluding him or her my $trouble = undef; foreach(reverse sort by_weight keys %weight) { if ($weight{$_} > 1) { $trouble .= ((defined $trouble)?', ':'').$_; } } if ($trouble) { print "You seem to be having trouble with:\n $trouble\n\n"; } else { print "So far, you seem to be doing great!\n\n"; } } elsif (m/^help$/i) { help_message; } else { $tries++; printf "Sorry.\nScore: $score/$tries = %6.2f%%\n",100*($score/$t +ries); $weight{$correct} += 2; } print "$dict{$correct} (weight: $weight{$correct})\n"; print "(type an answer, '?' to see the answer, 'help', 'score', 's +ave', or 'quit')\n"; $_=<STDIN>; chomp; } if (m/$correct/) { # player shoots, player SCORES! $tries++; $score++; $weight{$correct}-- unless ($weight{$correct} < 1); printf "Correct!\nScore: $score/$tries = %6.2f%%\n",100*($score/$t +ries); if ($weight{$correct} < 1) { delete $weight{$correct}; delete $dict{$correct}; print "$correct removed from question list!\n"; } print "\n"; } elsif (m/q/i) { # player quits printf "Final score: $score/$tries = %6.2f%%\n",100*($score/$tries +) if ($tries > 0); save(); last; } elsif (m/\?/) { # player gives up and asks for the right answer $tries++; $weight{$correct} += 5; print "$correct\t-> $dict{$correct} (weight: $weight{$correct})\n" +; printf "Score: $score/$tries = %6.2f%%\n\n",100*($score/$tries); } } __DATA__ -X:run a file test abs:absolute value function accept:accept an incoming socket connect alarm:schedule a SIGALRM atan2:arctangent of Y/X bind:binds an address to a socket binmode:prepare binary files on old systems bless:create an object caller:get context of the current subroutine call chdir:change your current working directory chmod:changes the permissions on a list of files chomp:remove a trailing record separator from a string chop:remove the last character from a string chown:change the owership on a list of files chr:get character this number represents chroot:make directory new root for path lookups close:close file (or pipe or socket) handle closedir:close directory handle connect:connect to a remote socket continue:optional trailing block in a while or foreach cos:cosine function crypt:one-way passwd-style encryption dbmclose:breaks binding on a tied dbm file dbmopen:create binding on a tied dbm file defined:test whether a value, variable, or function is defined delete:deletes a value from a hash die:raise an exception or bail out do:turn a BLOCK into a TERM dump:create an immediate core dump each:retrieve the next key/value pair from a hash endgrent:be done using group file endhostent:be done using hosts file endnetent:be done using networks file endprotoent:be done using protocols file endpwent:be done using passwd file endservent:be done using services file eof:test a filehandle for its end eval:catch exceptions or compile code exec:abandon this program to run another exists:test whether a hash key is present exit:terminate this program exp:raise e to a power fcntl:file control system call fileno:return file descriptor from filehandle flock:lock an entire file with an advisory lock fork:create a new process just like this one format:declare a picture format with use by the write function formline:internal function used for formats getc:get the next character from the filehandle getgrent:get next group record getgrgid:get group record given group user ID getgrnam:get group record given group name gethostbyaddr:get host record given its address gethostbyname:get host record given name gethostent:get next hosts record getlogin:return who logged in at this tty getnetbyaddr:get network record given its address getnetbyname:get networks record given name getnetent:get next networks record getpeername:find the other end of a socket connection getpgrp:get process group getppid:get parent process ID getpriority:get current nice value getprotobyname:get protocol record given name getprotobynumber:get protocol record numeric protocol getprotoent:get next protocols record getpwent:get next passwd record getpwnam:get passwd record given user login name getpwuid:get passwd record given user ID getservbyname:get services record given its name getservbyport:get services record given numeric port getservent:get next services record getsockname:retrieve the sockaddr for a given socket getsockopt:get socket options on a given socket glob:expand filenames using wildcards gmtime:convert UNIX time into record or string using Greenwich time goto:create spaghetti code grep:locate elements in a list which test true against a given criteri +on hex:convert a string to a hexadecimal number import:patch a module's namespace into your own int:get the integer portion of a number ioctl:system-dependent device control system call join:join a list into a string using a separator keys:retrieve list of indices from a hash kill:send a signal to a process or process group last:exit a block prematurely lc:return lower-case version of a string lcfirst:return a string with just the first letter in lower case length:return the number of bytes in a string link:create a hard link in the filesytem listen:register your socket as a server local:create a temporary value for a global variable (dynamic scoping) + localtime:convert UNIX time into record or string using local time log:retrieve the natural logarithm for a number lstat:stat a symbolic link m:match a string with a regular expression pattern map:apply a change to a list to get back a new list with the changes mkdir:create a directory msgctl:SysV IPC message control operations msgget:get SysV IPC message queue msgrcv:receive a SysV IPC message from a message queue msgsnd:send a SysV IPC message to a message queue my:declare and assign a local variable (lexical scoping) next:iterate a block prematurely no:unimport some module symbols or semantics at compile time oct:convert a string to an octal number open:open a file, pipe, or descriptor opendir:open a directory ord:find a character's numeric representation pack:convert a list into a binary representation package:declare a separate global namespace pipe:open a pair of connected filehandles pop:remove the last element from an array and return it pos:find or set the offset for the last/next m//g search print:output a list to a filehandle printf:output a formatted list to a filehandle prototype:get the prototype (if any) of a subroutine push:append one or more elements to an array q:singly quote a string qq:doubly quote a string quotemeta:quote regular expression magic characters qw:quote a list of words qx:backquote quote a string rand:retrieve the next pseudorandom number read:fixed-length buffered input from a filehandle readdir:get a directory from a directory handle readlink:determine where a symbolic link is pointing recv:receive a message over a Socket redo:start this loop iteration over again ref:find out the type of thing being referenced rename:change a filename require:1) demands some semantics, 2) demands that the current version + of Perl be at least as recent as that version, 3) demands that a lib +rary file be included if it hasn't already been included. reset:clear all variables of a given name return:get out of a function early reverse:flip a string or a list rewinddir:reset directory handle rindex:right-to-left substring search rmdir:remove a directory s:replace a pattern with a string scalar:force a scalar context seek:reposition file pointer for random-access I/O seekdir:reposition directory pointer select:reset default output or do I/O multiplexing semctl:SysV semaphore control operations semget:get set of SysV semaphores semop:SysV semaphore operations send:send a message over a socket setgrent:prepare group file for use sethostent:prepare hosts file for use setnetent:prepare networks file for use setpgrp:set the process group of a process setpriority:set a process's nice value setprotoent:prepare protocols file for use setpwent:prepare passwd file for use setservent:prepare services file for use setsockopt:set some socket options shift:remove the first element of an array, and return it shmctl:SysV shared memory operations shmget:get SysV shared memory segment identifier shmread:read SysV shared memory shmwrite:write SysV shared memory shutdown:close down just half of a socket connection sin:return the sine of a number sleep:block for some number of seconds socket:create a socket socketpair:create a pair of sockets sort:sort a list of values splice:add or remove elements anywhere in an array split:split up a string using a regexp delimiter sprintf:formatted print into a string sqrt:square root function srand:seed the random number generator stat:get a file's status information study:optimize input data for repeated searches sub:declare a subroutine, possibly anonymously substr:get or alter a portion of a string symlink:create a symbolic link to a file syscall:execute an arbitrary system call sysread:fixed-length unbuffered input from a filehandle system:run a separate program syswrite:fixed-length unbuffered output to a filehandle tell:get current seekpointer on a filehandle telldir:get current seekpointer on a directory handle tie:bind a variable to an object class time:return number of seconds since 1970 times:return elapsed time for self and child processes tr:transliterate a string (non-AWK style) truncate:shorten a file uc:return upper-case version of a string ucfirst:return a string with just the first letter in upper case umask:set file creation mode mask undef:remove a variable or function definition unlink:remove one link to a file unpack:convert binary structure into normal perl variables unshift:prepend more elements to the beginning of a list untie:break a tie binding to a variable use:load in a module at compile time utime:set a file's last access and modify times values:return a list of the values in a hash vec:test or set particular bits in a string wait:wait for any child process to die waitpid:wait for a particular child process to die wantarray:get list vs array context of current subroutine call warn:print debugging info without bailing write:print a picture record y:transliterate a string (AWK style)

Replies are listed 'Best First'.
Re: Yet Another Perl Aptitude Test (yapat)
by tame1 (Pilgrim) on Feb 28, 2001 at 20:59 UTC
    My perl is barfing over "use warnings".

    Where can I get that module?

    What does this little button do . .<Click>; "USER HAS SIGNED OFF FOR THE DAY"

      The warnings pragma is new in Perl 5.6. Under earlier versions you can get much the same effect using the -w command line option.

      --
      <http://www.dave.org.uk>

      "Perl makes the fun jobs fun
      and the boring jobs bearable" - me

        Running in Perl 5.005, the darn thing always tells me I am wrong, even when I am not!

        Kind of disheartening! (The use warnings is now commented out)

        What does this little button do . .<Click>; "USER HAS SIGNED OFF FOR THE DAY"
      Sorry, I've taken the "use warnings" line out. It's not a module, it's a pragma, and it only works in 5.6. Anyway, it wasn't really necessary, (as davorg says here), since I started out with #!/usr/bin/perl -w. Duh, I guess I just put it there in autopilot. Hope it works for you now!
        I am coming across the problem now that when I answer a question,
        the program tells me I am wrong, no matter what I say.
        If I then use the ? to see the "right" answer, it's the same thing I answered!

        What does this little button do . .<Click>; "USER HAS SIGNED OFF FOR THE DAY"
Re: Yet Another Perl Aptitude Test (yapat)
by EvanK (Chaplain) on Mar 07, 2001 at 04:09 UTC
    Well, you know what I'd like to suggest...mabye instead of just printing the defition and waiting for input, mabye it could do it like a multiple choice thing? and it could pick random terms for the choices? for instance, instead of:
    get network record given its address (weight: 1) (type an answer, '?' to see the right answer, 'score', 'save', or 'qui +t') _
    it could have:
    get network record given its address (weight: 1) a) socket b) bless c) getnetbyaddr d) getgrent (type an answer, '?' to see the right answer, 'score', 'save', or 'qui +t') _
    And they could type in the name of the function OR the letter choice. Just a suggestion.

    ______________________________________________
    When I get a little money, I buy books. If I have any left over, I buy food and clothes.
    -Erasmus

      Dear Erasmus,
      That's a really good idea-- I was thinking about doing something like this to make a kind of "beginner's level", then allowing the advanced level of typing in the answer.

      Ultimately, I'll wrap this up in CGI, put it on a web page, allow recording of high scores, etc etc.

      Good suggestion! I'll try and put this together pretty soon.

      -zeno
        >>Dear Erasmus,<<

        Heh...who's Erasmus? I'm Evan! :-] (I know, my signature.)

        ______________________________________________
        When I get a little money, I buy books. If I have any left over, I buy food and clothes.
        -Erasmus