#!/usr/bin/env runghc import Control.Monad (liftM) import Data.Char (isAlphaNum) import Data.List (intersperse, intercalate) main :: IO () main = do arg <- fmap (concatMap (permuteAndPutTogether . lines) . breakBlankLines) getContents mapM_ putStrLn arg -- Utility function. It *should* be in the base libraries, but alas... split :: (a -> Bool) -> [a] -> [[a]] split _ [] = [] split p s = let (l,s') = break p s in l : case s' of [] -> [] (r:s'') -> [r] : split p s'' -- We don't want to split on newlines, but empty lines - there is one blank line -- between each supplied item. breakBlankLines :: String -> [String] breakBlankLines = map (\x -> if head x == nl then tail x else x) . filter (not . (==) "") . map concat . split (== "") . split (== nl) where nl = '\n' {- | OK. So each 'text' of our input has previously been split up by newlines. So if we wanted to memorize a quatrain or rubaiyat, say, it gets piped in on stdin as foo\nbar\n\quux\nbaz, turned into ["foo", "bar", "quux", "baz"], and then passed into 'permuteAndPutTogether'. This monster mangles the input through 'onHalves', puts the [[String]] pieces together (remembering that in the tab format, a literal newline is actual "
"), and then appends the answer, and catenates *that* (remembering that in the tabbed format, we separate questions and answers not with a newline, but a tab character). Finished, we let 'main' handle the I/O of printing to stdout. Whew! (In theory, this function should work on any length of text, thanks to the generality of 'onNths', but I haven't tested it with anything but the rubaiyats of Omar Khayyam.) -} permuteAndPutTogether:: [String] -> [String] permuteAndPutTogether str = map ((\x -> x ++ "\t" ++ concatQuestn str) . concatQuestn) $ onHalves hide str where concatQuestn = intercalate "
" {- | Take an answer, and hide it, so we can do Cloze deletion. ie, you could do question = [hide "First line", "Second line"]; answer = ["First line", "Second line"]. -} hide :: String -> String hide = map (\x -> if isAlphaNum x then '_' else x) {- | A highly general list function exploiting the capabilities of the List monad to generate permutations of a list such that we apply a function to all possible halves or thirds or nths. Thanks be to vixey on #haskell. -} onNths :: (Num t, Eq t) => t -> (a -> a) -> [a] -> [[a]] onNths 0 _ list = return list onNths n f (x:xs) = map (f x :) (onNths (n-1) f xs) ++ map (x :) (onNths n f xs) onNths _ _ [] = [] -- Yes, we do need to put this definition last. {- | Given a function f and the list [1,2], we'd get back [[f 1, 2], [1, f 2]]; for f [1,2,3,4], we'd get [[f 1, f 2, 3, 4], [1, 2, f 3, f 4], [f 1, 2, f 3, 4], [1, f 2, 3, f 4]]. -} onHalves :: (a -> a) -> [a] -> [[a]] onHalves f list = onNths (length list `div` 2) f list