#!/usr/bin/runhugs -- numsort -- -- Sort stdin by lines, except that sequences of digits are sniffed -- out and compared numerically rather than lexically. Useful for -- sorting things like 'foo1, foo2, ..., foo10, foo11, ...' which -- normal sort wouldn't get right. -- -- Bugs: Reads only from stdin, not filenames specified. -- Doesn't work reading from the console. -- -- Todo: use the ReadS parsing stuff rather than chunking by hand -- -- -- Ed Avis, ed@membled.com, 2000-06-09 -- module Main where import List import "/home/ed/lib/hs/Misc.hs" main = unix_filter numsort numsort :: String -> String numsort = wrap (lines, unlines) $ sortBy cmp_lines where cmp_lines a b = compare (parse_line a) (parse_line b) -- chunk : split up a string into chunks which all satisfy a certain -- property. The property is that f (first char in chunk) (char) is -- True. So you might use some sort of equality test for f. -- chunk :: (a -> a -> Bool) -> [a] -> [[a]] chunk _ [] = [] chunk f (x:xs) = (x : c) : chunk f rest where (c, rest) = span (f x) xs same_digitness a b = (da && db) || ((not da) && (not db)) where (da, db) = (isDigit a, isDigit b) -- toIS: parse a String and convert it to a list of IS. The string -- must be entirely digits (becomes a single IS), or entirely -- nondigits (each char becomes its own IS), and not empty. -- toIS :: String -> [Either Char Int] toIS s = let r = readDec s in if (length r == 0) then map Left s else if (length r == 1) then let (i, rest) = head r in if (length rest == 0) then [ Right i ] else error "trailing junk after digits" else error "too many results from readDec" parse_line :: String -> [Either Char Int] parse_line = concat . map toIS . chunk same_digitness