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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
||Text formatting program (DT)
||Reformats text to a specified width, with line-fill
%export just
||To try this out from within a Miranda session, say e.g.
|| just 60 (read "file")
||where "file" contains some text you want to reformat.
||You could also make it into a UNIX filter -- see the example `mrev'.
||----------------------------------------------------------------------||
|| in this program we move between three different representations of ||
|| text - as a flat list of characters, including spaces and newlines ||
|| - as a list of lines (containing spaces but not newlines) ||
|| - and as a list of list of words. ||
||----------------------------------------------------------------------||
text == [char]
line == [char]
word == [char]
just::num->text->text ||the main function
just n = concat.map(reformat n).paras.map words.lines
||lines::text->[line]
||lines is defined in <stdenv> - it breaks a string into lines,
||removing the newline characters
paras::[[word]]->[[word]]
||make each paragraph into one long line, by joining adjacent
||non-blank lines
paras (a:b:x) = paras ((a++b):x), if a~=[]~=b
= a:paras (b:x), otherwise
paras (a:[]) = a:[]
paras [] = []
reformat::num->[word]->text
||reformat a paragraph to width n
reformat n [] = "\n" ||the empty paragraph represents a blank line
reformat n x = lay(justify n (partition n x))
||lay::[line]->text
||lay is defined in <stdenv> - it is the inverse of lines
justify::num->[[word]]->[line]
justify n para = map(fill_line n)(init para)++[unwords(last para)]
partition::num->[word]->[[word]]
||break a paragraph into lines, with as many words as will fit in width
||n on each line (except the last)
partition n [] = []
partition n x = x1 : partition n rest
where
(x1,rest) = grab [] x
grab y (w:x) = grab (w:y) x, if sum(map(#)y)+#y+#w <= n
= (reverse y,w:x), otherwise
grab y [] = (reverse y,[])
fill_line :: num->[word]->line
||make words into a line of length n exactly, by inserting enough spaces
fill_line n words
= (concat.concat) (transpose [words,mkspaces (w-1) (n-sw)])
where
w = #words
sw = sum(map (#) words)
mkspaces :: num->num->[[char]]
||return s spaces broken into n groups
mkspaces n s = map f [1..n], if n mod 2=0 ||see note
= map f [n,n-1..1], otherwise
where
f i = rep (s div n + delta) ' '
where
delta = 1, if i<=s mod n
= 0, otherwise
||note: we put the extra spaces in sometimes from the left and sometimes
||from the right, depending on the parity of n. This is to avoid
||visually unbalancing the text by having all the extra spaces towards
||one margin. Using the parity of n to decide this is arbitrary.
words :: line->[word]
||break a line into words
words = filter (~=[]) . foldr (breakon ' ') [[]]
unwords :: [word]->line
||join words to make a line, inserting one space as separator
unwords = foldr1 (insert ' ')
insert :: *->[*]->[*]->[*]
insert a x y = x ++ [a] ++ y
breakon :: *->*->[[*]]->[[*]]
breakon c a x = []:x, if a=c
= (a:hd x):tl x, otherwise
||These definitions of `words' and `unwords' are due to Richard Bird, see
||Bird and Wadler (1988), page 91.
|