September 2017 Update

This commit is contained in:
Ingy döt Net 2017-09-23 10:01:46 +02:00
parent bba7bfd280
commit ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions

View file

@ -20,3 +20,4 @@ Note that:
# Output text will be viewed in a mono-spaced font on a plain text editor or basic terminal.
# The minimum space between columns should be computed from the text and not hard-coded.
# It is ''not'' a requirement to add separating characters between or around columns.
<br><br>

View file

@ -1,3 +1,5 @@
-- COLUMN ALIGNMENTS ---------------------------------------------------------
property pstrLines : ¬
"Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n" & ¬
"are$delineated$by$a$single$'dollar'$character,$write$a$program\n" & ¬
@ -10,29 +12,15 @@ property eLeft : -1
property eCenter : 0
property eRight : 1
on run
set lstCols to lineColumns("$", pstrLines)
script testAlignment
on lambda(eAlign)
columnsAligned(eAlign, lstCols)
end lambda
end script
intercalate(return & return, ¬
map(testAlignment, {eLeft, eRight, eCenter}))
end run
-- columnsAligned :: EnumValue -> [[String]] -> String
on columnsAligned(eAlign, lstCols)
-- padwords :: Int -> [String] -> [[String]]
script padwords
on lambda(n, lstWords)
on |λ|(n, lstWords)
-- pad :: String -> String
script pad
on lambda(str)
on |λ|(str)
set lngPad to n - (length of str)
if eAlign = my eCenter then
set lngHalf to lngPad div 2
@ -45,11 +33,11 @@ on columnsAligned(eAlign, lstCols)
{replicate(lngPad, space), str, ""}
end if
end if
end lambda
end |λ|
end script
map(pad, lstWords)
end lambda
end |λ|
end script
unlines(map(my unwords, ¬
@ -61,9 +49,9 @@ end columnsAligned
on lineColumns(strColDelim, strText)
-- _words :: Text -> [Text]
script _words
on lambda(str)
on |λ|(str)
splitOn(strColDelim, str)
end lambda
end |λ|
end script
set lstRows to map(_words, splitOn(linefeed, pstrLines))
@ -71,9 +59,9 @@ on lineColumns(strColDelim, strText)
-- fullRow :: [[a]] -> [[a]]
script fullRow
on lambda(lst)
on |λ|(lst)
lst & replicate(nCols - (length of lst), {""})
end lambda
end |λ|
end script
transpose(map(fullRow, lstRows))
@ -81,19 +69,56 @@ end lineColumns
-- widest [a] -> Int
on widest(xs)
script maxLen
on lambda(a, x)
set lng to length of x
cond(lng > a, lng, a)
end lambda
end script
foldl(maxLen, 0, xs)
|length|(maximumBy(comparing(my |length|), xs))
end widest
-- TEST ----------------------------------------------------------------------
on run
set lstCols to lineColumns("$", pstrLines)
script testAlignment
on |λ|(eAlign)
columnsAligned(eAlign, lstCols)
end |λ|
end script
-- GENERIC LIBRARY FUNCTIONS
intercalate(return & return, ¬
map(testAlignment, {eLeft, eRight, eCenter}))
end run
-- GENERIC FUNCTIONS ---------------------------------------------------------
-- comparing :: (a -> b) -> (a -> a -> Ordering)
on comparing(f)
set mf to mReturn(f)
script
on |λ|(a, b)
set x to mf's |λ|(a)
set y to mf's |λ|(b)
if x < y then
-1
else
if x > y then
1
else
0
end if
end if
end |λ|
end script
end comparing
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to |λ|(v, item i of xs, i, xs)
end repeat
return v
end tell
end foldl
-- Text -> [Text] -> Text
on intercalate(strText, lstText)
@ -103,52 +128,22 @@ on intercalate(strText, lstText)
return strJoined
end intercalate
-- Text -> Text -> [Text]
on splitOn(strDelim, strMain)
set {dlm, my text item delimiters} to {my text item delimiters, strDelim}
set lstParts to text items of strMain
set my text item delimiters to dlm
return lstParts
end splitOn
-- length :: [a] -> Int
on |length|(xs)
length of xs
end |length|
-- [Text] -> Text
on unlines(lstLines)
intercalate(linefeed, lstLines)
end unlines
-- [Text] -> Text
on unwords(lstWords)
intercalate(" ", lstWords)
end unwords
-- transpose :: [[a]] -> [[a]]
on transpose(xss)
script column
on lambda(_, iCol)
script row
on lambda(xs)
item iCol of xs
end lambda
end script
map(row, xss)
end lambda
end script
map(column, item 1 of xss)
end transpose
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to lambda(v, item i of xs, i, xs)
end repeat
return v
end tell
end foldl
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
@ -156,34 +151,36 @@ on map(f, xs)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to lambda(item i of xs, i, xs)
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
on zipWith(f, xs, ys)
set lng to length of xs
if lng is not length of ys then return missing value
-- maximumBy :: (a -> a -> Ordering) -> [a] -> a
on maximumBy(f, xs)
set cmp to mReturn(f)
script max
on |λ|(a, b)
if a is missing value or cmp's |λ|(a, b) < 0 then
b
else
a
end if
end |λ|
end script
tell mReturn(f)
set lst to {}
repeat with i from 1 to lng
set end of lst to lambda(item i of xs, item i of ys)
end repeat
return lst
end tell
end zipWith
foldl(max, missing value, xs)
end maximumBy
-- cond :: Bool -> a -> a -> a
on cond(bool, f, g)
if bool then
f
-- min :: Ord a => a -> a -> a
on min(x, y)
if y < x then
y
else
g
x
end if
end cond
end min
-- Egyptian multiplication - progressively doubling a list, appending
-- stages of doubling to an accumulator where needed for binary
@ -191,7 +188,11 @@ end cond
-- replicate :: Int -> a -> [a]
on replicate(n, a)
set out to cond(class of a is string, "", {})
if class of a is string then
set out to ""
else
set out to {}
end if
if n < 1 then return out
set dbl to a
@ -203,14 +204,49 @@ on replicate(n, a)
return out & dbl
end replicate
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
if class of f is script then
f
else
script
property lambda : f
end script
end if
end mReturn
-- Text -> Text -> [Text]
on splitOn(strDelim, strMain)
set {dlm, my text item delimiters} to {my text item delimiters, strDelim}
set lstParts to text items of strMain
set my text item delimiters to dlm
return lstParts
end splitOn
-- transpose :: [[a]] -> [[a]]
on transpose(xss)
script column
on |λ|(_, iCol)
script row
on |λ|(xs)
item iCol of xs
end |λ|
end script
map(row, xss)
end |λ|
end script
map(column, item 1 of xss)
end transpose
-- [Text] -> Text
on unlines(lstLines)
intercalate(linefeed, lstLines)
end unlines
-- [Text] -> Text
on unwords(lstWords)
intercalate(" ", lstWords)
end unwords
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
on zipWith(f, xs, ys)
set lng to min(length of xs, length of ys)
set lst to {}
tell mReturn(f)
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, item i of ys)
end repeat
return lst
end tell
end zipWith

View file

@ -0,0 +1,66 @@
10 rem ********************************
20 rem print words in columns
30 rem commodore basic 2.0
40 rem ********************************
50 print chr$(14) : rem change to upper/lower case set
60 gosub 140 : rem find length of longest word
70 algn$ = "left"
80 gosub 260 : rem print aligned text
90 algn$ = "center"
100 gosub 260
110 algn$ = "right"
120 gosub 260
130 end
140 rem *** find length of longest word
150 mx=0
160 for i=1 to 6
170 read a$
180 n=1
190 for j=1 to len(a$)
200 if mid$(a$,j,1)<>"$" then n=n+1: goto 230
210 if mx<n then mx=n
220 n=1
230 next
240 next
250 return
260 rem print aligned text
270 restore : rem reset data read pointer
280 s$ = " "
290 print : print algn$;"-aligned"
300 c=1 : rem column counter
310 for i=1 to 6
320 read a$
330 n=1
340 for j=1 to len(a$)
350 if mid$(a$,j,1)<>"$" then n=n+1 : goto 380
360 gosub 440 : rem print word
370 n=1
380 next
390 if n>1 then gosub 440
400 next
410 print
420 return
430 rem ********* print word **********
440 b$ = mid$(a$,j-n+1,n-1)
450 b = len(b$)
460 if algn$ = "center" then 520
470 if algn$ = "right" then 570
480 if c+b<40 and c+mx>40 then print b$: c=1: return
490 if c+mx>40 then print : c=1
500 print b$;left$(s$,mx-b);: c=c+mx
510 return
520 if c+mx>40 then print : c=1
530 bb=(mx-b)/2 : ba=bb
540 if bb>1 and int(bb)=bb then ba=bb-1
550 print left$(s$,ba);b$;left$(s$,bb);: c=c+mx
560 return
570 if c+mx>40 then print : c=1
580 print left$(s$,mx-b);b$;: c=c+mx
590 return
600 rem *********** the words *********
610 data "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
620 data "are$delineated$by$a$single$'dollar'$character,$write$a$program"
630 data "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
640 data "column$are$separated$by$at$least$one$space."
650 data "Further,$allow$for$each$word$in$a$column$to$be$either$left$"
660 data "justified,$right$justified,$or$center$justified$within$its$column"

View file

@ -1,7 +1,7 @@
defmodule Align do
def columns(text, alignment) do
fieldsbyrow = String.split(text, "\n", trim: true)
|> Enum.map(fn line -> String.split(line, "$", trim: true) end)
|> Enum.map(fn row -> String.split(row, "$", trim: true) end)
maxfields = Enum.map(fieldsbyrow, fn field -> length(field) end) |> Enum.max
colwidths = Enum.map(fieldsbyrow, fn field -> field ++ List.duplicate("", maxfields - length(field)) end)
|> List.zip
@ -15,9 +15,9 @@ defmodule Align do
end)
end
defp adjust(field, width, :Left), do: String.ljust(field, width)
defp adjust(field, width, :Right), do: String.rjust(field, width)
defp adjust(field, width, _), do: :string.centre(String.to_char_list(field), width)
defp adjust(field, width, :Left), do: String.pad_trailing(field, width)
defp adjust(field, width, :Right), do: String.pad_leading(field, width)
defp adjust(field, width, _), do: :string.centre(String.to_charlist(field), width)
end
text = """

View file

@ -0,0 +1,44 @@
Public Sub Main() 'Written in Gambas 3.9.2 as a Command line Application - 15/03/2017
Dim siCount, siCounter, siLength As Short 'Counters
Dim siLongest As Short = -1 'To store the longest 'Word'
Dim sLine, sRows As New String[] 'Arrays
Dim sTemp, sAlign As String 'Temp strings
Dim sInput As String = "Given$a$text$file$of$many$lines, $where$fields$within$a$line$" & "\n"
"are$delineated$by$a$single$ 'dollar'$character,$write$a$program" & "\n"
"that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$" & "\n"
"column$are$separated$by$at$least$one$space." & "\n"
"Further, $allow$for$each$word$in$a$column$to$be$either$left$" & "\n"
"justified, $right$justified, $or$center$justified$within$its$column." 'Main string (with End Of Line characters added)
For Each sTemp In Split(sInput, "\n") 'For each Line (split by End of Line character)..
sLine.add(sTemp) 'Add the Line to sLine array
Next
For siCount = 0 To sLine.Max 'For each of Lines in the array..
For Each sTemp In Split(sLine[siCount], "$") 'For each 'Word' in the Line (Split by the '$')
siLength = Len(sTemp) 'Store the length of the current 'Word'
If siLength > siLongest Then siLongest = siLength 'Make sure siLength has the length of the longest 'Word'
sRows.add(Trim(sTemp)) 'Create an array of the 'Words'
Next
sRows.add("\n") 'Add a End Of Line character to the sRows array
Next
For siCounter = 0 To 2 'For each alignment (Left, Right and Centre)
For Each sTemp In sRows 'For each 'Word' in the sRows array..
If sTemp = "\n" Then 'If it's a End Of Line character then..
Print 'Print
Continue 'Jump to the next iteration of the For Next Loop
Endif
If siCounter = 0 Then Print sTemp & Space(siLongest - Len(sTemp)); 'Print control for Left align
If siCounter = 1 Then Print Space(siLongest - Len(sTemp)) & sTemp; 'Print control for Right align
If siCounter = 2 Then 'Print control for Centre align
siCount = (siLongest - Len(sTemp)) / 2 'Difference between the length of the longest 'Word' and the current 'Word' / 2
sAlign = Space(siCount) & sTemp & Space(siCount) 'Put the string together for printing
If Len(sAlign) < siLongest Then sAlign &= " " 'Check it's the correct length if not add a space on the end
Print sAlign; 'Print the 'Word'
Endif
Next
Print 'Print an empty line between each alignment list
Next
End

View file

@ -0,0 +1,26 @@
import Data.List (unfoldr, transpose)
import Control.Arrow (second)
dat =
"Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n" ++
"are$delineated$by$a$single$'dollar'$character,$write$a$program\n" ++
"that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$\n" ++
"column$are$separated$by$at$least$one$space.\n" ++
"Further,$allow$for$each$word$in$a$column$to$be$either$left$\n" ++
"justified,$right$justified,$or$center$justified$within$its$column.\n"
brkdwn =
takeWhile (not . null) . unfoldr (Just . second (drop 1) . span ('$' /=))
format j ls = map (unwords . zipWith align colw) rows
where
rows = map brkdwn $ lines ls
colw = map (maximum . map length) . transpose $ rows
align cw w =
case j of
'c' -> replicate l ' ' ++ w ++ replicate r ' '
'r' -> replicate dl ' ' ++ w
'l' -> w ++ replicate dl ' '
where
dl = cw - length w
(l, r) = (dl `div` 2, dl - l)

View file

@ -0,0 +1,35 @@
import Prelude as P
import Data.Text as T
(Text, pack, unpack, splitOn, unlines, unwords, length,
justifyLeft, justifyRight, center)
import Data.List (transpose, zip, maximumBy)
import Data.Ord (comparing)
rows :: [[Text]]
rows =
(splitOn (pack "$") . pack) <$>
[ "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
, "are$delineated$by$a$single$'dollar'$character,$write$a$program"
, "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
, "column$are$separated$by$at$least$one$space."
, "Further,$allow$for$each$word$in$a$column$to$be$either$left$"
, "justified,$right$justified,$or$center$justified$within$its$column."
]
cols :: [[Text]]
cols =
transpose $
((++) <*>
(flip P.replicate (pack []) .
(-) (maximum (P.length <$> rows)) . P.length)) <$>
rows
main :: IO ()
main =
mapM_ putStrLn $
[ (\cols f ->
(unpack . T.unlines) $
T.unwords <$> transpose ((\(xs, n) -> f (n + 1) ' ' <$> xs) <$> cols))
(zip cols ((T.length . maximumBy (comparing T.length)) <$> cols))
] <*>
[justifyLeft, justifyRight, center]

View file

@ -1,25 +0,0 @@
import Data.List
import Control.Monad
import Control.Arrow
dat = "Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n" ++
"are$delineated$by$a$single$'dollar'$character,$write$a$program\n" ++
"that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$\n" ++
"column$are$separated$by$at$least$one$space.\n" ++
"Further,$allow$for$each$word$in$a$column$to$be$either$left$\n" ++
"justified,$right$justified,$or$center$justified$within$its$column.\n"
brkdwn = takeWhile (not.null) . unfoldr (Just . second (drop 1) . span ('$'/=))
format j ls = map (unwords. zipWith align colw) rows
where
rows = map brkdwn $ lines ls
colw = map (maximum. map length) . transpose $ rows
align cw w =
case j of
'c' -> (replicate l ' ') ++ w ++ (replicate r ' ')
'r' -> (replicate dl ' ') ++ w
'l' -> w ++ (replicate dl ' ')
where
dl = cw-length w
(l,r) = (dl `div` 2, dl-l)

View file

@ -1,10 +1,8 @@
package column_alignment
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
enum class Align_function {
enum class AlignFunction {
LEFT { override fun invoke(s: String, l: Int) = ("%-" + l + 's').format(("%" + s.length + 's').format(s)) },
RIGHT { override fun invoke(s: String, l: Int) = ("%-" + l + 's').format(("%" + l + 's').format(s)) },
CENTER { override fun invoke(s: String, l: Int) = ("%-" + l + 's').format(("%" + ((l + s.length) / 2) + 's').format(s)) };
@ -16,8 +14,8 @@ enum class Align_function {
* @constructor Initializes columns aligner from lines in a list of strings.
* @property lines Lines in a single string. Empty string does form a column.
*/
class Column_aligner(val lines: List<String>) {
operator fun invoke(a: Align_function) : String {
class ColumnAligner(val lines: List<String>) {
operator fun invoke(a: AlignFunction) : String {
var result = ""
for (lineWords in words) {
for (i in lineWords.indices) {
@ -38,26 +36,28 @@ class Column_aligner(val lines: List<String>) {
lines.forEach {
val lineWords = java.lang.String(it).split("\\$")
words += lineWords
for (i in lineWords.indices)
if (i >= column_widths.size)
for (i in lineWords.indices) {
if (i >= column_widths.size) {
column_widths += lineWords[i].length
else
} else {
column_widths[i] = Math.max(column_widths[i], lineWords[i].length)
}
}
}
}
}
fun main(args: Array<String>) {
if (args.size < 1)
if (args.isEmpty()) {
println("Usage: ColumnAligner file [L|R|C]")
else {
val ca = Column_aligner(Files.readAllLines(Paths.get(args[0]), StandardCharsets.UTF_8))
val alignment = if (args.size >= 2) args[1] else "L"
when (alignment) {
"L" -> print(ca(Align_function.LEFT))
"R" -> print(ca(Align_function.RIGHT))
"C" -> print(ca(Align_function.CENTER))
else -> System.err.println("Error! Unknown alignment: " + alignment)
}
return
}
val ca = ColumnAligner(Files.readAllLines(Paths.get(args[0]), StandardCharsets.UTF_8))
val alignment = if (args.size >= 2) args[1] else "L"
when (alignment) {
"L" -> print(ca(AlignFunction.LEFT))
"R" -> print(ca(AlignFunction.RIGHT))
"C" -> print(ca(AlignFunction.CENTER))
else -> System.err.println("Error! Unknown alignment: " + alignment)
}
}

View file

@ -1,4 +1,6 @@
import strutils, sequtils, strfmt
from strutils import splitLines, split
from sequtils import mapIt
from strfmt import format, write
let textinfile = """Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
@ -7,16 +9,17 @@ column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column."""
var words = textinfile.splitLines.mapIt(seq[string], it.split '$')
var maxs = newSeq[int](max words.mapIt(int, it.len))
var words = textinfile.splitLines.mapIt(it.split '$')
var maxs = newSeq[int](max words.mapIt(it.len))
for l in words:
for j,w in l:
for line in words:
for j,w in line:
maxs[j] = max(maxs[j], w.len+1)
for i, align in ["<",">","^"]:
echo(["Left", "Right", "Center"][i], " column-aligned output:")
for l in words:
for j,w in l:
stdout.write w.format align & $maxs[j]
for line in words:
for j,w in line:
stdout.write(w.format align & $maxs[j])
stdout.write "\n"
stdout.write "\n"

View file

@ -0,0 +1,49 @@
# Task: Align columns
#
# Uses `Text::justify` from the standard library.
module align_columns
fun aligner(text: String, left: Float)
do
# Each row is a sequence of fields
var rows = new Array[Array[String]]
for line in text.split('\n') do
rows.add line.split("$")
end
# Compute the final length of each column
var lengths = new Array[Int]
for fields in rows do
var i = 0
for field in fields do
var fl = field.length
if lengths.length <= i or fl > lengths[i] then
lengths[i] = fl
end
i += 1
end
end
# Process each line and align each field
for fields in rows do
var line = new Array[String]
var i = 0
for field in fields do
line.add field.justify(lengths[i], left)
i += 1
end
print line.join(" ")
end
end
var text = """
Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column."""
aligner(text, 0.0)
aligner(text, 1.0)
aligner(text, 0.5)

View file

@ -0,0 +1,74 @@
text = .array~of("Given$a$text$file$of$many$lines,$where$fields$within$a$line$", -
"are$delineated$by$a$single$'dollar'$character,$write$a$program", -
"that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$", -
"column$are$separated$by$at$least$one$space.", -
"Further,$allow$for$each$word$in$a$column$to$be$either$left$", -
"justified,$right$justified,$or$center$justified$within$its$column.")
columns = 0
parsedText = .array~new
-- split each line of text into words and figure out how many columns we need
loop line over text
parsedLine = line~makearray("$")
parsedText~append(parsedLine)
columns = max(columns, parsedLine~items)
end
-- now figure out how wide we need to make each column
columnWidths = .array~new(columns)
linelength = 0
loop i = 1 to columns
width = 0
loop line over parsedText
word = line[i]
if word \= .nil then width = max(width, word~length)
end
columnWidths[i] = width
-- keep track of the total width, including space for a separator
linelength += width + 1
end
say "align left:"
say
out = .mutableBuffer~new(linelength)
loop line over parsedText
-- mutable buffers are more efficient than repeated string concats
-- reset the working buffer to zero
out~setbuffersize(0)
loop col = 1 to line~items
word = line[col]
if word == .nil then word = ''
out~append(word~left(columnwidths[col] + 1))
end
say out~string
end
say
say "align right:"
say
loop line over parsedText
-- mutable buffers are more efficient than repeated string concats
-- reset the working buffer to zero
out~setbuffersize(0)
loop col = 1 to line~items
word = line[col]
if word == .nil then word = ''
out~append(word~right(columnwidths[col] + 1))
end
say out~string
end
say
say "align center:"
say
loop line over parsedText
-- mutable buffers are more efficient than repeated string concats
-- reset the working buffer to zero
out~setbuffersize(0)
loop col = 1 to line~items
word = line[col]
if word == .nil then word = ''
out~append(word~center(columnwidths[col] + 1))
end
say out~string
end

View file

@ -1,34 +1,32 @@
/*REXX program displays various alignments for words in a text string.*/
cols=0; size=0; wid.=0; t.=; @.= /*zero|nullify some variables*/
/* [↓] some "text" lines. */
/*REXX program displays various alignments for words in an array of text strings. */
cols=0; size=0; wid.=0; t.=; @.= /*zero or nullify some variables. */
/* [↓] some "text" lines. */
t.1 = "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
t.2 = "are$delineated$by$a$single$'dollar'$character,$write$a$program"
t.3 = "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
t.4 = "column$are$separated$by$at$least$one$space."
t.5 = "Further,$allow$for$each$word$in$a$column$to$be$either$left$"
t.6 = "justified,$right$justified,$or$center$justified$within$its$column."
/* [↑] a null line is the end*/
do r=1 while t.r\=='' /* [↓] process all text lines*/
_=strip(t.r,,'$') /*strip leading & trailing $.*/
do c=1 until _=='' /* [↓] process each word. */
parse var _ @.r.c '$' _
wid.c=max(wid.c, length(@.r.c)) /*max word width.*/
/* [↑] a null line is the end of text.*/
do r=1 while t.r\=='' /* [↓] process all the text lines. */
_=strip(t.r,,'$') /*strip leading & trailing dollar signs*/
do c=1 until _=='' /* [↓] process each of the words. */
parse var _ @.r.c '$' _
wid.c=max(wid.c, length(@.r.c)) /*find the maximum word width. */
end /*c*/
cols=max(cols,c) /*use the maximum COLS found.*/
cols=max(cols,c) /*use the maximum COLS found. */
end /*r*/
do k=1 for cols; size=size+wid.k; end /*find width of biggest line.*/
rows=r-1 /*adjust ROWS because of DO.*/
do j=1 for 3; say; say /*show 2 blank lines for sep.*/
say center(word('left right center', j) "aligned", size+cols-1, "")
do r=1 for rows; _= /*build row by row*/
do c=1 for cols; x=@.r.c /* " col " col*/
if j==1 then _=_ left(x, wid.c) /*justified left.*/
if j==2 then _=_ right(x, wid.c) /* " right.*/
if j==3 then _=_ centre(x, wid.c) /* " center.*/
do k=1 for cols; size=size+wid.k; end /*find the width of the biggest line. */
rows=r-1 /*adjust ROWS because of the DO loop.*/
do j=1 for 3; say; say /*show two blank lines for a separator.*/
say center(word('left right center', j) "aligned", size+cols-1, "") /*show title*/
do r=1 for rows; _= /*construct row by row. */
do c=1 for cols; x=@.r.c /* " col " col. */
if j==1 then _=_ left(x, wid.c) /*justified left. */
if j==2 then _=_ right(x, wid.c) /* " right. */
if j==3 then _=_ centre(x, wid.c) /* " center. */
end /*c*/
say substr(_,2) /*ignore the leading extra blank.*/
say substr(_, 2) /*ignore the leading extra blank. */
end /*r*/
end /*j*/
/*stick a fork in it, we're done.*/
end /*j*/ /*stick a fork in it, we're all done. */

View file

@ -1,40 +1,39 @@
/*REXX program displays various alignments for words in a text string.*/
cols=0; size=0; wid.=0; t.=; @.= /*zero|nullify some variables*/
/* [↓] some "text" lines. */
/*REXX pgm displays various (boxed) alignments for words in an array of text strings. */
cols=0; size=0; wid.=0; t.=; @.= /*zero or nullify some variables. */
/* [↓] some "text" lines. */
t.1 = "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
t.2 = "are$delineated$by$a$single$'dollar'$character,$write$a$program"
t.3 = "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
t.4 = "column$are$separated$by$at$least$one$space."
t.5 = "Further,$allow$for$each$word$in$a$column$to$be$either$left$"
t.6 = "justified,$right$justified,$or$center$justified$within$its$column."
/* [↑] a null line is the end*/
do r=1 while t.r\=='' /* [↓] process all text lines*/
_=strip(t.r,,'$') /*strip leading & trailing $.*/
do c=1 until _=='' /* [↓] process each word. */
parse var _ @.r.c '$' _
wid.c=max(wid.c, length(@.r.c)) /*max word width.*/
/* [↑] a null line is the end of text.*/
do r=1 while t.r\=='' /* [↓] process all the text lines. */
_=strip(t.r,,'$') /*strip leading & trailing dollar signs*/
do c=1 until _=='' /* [↓] process each of the words. */
parse var _ @.r.c '$' _
wid.c=max(wid.c, length(@.r.c)) /*find the maximum word width. */
end /*c*/
cols=max(cols,c) /*use the maximum COLS found.*/
cols=max(cols,c) /*use the maximum COLS found. */
end /*r*/
do k=1 for cols; size=size+wid.k; end /*find width of biggest line.*/
rows=r-1 /*adjust ROWS because of DO.*/
do j=1 for 3; say; say /*show 2 blank lines for sep.*/
say center(word('left right center', j) "aligned", size+cols+1, "")
do k=1 for cols; size=size+wid.k; end /*find the width of the biggest line. */
rows=r-1 /*adjust ROWS because of the DO loop.*/
do j=1 for 3; say; say /*show two blank lines for a separator.*/
say center(word('left right center', j) "aligned", size+cols+1, "") /*show title*/
do r=0 to rows; _=; !=''; if r==0 then !=''
do c=1 for cols; x=@.r.c
if r==0 then x=copies("",wid.c+1)
if j==1 then _=_ || ! || left(x,wid.c)
if j==2 then _=_ || ! || right(x,wid.c)
if j==3 then _=_ || ! || centre(x,wid.c)
do r=0 to rows; _=; !=''; if r==0 then !=''
do c=1 for cols; x=@.r.c
if r==0 then x=copies("", wid.c +1)
if j==1 then _=_ || ! || left(x, wid.c)
if j==2 then _=_ || ! || right(x, wid.c)
if j==3 then _=_ || ! || centre(x, wid.c)
end /*c*/
if r==0 then do; _= ''substr(_,2,length(_)-1)""
bot= ''substr(_,2,length(_)-2)""
end
else _=_ || !
if r==0 then do; _= ''substr(_, 2, length(_) -1)""
bot= ''substr(_, 2, length(_) -2)""
end
else _=_ || !
say _
end /*r*/
/* [↑] shows words in boxes.*/
say translate(bot,'',"")
end /*j*/
end /*r*/ /* [↑] shows words in boxes. */
say translate(bot, '', "")
end /*j*/ /*stick a fork in it, we're all done. */

View file

@ -0,0 +1,53 @@
(import (scheme base)
(scheme write)
(srfi 1)
(except (srfi 13) string-for-each string-map)
(srfi 14))
;; text is a list of lines, alignment is left/right/center
;; displays the aligned text in columns with a single space gap
(define (align-columns text alignment)
(define (split line) ; splits string on $ into list of strings
(string-tokenize line (char-set-complement (->char-set "$"))))
(define (extend lst n) ; extends list to length n, by adding "" to end
(append lst (make-list (- n (length lst)) "")))
(define (align-word word width) ; align single word to fit width
(case alignment
((left) (string-pad-right word width))
((right) (string-pad word width))
((center) (let ((rem (- width (string-length word))))
(string-pad-right (string-pad word (- width (truncate (/ rem 2))))
width)))))
;
(display alignment) (newline)
(let* ((text-list (map split text))
(max-line-len (fold (lambda (text val) (max (length text) val)) 0 text-list))
(text-lines (map (lambda (line) (extend line max-line-len)) text-list))
(min-col-widths (map (lambda (col)
(fold (lambda (line val)
(max (string-length (list-ref line col))
val))
0
text-lines))
(iota max-line-len))))
(map (lambda (line)
(map (lambda (word width)
(display (string-append (align-word word width)
" ")))
line min-col-widths)
(newline))
text-lines))
(newline))
;; show example
(define *example*
'("Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
"are$delineated$by$a$single$'dollar'$character,$write$a$program"
"that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
"column$are$separated$by$at$least$one$space."
"Further,$allow$for$each$word$in$a$column$to$be$either$left$"
"justified,$right$justified,$or$center$justified$within$its$column."))
(align-columns *example* 'left)
(align-columns *example* 'center)
(align-columns *example* 'right)

View file

@ -0,0 +1,75 @@
#!/bin/sed -nrf
# Format: <master-pattern>\n<line1>\n<line1-as-pattern>\n<line2>\n<line2-as-pattern>...
# After reading whole file <master-pattern> contains max number of fields of max width each.
# If no $ at start or end of a line -- add them
/^\$/! s/^/$/
/\$$/! s/$/$/
# First line saved as three lines in hold space:
# <line1-as-pattern>\n<line1>\n<line1-as-pattern>
1{
h
s/[^$]/ /g
H
G
x
# Restart -- go to next line
b
}
# For lines 2,3,...
H
# Current line -> pattern
# (each character replaced by constant symbol (e.g. space) so that we can count them)
s/[^$]/ /g
H
G
# Add two markers
s/\$/1$/
s/(\n[^$]*)\$/\12$/
# Compare patterns
:cmp
s/(1\$([^$\n]*)([^$\n]*)[^2]*2\$\2)/\1\3/
/1\$\n/ bout
# Advance markers
s/1(\$[^12$\n]*)/\11/
s/2(\$[^12$\n]*)/\12/
# Add one more field
/^[^2]*2\$\n/{ s/^([^2]*)2\$\n/\12$$\n/; }
bcmp
:out
# Remove first line
s/[^\n]*\n//
# Remove 2$-marker
s/2\$/$/
x
${
# We are on the last line -- start printing
x;
# Add a line for aligned string
s/^/\n/
:nextline
# Add marker again (only one this time)
s/\$/1$/
:align
# 1. look up missing spaces,
# 2. put first word of 2nd line before first newline adding missing spaces
# 3. cut first word of 2nd and 3rd lines.
# Replace \5\3 by \3\5 for RIGHT ALIGNMENT
s/(\n[^\n]*)1\$([^$\n]*)([^$\n]*)\$([^\n]*\n)\$([^$\n]*)([^\n]*\n)\$\2\$/\5\3 \1$\2\31$\4\6$/
talign
# We ate 2nd and 3rd lines completely, except newlines -- remove them
s/\$\n\$\n\$\n/$\n/
# Print the first line in pattern space
P
# ... and remove it
s/^[^\n]*//
# Remove marker
s/1\$/$/
# If no more lines -- exit
/\$\n\$$/q
bnextline
}

View file

@ -0,0 +1,16 @@
fcn format(text,how){
words:=text.split("$").apply("split").flatten();
max:=words.reduce(fcn(p,n){ n=n.len(); n>p and n or p },0);
wordsPerCol:=80/(max+1);
fmt:=(switch(how){
case(-1){ "%%-%ds ".fmt(max).fmt }
case(0) { fcn(max,w){
a:=(max-w.len())/2; b:=max-w.len() - a; String(" "*a,w," "*b);
}.fp(max)
}
case(1){ "%%%ds ".fmt(max).fmt }
});
w:=words.walker(); d:=Data(0,Int);
do{ w.pump(wordsPerCol,d,fmt).append("\n") } while(not w.atEnd);
d.text;
}

View file

@ -0,0 +1,11 @@
text:=
"Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n"
"are$delineated$by$a$single$'dollar'$character,$write$a$program\n"
"that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$\n"
"column$are$separated$by$at$least$one$space.\n"
"Further,$allow$for$each$word$in$a$column$to$be$either$left$\n"
"justified,$right$justified,$or$center$justified$within$its$column.\n";
format(text,-1).print();
format(text, 0).print();
format(text, 1).print();