Contents: Chronological Listing Of Exercises
July 2, 2010
Although Scheme is a great language for the algorithmic exercises that we usually examine, I wrote this program in Awk, because of its good support for text munging:
export PRAXIS=/home/phil/praxis
awk ' # exercises in chronological order (page "chron")
BEGIN { itemsperpage = 30; FS = "\n"; RS = ""
split("Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Oct:Nov:Dec",
monthname, ":") }
$1 ~ /^number\t[1-9][0-9]*$/ {
for (i=1; i<=NF; i++)
if (split($i, f, /\t/) == 2)
val[f[1]] = f[2]
out[++nitems] = \
sprintf("<tr><td>%d</td><td>%02d %s %d</td>" \
"<td><a href=\"/%d/%02d/%02d/%s/\">%s</a>: %s</td>" \
"<td><a href=\"/%d/%02d/%02d/%s/\">exercise</a> " \
"<a href=\"/%d/%02d/%02d/%s/%d/\">solution</a> " \
"<a href=\"http://programmingpraxis.codepad.org/" \
"%s\">codepad</a></td></tr>",
val["number"], val["pubday"], monthname[val["pubmon"]],
val["pubyear"], val["pubyear"], val["pubmon"],
val["pubday"], val["file"], val["title"],
val["blurb"], val["pubyear"], val["pubmon"],
val["pubday"], val["file"], val["pubyear"],
val["pubmon"], val["pubday"], val["file"],
val["soln"], val["codepad"]) }
# END { for (i=1; i<=nitems; i++) {
# if (i % itemsperpage == 1)
# printheader(i, nitems)
# print ""; print out[i]
# if (i % itemsperpage == 0 || i == nitems)
# printfooter(i, nitems)
# if (i % itemsperpage == 0 && i != nitems)
# printseparator(i, nitems) } }
END { print "<table cellpadding=\"10\">"
for (i=1; i<=nitems; i++) {
print ""; print out[i] }
print "</table>" }
function ceiling(n) { if (int(n) == n) return n; else return int(n) + 1 }
function printheader(i, nitems) {
printf("%s", "<big><big>Page: ")
for (page=1; page<=ceiling(nitems/itemsperpage); page++) {
if (ceiling(i/itemsperpage) == page) printf(" %d", page)
else printf(" <a href=\"/chron/%d\">%d</a>", page, page) }
print "</big></big>"; print ""; print "<table>" }
function printfooter(i, nitems) { print ""; print "</table>" }
function printseparator(i, nitems) { print ""; print "<!--nextpage-->\n" }
' $PRAXIS/praxis.info > $PRAXIS/pages/chron
The BEGIN action does some needed initialization. The main loop takes every record (maximal sequence of non-blank lines) beginning with the regular expression ^number\t[1-9][0-9]*$, splits the lines into name/value pairs stored in the val associative array, then builds an output line and stores it in the out associative array, incrementing the nitems variable as it does so. The END action writes the table header, the output lines in order, and the table footer. The program that creates the list of exercises in reverse chronological order is the same, except that the END action counts the i variable down from nitems to 1; it is not shown here.
Originally, I intended to put page breaks every thirty exercises, as in the original Contents page, but I decided instead to just put the entire listing on a single page, which is more convenient; after seeing the size of the permuted table of contents, it no longer seemed to matter that the chronological listing was too long. But I left the code to do that in the program source text, commented out, in case I change my mind some time in the future.
The code is available at http://programmingpraxis.codepad.org/KpvDEPnn.
[…] Praxis – Chronological Listing Of Exercises By Remco Niemeijer In today’s Programming Praxis exercise our goal is to replicate a script Phil wrote to generate chronological […]
My Haskell solution (see http://bonsaicode.wordpress.com/2010/07/02/programming-praxis-chronological-listing-of-exercises/ for a version with comments):
import Data.List import Data.List.Split import Text.Printf import Text.Regex.Posix toMonth :: Int -> String toMonth m = chunk 3 "JanFebMarAprMayJunJulAugSepOctNovDec" !! (m - 1) item :: [[String]] -> String item xs = printf "<tr><td>%s</td><td>%02s %s %s</td><td>%s: %s</td>\ \<td>%s%s<a href=\"http://programmingpraxis.codepad.org/%s\">\ \codepad</a></td></tr>" (g "number") (g "pubday") (toMonth . read $ g "pubmon") (g "pubyear") (link "" (g "title")) (g "blurb") (link "" "exercise") (link ("/" ++ g "soln") "solution") (g "codepad") where g x = maybe "" last $ find ((== x) . head) xs link :: String -> String -> String link = printf "<a href=\"/%s/%02s/%02s/%s%s/\">%s</a>" (g "pubyear") (g "pubmon") (g "pubday") (g "file") items :: String -> [String] items = map (item . map (splitOn "\t") . lines) . filter (=~ "^number\t[1-9][0-9]*$") . splitOn "\n\n" listing :: ([String] -> [String]) -> String -> String listing f xs = "<table cellpadding=\"10\">" ++ concat (f $ items xs) ++ "</table>" main :: IO () main = do x <- readFile "praxis.info" putStrLn $ listing id x putStrLn $ listing reverse x