Sample solutions and discussion Perl Quiz of The Week #5 (20021113) You will write a function to lay out crossword puzzles. If you are unfamiliar with American and British style crossword puzzles, an example is at: http://perl.plover.com/qotw/misc/r005/puzzle.jpg Your function, 'layout_crossword' will get an array argument which represents the desired layout of the crossword puzzle. it will then return a display version of the puzzle. For example, the diagram at the URL above would be represented like this: @sample_puzzle = qw( ....X.....X.... ....X.....X.... ....X.....X.... .......X....... XXX......X..... .....X......XXX ......X...X.... ...X.......X... ....X...X...... XXX......X..... .....X......XXX .......X....... ....X.....X.... ....X.....X.... ....X.....X.... ); If given this array as argument, layout_crossword(@sample_puzzle) would return an array containing the following 61 strings: ############################################################################ #1 #2 #3 #4 ######5 #6 #7 #8 #9 ######10 #11 #12 #13 # # # # # ###### # # # # ###### # # # # # # # # ###### # # # # ###### # # # # ############################################################################ #14 # # # ######15 # # # # ######16 # # # # # # # # ###### # # # # ###### # # # # # # # # ###### # # # # ###### # # # # ############################################################################ #17 # # # ######18 # # # # ######19 # # # # # # # # ###### # # # # ###### # # # # # # # # ###### # # # # ###### # # # # ############################################################################ #20 # # # #21 # # ######22 # #23 # # # # # # # # # # # # ###### # # # # # # # # # # # # # # ###### # # # # # # # ############################################################################ ################24 # # # #25 # ######26 # # # # # ################ # # # # # ###### # # # # # ################ # # # # # ###### # # # # # ############################################################################ #27 #28 #29 # # ######30 # # #31 # # ################ # # # # # ###### # # # # # ################ # # # # # ###### # # # # # ################ ############################################################################ #32 # # # # #33 ######34 # # ######35 #36 #37 #38 # # # # # # # ###### # # ###### # # # # # # # # # # ###### # # ###### # # # # ############################################################################ #39 # # ######40 # #41 # # # #42 ######43 # # # # # # ###### # # # # # # ###### # # # # # # ###### # # # # # # ###### # # # ############################################################################ #44 # # #45 ######46 # # ######47 # #48 # # # # # # # # ###### # # ###### # # # # # # # # # # ###### # # ###### # # # # # # ############################################################################ ################49 #50 # # # #51 ######52 # # # # # ################ # # # # # ###### # # # # # ################ # # # # # ###### # # # # # ############################################################################ #53 #54 #55 # # ######56 # # #57 # # ################ # # # # # ###### # # # # # ################ # # # # # ###### # # # # # ################ ############################################################################ #58 # # # # #59 # ######60 # # # #61 #62 #63 # # # # # # # # ###### # # # # # # # # # # # # # # ###### # # # # # # # ############################################################################ #64 # # # ######65 # #66 # # ######67 # # # # # # # # ###### # # # # ###### # # # # # # # # ###### # # # # ###### # # # # ############################################################################ #68 # # # ######69 # # # # ######70 # # # # # # # # ###### # # # # ###### # # # # # # # # ###### # # # # ###### # # # # ############################################################################ #71 # # # ######72 # # # # ######73 # # # # # # # # ###### # # # # ###### # # # # # # # # ###### # # # # ###### # # # # ############################################################################ (There are 61 lines here, each 76 characters long; the function should return a list of 61 strings, one for each line, with each string 76 characters long.) layout_crossword() will make use of two auxiliary arrays that describe what empty and full squares should look like. In the example above, these arrays were: @empty_square = qw(###### #....# #....# #....# ###### ); @full_square = qw(###### ###### ###### ###### ###### ); layout_crossword() must scan over the input representation, constructing an array of strings, which it will return. It must repeatedly insert the contents of @empty_square or @full_square into the appropriate place in the output array, depending on whether the corresponding character of the input was '.' (an empty square) or any other character (a full square.) layout_crossword() must also compute the appropriate numerals to insert into the blank squares, and insert them into the right places in the output. A numeral should be placed into the upper-leftmost empty part of its square. If the numeral doesn't fit in the square, the program should die. Again, empty parts of a square are denoted by '.' characters. An empty square should receive a numeral if it is the first square in a word; words run from left to right and from top to bottom. See the diagram for an example. All '.' characters should be translated to real spaces on output, as in the example. The function should RETURN A LIST OF STRINGS, which might be printed later. It should not print anything. ---------------------------------------------------------------- There was some confusion about the purpose of the @empty_square and @full_square arrays. (The 'glyphs'.) When I thought up the question, I wanted the function to get three arguments: The crossword template, and the empty and full glyphs. But there was no good way to pass these three arguments to a single function without using references, and the rules of the game say that the regular quiz must be soluble using only the techniques explained in _Learning Perl_. That means no references. So I compromised and had the glyphs passed via to external, global arrays. Unfortunately, I didn't make my intentions clear enough when I posed the problem, and a number of people thought that the glyph arrays were private to the layout_crossword function. This was a perfectly reasonable conclusion, since the problem statement erroneously declared the arrays as 'my' variables. Oops! My apologies to anyone who was confused by this. When I tested the programs that were sent to the qotw-discuss list, I hacked them all to use global glyph arrays. I also did other minor hacking that was necessary to make the programs work with my test harness, which such hacking suggested itself. Three programs consistently produced the best-looking output: Ron Isaacson's, which was by far the best, Alex Lewin's, and mine. But mjd1.pl was 43% shorter than lewin.pl and 51% shorter than isaacson.pl, so that's the one I'll discuss in detail. (Also, both isaacson.pl and lewin.pl use references, and lewin.pl is in object-oriented style, and so outside the scope of _Learning Perl_.) There are two small utility subroutines: sub add_numeral { my ($n, @sq) = @_; my $space = '\.' x length($n); for (@sq) { return @sq if s/$space/$n/; } die "Square was too small for numeral $n\n"; } Here $n is a numeral, and @sq is a glyph array. add_numeral() inserts the numeral $n into the glyph array and returns the result. It first assembles a regular expression that looks for L dots in a row, where L is the length of $n. Then it scans the glyph from top to bottom, looking for the L dots, which represent a space large enough to hold the numeral. When it finds one, it replaces the dots with the numeral and returns the result. If there is no place to put the numeral, it dies. One of the tests I ran used an 'empty' glyph that looked like this: ###### ## ## # # # # ###### A correct program will insert the numeral into the top space like this: ###### ###### ##7 ## ##17## # # # # # # # # ###### ###### Several of the less-correct programs assumed that the empty glyphs would be completely empty inside their borders, and produced outputs like these: ###### ###### #7 ## #17 # # # # # # # # # ###### ###### The other utility subroutine just gets an X and Y coordinate and the puzzle template, and returns true if the corresponding square is empty. sub is_blank { my ($x, $y, @puzzle) = @_; return if $y < 0 || $x < 0 || $y >= @puzzle || $x > length($puzzle[0]); return substr($puzzle[$y], $x, 1) eq "."; } The reason this is here is to establish the convention that squares outside the puzzle are considered to be full, not empty. This simplifies the process of determining whether an empty square should receive a numeral. The complete rule for deciding whether a square gets a numeral is that a blank square gets a numeral if the square above is full, if the square to the left is full, if it is in the top row, or if it is in the leftmost column. By adopting the convention that squares outside the diagram are considered full, we can simplify the logic for numbering squares: A square gets a numeral if the square above or to the left is full. The main function is fairly straightforward. It loops over the rows from top to bottom, and over the squares in each row from left to right. It sets '@square' to an appropriate glyph for the current square, copying it from @full_square or @empty_square as appropriate, and then, if empty, it uses add_numeral() to add a numeral to @square if the square above or to the left is full. use strict; our (@empty_square, @full_square); sub layout_crossword { my @puzzle = @_; my $N = 1; my @result; my ($h, $w) = (scalar(@puzzle), length($puzzle[0])); for my $y (0 .. $h-1) { my @row; for my $x (0 .. $w-1) { my @square; my $blank = is_blank($x, $y, @puzzle); if ($blank) { @square = @empty_square; unless (is_blank($x-1, $y, @puzzle) && is_blank($x, $y-1, @puzzle)) { @square = add_numeral($N++, @square); } } else { @square = @full_square; } Now there's the interesting question of what to do with the overlapping parts of adjacent squares. This program uses an extremely simple strategy: It trims off the left-hand edge of the square, so that: +----+ ----+ and ###### ##### | | becomes | # # becomes # | | | # # # +----+ ----+ ###### ##### Now the current square can borrow the right-hand edge of the square to its left. Squares in the leftmost column have nobody to borrow from, so the trimming is not performed for those squares: # trim off overlap with square to left if ($x > 0) { s/^.// for @square; } Then we similarly trim off the topmost edge of each square, except for those in the topmost row of the diagram: # Now trim off overlap with square above if ($y > 0) { shift @square; } Now that the square is complete, we append it to the right-hand end of the current row of the output: # add square to output for (0 .. $#square) { $row[$_] .= $square[$_]; } } When we finish a row, we turn the dots into spaces, as required by the spec, and insert the row into the return value array. When we finish the last row, we return the array: for (@row) { tr/./ /; } push @result, @row; } @result; } ---------------------------------------------------------------- Notes: 1. The test data and the test results are at http://perl.plover.com/qotw/misc/r005/ The program above is http://perl.plover.com/qotw/misc/r005/mjd1.pl The subdirectory 'templates' contains four sample crossword templates. The subdirectory 'glyphs' contains ten pairs of glyphs. The test harness, TestXWord.pm, tries the function on each combination of templates and glyphs, and deposits the output into the 'output' directory. To use it, say perl -MTestXWord yourprogram.pl < /dev/null The 'check-results' program checks the outputs against the sample results in the 'standard' directory. Files in the 'standard' directory have names of the form PUZZLE-glyphset-##.x where ## is a number of points and 'x' is an arbitrary letter. If a program's output matches this file, it is awarded that many points. If an output doesn't match any of the 'standard' forms, it is copied to the 'mismatches' directory. I went over 'mismatches' repeatedly and copied all the 'mismatches' that actually looked good into the 'standards' directory. 2. The technique I used to abutting the squares is very simple, and produces good-looking output most of the time. For some examples, it is not so good. One of the test glyph sets, 'mixed', contains mismatched glyphs: +----+ ###### |....| ###### |....| ###### |....| ###### +----+ ###### With these glyphs, the asymmetry in my algorithm becomes obvious: ######----+----+----+##### ######1 |2 |3 |##### ###### | | |##### ###### | | |##### ######----+----+----+##### ######4 | | |5 | ###### | | | | ###### | | | | ######----+----+----+----+ |6 | |#####7 | | | | |##### | | | | |##### | | +----+----+#####----+----+ |8 | |9 | |##### | | | | |##### | | | | |##### +----+----+----+----+##### ######10 | | |##### ###### | | |##### ###### | | |##### ######----+----+----+##### When there's a disagreement between two adjoining cells about what their shared property should look like, the cell above or the cell to the left always wins. 3. Peter Haworth's program does better with the mixed glyphs, and is also very small. I thought that getting this exactly right would be a big pain. I even wrote code do to it, and then decided to leave it out of the question because it was too much code. Peter cuts the Gordian Knot here and uses a very simple method. Peter's program starts by filling the entire grid with full-square glyphs, and then superimposes the empty-square glyphs on top of those. Empty squares win whenever there is a disagreement about the appearance of shared territory. For the template above, his program generates this output: #####+----+----+----+##### #####|1 |2 |3 |##### #####| | | |##### #####| | | |##### #####+----+----+----+----+ #####|4 | | |5 | #####| | | | | #####| | | | | +----+----+----+----+----+ |6 | |####|7 | | | | |####| | | | | |####| | | +----+----+----+----+----+ |8 | |9 | |##### | | | | |##### | | | | |##### +----+----+----+----+##### #####|10 | | |##### #####| | | |##### #####| | | |##### #####+----+----+----+##### The difference is subtle, but I think it is much handsomer. 4. The algorithm I used to determine whether a square should receive a numeral fails in certain cases. Consider this template: ..... .#.#. ..... .#.#. ..... My program generates this output: But it should be: ##################### ##################### #1 #2 #3 #4 #5 # #1 # #2 # #3 # # # # # # # # # # # # # ##################### ##################### #6 #####7 #####8 # # ##### ##### # # ##### ##### # # ##### ##### # ##################### ##################### #9 #10 # #11 # # #5 # # # # # # # # # # # # # # # # # ##################### ##################### #12 #####13 #####14 # # ##### ##### # # ##### ##### # # ##### ##### # ##################### ##################### #15 #16 # #17 # # #6 # # # # # # # # # # # # # # # # # ##################### ##################### The problem here is that I generate a numeral for any empty square below (or to the right of) a full one, but it's only correct to generate a numeral for an empty square below (or to the right of) a full one that is not also above (or to the left of) a full one. If there are full squares on both sides, the numeral is inappropriate because there is nowhere for the word to go. In American-style crossword puzzles, this situation can never occur, because there is an express prohibition on exactly this situation. Every blank square must be at the intersection of two words, one across and one down. A square that is part of only a single word is called an 'unkeyed square' and is strictly forbidden. However, in many British-style crossword puzzles, most famously the London Times Sunday puzzle, there are unkeyed letters. I specifically mentioned "British style crossword puzzles" in the question, so this is a defect. To fix it, change unless (is_blank($x-1, $y, @puzzle) && is_blank($x, $y-1, @puzzle)) { @square = add_numeral($N++, @square); } to if (!is_blank($x-1, $y, @puzzle) && is_blank($x+1, $y, @puzzle) ||!is_blank($x, $y-1, @puzzle) && is_blank($x, $y+1, @puzzle)) { @square = add_numeral($N++, @square); } I forgot all about this until I looked closely at Peter Haworth's contribution, which gets it right. (Peter, of course, is a Brit.) 5. ensch2.pl is a peculiar case. Faced with the problem of how to overlap adjacent glyphs, Peter B. Ensch did something interesting. His program ensch2.pl inserted backspace characters between the glyphs: +---+^H+---+^H | |^H| |^H +---+^H+---+^H (I have represented the backspaces by '^H'). I didn't realize this at first. When I went to look at the program's output, I used the 'less' pager program, which interpreted the ^H's as requests to overstrike! So when I used the pager, I got what looked like +---+--- | | +---+--- but with the middle vertical line in boldface. When I just printed the output to the terminal with 'cat', it looked normal. But to the automatic test suite, the answers looked completely wrong. The test suite hated it, but if the backspacing is allowed, ensch2.pl would be one of the better performers. 6. One common problem was programs that assumed that the glyphs would have a certain appearance, or would be a certain size. Using the glyphs ## and .. ## .. caused problems for many peoples' programs, which could not figure out how to fit the numerals in, or which assumed the presence of a border. The example program above completely botches these examples because it insists on overlapping the adjacent glyphs, even though that means stripping out 3/4 of each glyph. For small glyphs, overlapping is a mistake. Ron Isaacson's program was the only one posted on the qotw-discuss list that handled this case properly at all. His program overlaps squares only if the empty and full square borders match. This leads to cluttered but reasonable behavior in the 'mixed' case above: ######+----++----++----+###### ######|1 ||2 ||3 |###### ######| || || |###### ######| || || |###### ######+----++----++----+###### ######+----++----++----++----+ ######|4 || || ||5 | ######| || || || | ######| || || || | ######+----++----++----++----+ +----++----+######+----++----+ |6 || |######|7 || | | || |######| || | | || |######| || | +----++----+######+----++----+ +----++----++----++----+###### |8 || ||9 || |###### | || || || |###### | || || || |###### +----++----++----++----+###### ######+----++----++----+###### ######|10 || || |###### ######| || || |###### ######| || || |###### ######+----++----++----+###### and perfect behavior in the very-small-glyph case: ##1 2 3 ## ## ## ##4 5 ## 6 ##7 ## 8 9 ## ## ##10 ## ## ## 7. There were a few oddities that caused some programs to appear to perform more poorly in the tests than was actually warranted. The program sainio.pl used a global variable to store the current clue number, and never reset it between calls to layout_crossword(). Since the test harness called layout_crossword() forty times in a row, the numbers grew to be four digits long and then wouldn't fit into the boxes any more. sainio2.pl is a corrected version. schmidt.pl and schmidt2.pl copied the glyphs into two private arrays, @UL_empty_square and @UL_full_square. Unfortunately, they did so at compile time, thus foreclosing the possibility that anyone could change the glyphs later, and preventing the test harness from changing the glyphs. With this defect repaired, these two programs did well in the testing. As I mentioned above, a better design for this function would be for it to have three arguments instead of using the two global glyph arrays. jones2.pm did do this, so it failed the tests. I hacked it so that it used the specified argument format instead. But the output was quite broken! It didn't use the correct glyphs, and it only numbered the 'across' clues! ++++++++++++++++ +##+1 + + +##+ +##+ + + +##+ +##+ + + +##+ ++++++++++++++++ +##+2 + + + + +##+ + + + + +##+ + + + + ++++++++++++++++ +3 + +##+4 + + + + +##+ + + + + +##+ + + ++++++++++++++++ +5 + + + +##+ + + + + +##+ + + + + +##+ ++++++++++++++++ +##+6 + + +##+ +##+ + + +##+ +##+ + + +##+ ++++++++++++++++ wolters.pl produced some reasonable-looking outputs, but did not translate the dots to spaces, so the results looked like: +----+----+----+----+----+ |####|1...|2...|3...|####| |####|....|....|....|####| |####|....|....|....|####| +----+----+----+----+----+ |####|4...|....|....|5...| |####|....|....|....|....| |####|....|....|....|....| +----+----+----+----+----+ |6...|....|####|7...|....| |....|....|####|....|....| |....|....|####|....|....| +----+----+----+----+----+ |8...|....|9...|....|####| |....|....|....|....|####| |....|....|....|....|####| +----+----+----+----+----+ |####|10..|....|....|####| |####|....|....|....|####| |####|....|....|....|####| +----+----+----+----+----+ I added the line tr/./ / for @puzzle; just before the return from the function. However, it didn't correctly handle different-sized glyphs. 8. I got the idea for this problem from _The Art of Computer Programming, Vol. 1: Fundamental Algorithms_, by Donald E. Knuth. (In the 3rd edition, it is problem 1.3.2.23, and is on page 163.) I remembered that there was some issue in the Knuth problem that made it more difficult than the problem I was posing, but I didn't remember what is was, and I didn't look it up until just now. The Knuth version of the puzzle says that black squares at the border of the puzzle should be deleted from the output. His example: If the input was #....# ..#... ....#. .#.... ...#.. #....# Then the output should be +++++++++++++++++++++ +01 + +02 +03 + + + + + + +++++++++++++++++++++++++++++++ +04 + ++++++05 + +06 + + + ++++++ + + + +++++++++++++++++++++++++++++++ +07 + +08 + ++++++ + + + + + ++++++ + +++++++++++++++++++++++++++++++ + ++++++09 + +10 + + + ++++++ + + + + +++++++++++++++++++++++++++++++ +11 +12 + ++++++13 + + + + + ++++++ + + +++++++++++++++++++++++++++++++ +14 + + + + + + + + + +++++++++++++++++++++ Notice how the corner black squares have vanished. If there were other black squares next to these, they would vanish also. Knuth says: "The diagram... might have long paths of black squares that are connected to the outside in strange ways." Although my version of the problem was missing this complication, it had an additional complication because the full and empty square glyphs were variable instead of fixed. My problem specification didn't provide much guidance about how to make the glyphs overlap, and in the case where the edges of the two glyphs didn't match, it wasn't immediately clear how to overlap them and still make the result look good. 9. It was pointed out on the -discuss list that the code I posted yields a warning, if warnings are enabled. Specifically my @empty_square = qw(###### #....# #....# #....# ###### ); yields the warning "Possible attempt to put comments in qw() list". One poster to the list said: Seeing as much of the Perl community have been trying to get new Perl programmers to turn on warnings and strict, in an effort to highlight problems with their code, I have been surprised to see MJD's quiz this week. In order to use the empty_square and full_square arrays, as included in the quiz text, you are actually inclining people to turn off strict and warnings. Which IMHO is not good. http://perl.plover.com/~alias/list.cgi?1:mss:605 This remark, unfortunately, comes right at the intersection of several philosophical stances I hold, and that makes me very cranky. First, the warning has nothing whatever to do with 'strict'. Throughout this message, the author says "warnings and strict", "strict and warnings", as if in one breath. The code in question is completely strict-safe. (It *shouldn't* be, but that is an unrelated matter.) Why mention 'strict' at all? The Perl community has become increasingly dogmatic in the past few years about the use of 'strict'. It is common to see people ask questions in newsgroups, and to post four-line examples, and be criticized for failing to use 'strict'. "Why aren't you using 'strict'?" people ask. Well, because it is a four-line example posted in a Usenet article, obviously. 'strict' has no value in such cases, except perhaps to get people to shut up about it. It is true that the Perl community has been trying to get new Perl programmers to turn on warnings and strict. I have no objection to this. What I do object to is that the community seems to be trying to get people to turn on warnings and strict without knowing why they are doing that, or what they are for. It is common to see people ask questions in newsgroups like this: I got the error "Global symbol "$z" requires explicit package name." What does that mean? This is like someone coming to say that there is a loud bell ringing in the hallway, and what should be done about it? Of course, it is the fire alarm. They were told to always turn the fire alarm on, but nobody told them what it would mean if it began to ring. I believe that one of the biggest problems with programming as a profession is that programmers are fearful and superstitious. Programming is only about sixty years old. When chemistry was sixty years old, practitioners were trying to turn lead into gold, to extract the essence of fire, and so forth. After a few hundred years they learned a little more and began to study phlogiston. So we are in the dark ages of programming, and we live in a dangerous world that we do understand only poorly. Many people respond to this with superstition: "Always use objects." "Never use a global variable." "Perl is better than Python." "Always use strict." We do not have to give in to this superstition. We don't have to say "To be safe, always use strict. And to be double safe, throw salt over your left shoulder." We are engineers, and programming is empirical. We should by all means encourage beginners to use the best possible engineering practices. But we should not encourage the blind use of certain programming features. When a person says "warnings and strict" four times, when talking about a piece of code that emits a warning but is strict-safe, what is going on? Clearly this person is not thinking about the meaning of what he is saying. The code is also not ISO 9000 compliant; why not mention that also? I think this is a superstitious effect. Instead of looking at the reality of the situation, which is that the code raises certain specific warnings, and the warnings have a certain specific meaning, which suggests that the code should be examined for certain specific problems, the speaker has lumped all warnings and diagnostics together in one group and adopted the stance that all such warnings should be eliminated. This shows a lack of understanding. I think almost anyone who says "always use strict" is suffering from this lack of understanding. "strict" is not one but three features, and none of these three features has anything at all to do with the other two. Saying "always use strict" is like saying "always use a hammer, a screwdriver, and a drill." For some projects, perhaps only the hammer and drill are appropriate, and the screwdriver is an irrelevant distraction. So it is too with "strict". People are being encouraged to load up with tools that they don't know how to use. The results of this are sometimes stunningly silly. I have many examples of programs that start by saying: use strict; my ($rounds, $round_temp, $squares, $page, $x, $y, $z, $cell, $player_move, @available_choices, $computer_move, @choices, $round, $winner, $player_move_pretty, $computer_move_pretty); my ($round_minus_one); The programmer here wants to use global variables; she does not understand what lexical variables or for, or why they are preferred. But, at the advice of some well-meaning person, she has put 'use strict' at the top of the program, and now global variables are forbidden. So she declares every variable at the top of the program, effectively making them all global, and getting none of the encapsulation, reuse, or maintenance benefits that lexical variables are supposed to accrue. Another example: my @ret=eval "layout_tree_$format(\$tree)"; Why do this? There is a safer and more efficient method: my @ret= "layout_tree_$format"->($tree); Perhaps the programmer didn't know about the safer and more efficient method. Or perhaps he avoids it, as many people do, because it causes a 'strict refs' failure, while the 'eval' method, although inferior in every way, does not. I don't think we need to do more to encourage people to usewarningsandstrict. I think we need to do more to encourage them to understand the warnings they get and to take appropriate action. When I teach programming classes, I am always astonished at how little attention the students pay to the error messages they receive. The compiler complains of a syntax error on line 197, and the programmer's response is not to look at line 197, but to eyeball a random portion of the program in the hope that the error is there. By encouraging people to "always use strict and warnings" and to think of diagnostic messages as bad, and something to avoid, we are doing the exact wrong thing. The right thing is to encourage people to pay attention to the messages, to try to understand them, and then to make considered judgements about what they mean. That is what I think beginners need to learn. In this case, the warning is saying "Possible attempt to put comments in qw() list". What does that mean? It means that perl has seen a # sign in a qw(), and it is afraid that I might be trying to write something like this: my @array = qw( red crimson # But not scarlet blue azure green ); Here the thing that looks like a comment is not a comment; instead, the @array gets nine elements, including 'But', 'not', 'scarlet', and '#'. It is good that perl warns us about this. In my example code, however, this is not the case: my @empty_square = qw(###### #....# #....# #....# ###### ); I am *not* trying to put a comment into a qw() list. Perl sees the '#' signs, and it is afraid that I *might* be doing that, so it warns me. But it is mistaken; the # signs are doing what I want here. In such a case, it is perfectly appropriate to ignore the warning. The compiler has had its say, and I have listened to it, but it is just a machine, and I know better than it does what I want. If you are troubled by the warning message itself, the correct approach here is NOT to code around it by writing something like this: my @empty_square = ('######', '#....#', '#....#', '#....#', '######', ); The correct response is to SHUT OFF THE WARNING: my @empty_square; { no warnings 'qw'; @empty_square = qw(###### #....# #....# #....# ###### ); } (The "no warnings 'qw'" declaration shuts off only those warnings that pertain to the qw() operator, and only inside that one block. Elsewhere, all warnings will still be issued. Inside the block, all other warnings will still be issued.) The thing that really irks me about the 'strict' dogmatism is how defective is most of the dialog about it. Last year I read a review of a book about using Perl to write CGI programs. The reviewer harshly criticized the author for not having used 'strict'. The reviewer did not say which of the three parts of 'strict' would have been valuable. His opinion was apparently that all programs should use 'strict', whether it would be valuable or not. I objected, pointing out that none of the programs in the book used references, so that 'strict refs' would not be doing anything; that none of the example programs were more than twenty lines long, so there was no practical difference between global and lexical variables, and hence no reason to use 'strict vars' to forbid global variables; and that the only value of 'strict subs' is to prevent future maintenance problems when someone adds a subroutine whose name is the same as what was previously a bareword, a feature of small value at best and of less value in these tiny example programs. But the reviewer did not address any of my specific technical points. Instead, he told an anecdote about a bad programmer, and said that we should teach everyone "good programming style" right from the start. That begs the question of what "good programming style" is. I realized then that the reason for our disagreement was that my idea of good programming style was motivated by what was useful and effective, whereas his was motivated by superstition. Considerations of usefulness did not come into play. That is my opinion on "use warnings and strict". The short version is: No, I do not believe there is any inherent value in "keeping warnings and strict happy", and I am going to continue to try to do the most appropriate thing for the circumstances. I believe that that is the only way to set the best possible example for beginners. Sorry to go on so long, but this has all been seething inside me for a long time. New quizzes tomorrow. My grateful thanks to everyone who participated in the discussion, and also to those who quietly worked the problemns on their own.