#!/usr/bin/perl -00 use strict; use FileHandle; $| = 1; my $DELAY = 2; my $RANDOMIZE = 1; my @questions; while () { next if /===/; my %record; (undef, %record) = split /^([^:\s]+):\s*/m; for (@record{keys %record}) { s/\s+/ /g } if ($record{Question}) { push @questions, \%record} elsif($record{Answer} ) { push @{ $questions[-1]{Answers} }, \%record} else { die "unknown record $_" } } rand_questions(); exit; show_full_quiz(); #################################### sub rand_questions { my $q = @questions[rand @questions]; for (my $qnum = 0; $qnum < $#questions; $qnum++) { system('clear'); $q = $questions[$qnum]; question("Q".(1+$qnum), $q->{Question}); my $anum = 'a'; my @answers = scramble( (@{$q->{Answers}}) ); for my $a ( @answers ) { answer($anum++, $a->{Answer}); } delay(5); print "\nANSWERS:\n\n"; for ( my $i = 0; $i <= $#answers; $i++ ) { my $a = $answers[$i]; explain( ( 'a' .. 'z' )[$i], $a->{Correct} . $a->{Why} ); print "\n"; delay(2 + length($a->{Why})/50); } delay(5); } } sub delay { my $count = shift; sleep(1 + $DELAY * $count); } sub rand_question { my $q = @questions[rand @questions]; system('clear'); question('Q', $q->{Question}); my $anum = 'a'; my @answers = scramble( (@{$q->{Answers}}) ); for my $a ( @answers ) { answer($anum++, $a->{Answer}); } delay(3); print "\nANSWERS:\n\n"; for ( my $i = 0; $i <= $#answers; $i++ ) { answer( ( 'a' .. 'z' )[$i], $answers[$i]{Correct} . $answers[$i]{Why} ); print "\n"; delay(1); } print "\n"; delay(5); } sub show_full_quiz { my $qnum = '01'; for my $q (scramble(@questions)) { fwrite ($qnum++, $q->{Question}); my $anum = 'a'; for my $a ( scramble(@{$q->{Answers}}) ) { fwrite(' '.$anum++.':', $a->{Answer}); } print "\n"; } } sub scramble { return @_ unless $RANDOMIZE; my @list; srand(time() ^ ($$ + ($$ << 15))); push(@list, splice (@_, rand @_, 1)) while @_; return @list; } sub fwrite { my($num, $text) = @_; sub question { STDOUT->format_name("Question"); &fwrite; } sub answer { STDOUT->format_name("Answer"); &fwrite; } sub explain { STDOUT->format_name("Explain"); &fwrite; } format Question = @>>: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $num, $text ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $text . format Answer = @>>: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $num, $text ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $text . format Explain = @>>: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $num, $text ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $text . write; } __DATA__ Question: How do you produce a reference to a list? Type: References Difficulty: 6/7 (Hard) Answer: \@array Correct: No. Why: @array is not a list, but an array. Answer: [ @array ] Correct: No. Why: That makes a reference to a newly allocated anonymous array, and populates it with a copy of the contents of @array. Answer: \($s, @a, %h, &c) Correct: No. Why: The backslash operator is distributive across a list, and produces a list in return, this being (\$s, \@a, \%h, \&c). Well. In list context. In scalar context, it's a strange way to get a reference to the function &c. Answer: You can't. Correct: Yes. Why: A list is not an array, although is many places one may be used for the other. An array has an AV allocated, whereas a list is just some values on a stack somewhere. You cannot alter the length of a list, for example, any more than you could alter a number by saying something like 23++. While an array contains a list, it is not a list itself. ======================================================================== Question: What happens when you return a reference to a private variable? Type: References Difficulty: 4/7 (Medium) Answer: You get a core dump later when you use it. Correct: No. Why: Perl is not C or C++. Answer: The underlying object is silently copied. Correct: No. Why: Even though the reference returned is for all intents and purposes a copy of the original (Perl uses return by reference), the underlying referent has not changed. Answer: The Right Thing (tm). Correct: Yes. Why: Perl keeps track of your variables, whether dynamic or otherwise, and doesn't free things before you're done using them. Answer: The compiler doesn't let you. Correct: No. Why: Perl seldom stops you from doing what you want to do, and tries very hard to do what you mean to do. This is one of those cases. ======================================================================== Question: Why aren't Perl's patterns regular expressions? Type: Regular expressions Difficulty: 3/7 (Medium) Answer: Because Perl patterns have backreferences. Correct: Yes. Why: A regular expression by definition must be able to determine the next state in the finite automaton without requiring any extra memory to keep around previous state. A pattern /([ab]+)c\1/ requires the state machine to remember old states, and thus disqualifies such patterns as being regular expressions in the classic sense of the term. Answer: Because Perl allows both minimal matching and maximal matching in the same pattern. Correct: No. Why: The mere presence of minimal and maximal repetitions does not disqualify a language from being "regular". Answer: Because Perl uses a non-deterministic finite automaton rather than a deterministic finite automaton. Correct: No. Why: Both NFAs and DFAs can be used to solve regular expressions. Given an NFA, a DFA for it can be constructed, and vice versa. For example, classical grep uses an NFA, while classical egrep a DFA. Whether a pattern matches a particular string doesn't change, but where the match occurs may. In any case, they're both regular. However, an NFA can also be modified to handle backtracking, while a DFA cannot. Answer: Because Perl patterns can have look-aheads assertions and negations. Correct: No. Why: The `(?=foo)' and `(?!foo)' constructs no more violate whether the language is regular than do `^' and `$', which are also zero-width statements. ======================================================================== Question: What happens to objects lost in "unreachable" memory, such as the object returned by Ob->new() in `{ my $ap; $ap = [ Ob->new(), \$ap ]; }' ? Type: Objects Difficulty: 4/7 (Medium) Answer: Their destructors are called when that interpreter thread shuts down. Correct: Yes. Why: When the interpreter exits, it first does an exhaustive search looking for anything that it allocated. This allows Perl to be used in embedded and multithreaded applications safely, and furthermore guarantees correctness of object code. Answer: Their destructors are called when the memory becomes unreachable. Correct: No. Why: Under the current implementation, the reference-counted garbage collection system will not notice that the object in $ap's array cannot be reached, because the array reference itself never has its reference count go to zero. Answer: Their destructors are never called. Correct: No. Why: That would be very bad, because then you could have objects whose class-specific cleanup code didn't get called ever. Answer: Perl doesn't support destructors. Correct: No. Why: A class's DESTROY function, or that of its base classes, is called for any cleanup. It is not expected to deallocate memory, however. ======================================================================== Question: How do you give functions private variables that retain their values between calls? Type: Subroutines, Scoping Difficulty: 5/7 (Medium) Answer: Perl doesn't support that. Correct: No. Why: It would be difficult to keep private state in a function otherwise. Answer: Include them as extra parameters in the prototype list, but don't pass anything in at that slot. Correct: No. Why: Perl is not the Korn shell, nor anything like that. If you tried this, your program probably wouldn't even compile. Answer: Use localized globals. Correct: No. Why: The local() operator merely saves the old value of a global variable, restoring that value when the block in which the local occurred exits. Once the subroutine exits, the temporary value is lost. Before then, other functions can access the temporary value of that global variable. Answer: Create a scope surrounding that sub that contains lexicals. Correct: Yes. Why: Only lexical variables are truly private, and they will persist even when their block exits if something still cares about them. Thus: { my $i = 0; sub next_i { $i++ } sub last_i { --$i } } creates two functions that share a private variable. The $i variable will not be deallocated when its block goes away because next_i and last_i need to be able to access it. ======================================================================== Question: What value is returned by a lone `return;' statement? Type: Subroutines, Syntax Difficulty: 3/7 (Medium) Answer: The undefined value. Correct: No. Why: That would only be true in scalar context. Answer: The empty list value (). Correct: No. Why: That would only be true in list context. Answer: The undefined value in scalar context, and the empty list value () in list context. Correct: Yes. Why: This way functions that wish to return failure can just use a simple return without worrying about the context in which they were called. Answer: The result of the last evaluated expression in that subroutine's block. Correct: No. Why: That's what happens when the function ends without return being used at all. ======================================================================== Question: Assuming $_ contains HTML, which of the following substitutions will remove all tags in it? Type: Regular Expressions, WWW Difficulty: 6/7 (Hard) Answer: You can't do that. Correct: Yes. Why: If it weren't for HTML comments, improperly formatted HTML, and tags with interesting data like