2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -1,34 +1,41 @@
# syntax: GAWK -f ALIGN_COLUMNS.AWK ALIGN_COLUMNS.TXT
BEGIN {
FS="$"
lcounter = 1
maxfield = 0
# justification; pick one
#justify = "left"
justify = "center"
#justify = "right"
colsep = " " # separator between columns
report("raw data")
}
{
if ( NF > maxfield ) maxfield = NF;
for(i=1; i <= NF; i++) {
line[lcounter,i] = $i
if ( longest[i] == "" ) longest[i] = 0;
if ( length($i) > longest[i] ) longest[i] = length($i);
}
lcounter++
{ printf("%s\n",$0)
arr[NR] = $0
n = split($0,tmp_arr,"$")
for (j=1; j<=n; j++) {
width = max(width,length(tmp_arr[j]))
}
}
END {
just = (justify == "left") ? "-" : ""
for(i=1; i <= NR; i++) {
for(j=1; j <= maxfield; j++) {
if ( justify != "center" ) {
template = "%" just longest[j] "s "
} else {
v = int((longest[j] - length(line[i,j]))/2)
rt = "%" v+1 "s%%-%ds"
template = sprintf(rt, "", longest[j] - v)
}
printf(template, line[i,j])
}
print ""
}
report("left justified")
report("right justified")
report("center justified")
exit(0)
}
function report(text, diff,i,j,l,n,r,tmp_arr) {
printf("\nreport: %s\n",text)
for (i=1; i<=NR; i++) {
n = split(arr[i],tmp_arr,"$")
if (tmp_arr[n] == "") { n-- }
for (j=1; j<=n; j++) {
if (text ~ /^[Ll]/) { # left
printf("%-*s%s",width,tmp_arr[j],colsep)
}
else if (text ~ /^[Rr]/) { # right
printf("%*s%s",width,tmp_arr[j],colsep)
}
else if (text ~ /^[Cc]/) { # center
diff = width - length(tmp_arr[j])
l = r = int(diff / 2)
if (diff != l + r) { r++ }
printf("%*s%s%*s%s",l,"",tmp_arr[j],r,"",colsep)
}
}
printf("\n")
}
}
function max(x,y) { return((x > y) ? x : y) }

View file

@ -0,0 +1,55 @@
data b;
file f;
list c, r, s;
integer a, i, j, k, m, w;
b_cast(b, "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.");
f_b_affix(f, b);
m = 0;
while (f_news(f, r, 0, 0, "$") ^ -1) {
l_append(c, r);
m = max(m, l_length(r));
}
i = 0;
while (i < m) {
w = 0;
j = 0;
while (j < l_length(c)) {
r = c[j];
if (i < l_length(r)) {
w = max(w, length(r[i]));
}
j += 1;
}
l_append(s, w + 1);
i += 1;
}
k = 3;
while (k) {
k -= 1;
o_plan(l_effect("right", "center", "left")[k], " justified", "\n");
j = 0;
while (j < l_length(c)) {
i = 0;
r = c[j];
while (i < l_length(r)) {
w = s[i];
m = w - length(r[i]);
o_form("/w~3/~/w~1/", a = k * m >> 1, "", m - a, "", r[i]);
i += 1;
}
o_newline();
j += 1;
}
o_newline();
}

View file

@ -0,0 +1,216 @@
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" & ¬
"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."
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)
-- pad :: String -> String
script pad
on lambda(str)
set lngPad to n - (length of str)
if eAlign = my eCenter then
set lngHalf to lngPad div 2
{replicate(lngHalf, space), str, ¬
replicate(lngPad - lngHalf, space)}
else
if eAlign = my eLeft then
{"", str, replicate(lngPad, space)}
else
{replicate(lngPad, space), str, ""}
end if
end if
end lambda
end script
map(pad, lstWords)
end lambda
end script
unlines(map(my unwords, ¬
transpose(zipWith(padwords, ¬
map(my widest, lstCols), lstCols))))
end columnsAligned
-- lineColumns :: String -> String -> String
on lineColumns(strColDelim, strText)
-- _words :: Text -> [Text]
script _words
on lambda(str)
splitOn(strColDelim, str)
end lambda
end script
set lstRows to map(_words, splitOn(linefeed, pstrLines))
set nCols to widest(lstRows)
-- fullRow :: [[a]] -> [[a]]
script fullRow
on lambda(lst)
lst & replicate(nCols - (length of lst), {""})
end lambda
end script
transpose(map(fullRow, lstRows))
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)
end widest
-- GENERIC LIBRARY FUNCTIONS
-- Text -> [Text] -> Text
on intercalate(strText, lstText)
set {dlm, my text item delimiters} to {my text item delimiters, strText}
set strJoined to lstText as text
set my text item delimiters to dlm
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
-- [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
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
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)
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
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
-- cond :: Bool -> a -> a -> a
on cond(bool, f, g)
if bool then
f
else
g
end if
end cond
-- Egyptian multiplication - progressively doubling a list, appending
-- stages of doubling to an accumulator where needed for binary
-- assembly of a target length
-- replicate :: Int -> a -> [a]
on replicate(n, a)
set out to cond(class of a is string, "", {})
if n < 1 then return out
set dbl to a
repeat while (n > 1)
if (n mod 2) > 0 then set out to out & dbl
set n to (n div 2)
set dbl to (dbl & dbl)
end repeat
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

View file

@ -0,0 +1,84 @@
@echo off
setlocal enabledelayedexpansion
mode con cols=103
echo Given$a$text$file$of$many$lines,$where$fields$within$a$line$ >file.txt
echo are$delineated$by$a$single$'dollar'$character,$write$a$program! >>file.txt
echo that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$>>file.txt
echo column$are$separated$by$at$least$one$space.>>file.txt
echo Further,$allow$for$each$word$in$a$column$to$be$either$left$>>file.txt
echo justified,$right$justified,$or$center$justified$within$its$column.>>file.txt
for /f "tokens=1-13 delims=$" %%a in ('type file.txt') do (
call:maxlen %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m )
echo.
for /f "tokens=1-13 delims=$" %%a in ('type file.txt') do (
call:align 1 %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m )
echo.
for /f "tokens=1-13 delims=$" %%a in ('type file.txt') do (
call:align 2 %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m )
echo.
for /f "tokens=1-13 delims=$" %%a in ('type file.txt') do (
call:align 3 %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m )
exit /B
:maxlen &::sets variables len1 to len13
set "cnt=1"
:loop1
if "%1"=="" exit /b
call:strlen %1 length
if !len%cnt%! lss !length! set len%cnt%=!length!
set /a cnt+=1
shift
goto loop1
:align
setlocal
set cnt=1
set print=
:loop2
if "%2"=="" echo(%print%&endlocal & exit /b
set /a width=len%cnt%,cnt+=1
set arr=%2
if %1 equ 1 call:left %width% arr
if %1 equ 2 call:right %width% arr
if %1 equ 3 call:center %width% arr
set "print=%print%%arr% "
shift /2
goto loop2
:left %num% &string
setlocal
set "arr=!%2! "
set arr=!arr:~0,%1!
endlocal & set %2=%arr%
exit /b
:right %num% &string
setlocal
set "arr= !%2!"
set arr=!arr:~-%1!
endlocal & set %2=%arr%
exit /b
:center %num% &string
setlocal
set /a width=%1-1
set arr=!%2!
:loop3
if "!arr:~%width%,1!"=="" set "arr=%arr% "
if "!arr:~%width%,1!"=="" set "arr= %arr%"
if "!arr:~%width%,1!"=="" goto loop3
endlocal & set %2=%arr%
exit /b
:strlen StrVar &RtnVar
setlocal EnableDelayedExpansion
set "s=#%~1"
set "len=0"
for %%N in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%N,1!" neq "" set /a "len+=%%N" & set "s=!s:~%%N!"
)
endlocal & set %~2=%len%
exit /b

View file

@ -0,0 +1,94 @@
SUBROUTINE RAKE(IN,M,X,WAY) !Casts forth text in fixed-width columns.
Collates column widths so that each column is wide enough for its widest member.
INTEGER IN !Fingers the input file.
INTEGER M !Maximum record length thereof.
CHARACTER*1 X !The delimiter, possibly a comma.
INTEGER WAY !Alignment style.
INTEGER W(M + 1) !If every character were X in the maximum-length record,
INTEGER C(0:M + 1) !Then M + 1 would be the maximum number of fields possible.
CHARACTER*(M) ACARD !A scratchpad big enough for the biggest.
CHARACTER*(28 + 4*M) FORMAT !Guess. Allow for "Ann," per field.
INTEGER I !A stepper.
INTEGER L,LF !Text fingers.
INTEGER NF,MF !Field counts.
CHARACTER*6 WAYNESS(-1:+1) !Some annotation may be helpful.
PARAMETER (WAYNESS = (/"Left","Centre","Right"/)) !Using normal language.
INTEGER LINPR !The mouthpiece.
COMMON LINPR !Used all over.
W = 0 !Maximum field widths so far seen.
MF = 0 !Maximum number of fields to a record.
C(0) = 0 !Syncopation for the first field's predecessor.
WRITE (LINPR,*) !Some separation.
WRITE (LINPR,*) "Align ",WAYNESS(MIN(MAX(WAY,-1),+1)) !Explain, cautiously.
Chase through the file assessing the lengths of each field.
10 READ (IN,11,END = 20) L,ACARD(1:L) !Grab a record.
11 FORMAT (Q,A) !Working only up to its end.
CALL LIZZIEBORDEN !Find the chop points.
W(1:NF) = MAX(W(1:NF),C(1:NF) - C(0:NF - 1) - 1) !Thereby the lengths between.
MF = MAX(MF,NF) !Also want to know the most number of chops.
GO TO 10 !Get the next record.
Concoct a FORMAT based on the maximum size of each field. Plus one.
20 REWIND(IN) !Back to the beginning.
WRITE (FORMAT,21) W(1:MF) + 1 !Add one to meet the specified at least one space between columns.
21 FORMAT ("(",<MF>("A",I0,",")) !Generates a sequence of An, items.
LF = INDEX(FORMAT,", ") !The last one has a trailing comma.
IF (LF.LE.0) STOP "Format trouble!" !Or, maybe not!
FORMAT(LF:LF) = ")" !Convert it to the closing bracket.
WRITE (LINPR,*) "Format",FORMAT(1:LF) !Present it.
Chug afresh, this time knowing the maximum length of each field.
30 READ (IN,11,END = 40) L,ACARD(1:L) !Place just the record's content.
CALL LIZZIEBORDEN !Find the chop points.
SELECT CASE(WAY) !What is to be done?
CASE(-1) !Shove leftwards by appending spaces.
WRITE (LINPR,FORMAT) (ACARD(C(I - 1) + 1:C(I) - 1)// !The chopped text.
1 REPEAT(" ",W(I) - C(I) + C(I - 1) + 1),I = 1,NF) !Some spaces.
CASE( 0) !Centre by appending half as many spaces.
WRITE (LINPR,FORMAT) (ACARD(C(I - 1) + 1:C(I) - 1)// !The chopped text.
1 REPEAT(" ",(W(I) - C(I) + C(I - 1) + 1)/2),I = 1,NF) !Some spaces.
CASE(+1) !Align rightwards is the default style.
WRITE (LINPR,FORMAT) (ACARD(C(I - 1) + 1:C(I) - 1),I = 1,NF) !So, just the texts.
CASE DEFAULT !This shouldn't happen.
WRITE (LINPR,*) "Huh? WAY=",WAY !But if it does,
STOP "Unanticipated value for WAY!" !Explain.
END SELECT !So much for that record.
GO TO 30 !Go for another.
Closedown
40 REWIND(IN) !Be polite.
CONTAINS !This also marks the end of source for RAKE...
SUBROUTINE LIZZIEBORDEN !Take an axe to ACARD, chopping at X.
NF = 0 !No strokes so far.
DO I = 1,L !So, step away.
IF (ICHAR(ACARD(I:I)).EQ.ICHAR(X)) THEN !Here?
NF = NF + 1 !Yes!
C(NF) = I !The place!
END IF !So much for that.
END DO !On to the next.
NF = NF + 1 !And the end of ACARD is also a chop point.
C(NF) = L + 1 !As if here.
END SUBROUTINE LIZZIEBORDEN !She was aquitted.
END SUBROUTINE RAKE !So much raking over.
INTEGER L,M,N !To be determined the hard way.
INTEGER LINPR,IN !I/O unit numbers.
COMMON LINPR !Some of general note.
LINPR = 6 !Standard output via this unit number.
IN = 10 !Some unit number for the input file.
OPEN (IN,FILE="Rake.txt",STATUS="OLD",ACTION="READ") !For formatted input.
N = 0 !No records read.
M = 0 !Longest record so far.
1 READ (IN,2,END = 10) L !How long is this record?
2 FORMAT (Q) !Obviously, Q specifies the length, not a content field.
N = N + 1 !Anyway, another record has been read.
M = MAX(M,L) !And this is the longest so far.
GO TO 1 !Go back for more.
10 REWIND (IN) !We're ready now.
WRITE (LINPR,*) N,"Recs, longest rec. length is ",M
CALL RAKE(IN,M,"$",-1) !Align left.
CALL RAKE(IN,M,"$", 0) !Centre.
CALL RAKE(IN,M,"$",+1) !Align right.
END !That's all.

View file

@ -1,63 +1,133 @@
(function (lines) {
(function (strText) {
'use strict';
var LEFT = 0,
CENTRE = 1,
RIGHT = 2;
// [[a]] -> [[a]]
function transpose(lst) {
return lst[0].map(function (_, iCol) {
return lst.map(function (row) {
return row[iCol];
})
});
}
return alignedTable(
lines.map(function (s) {
return s.split('$');
// (a -> b -> c) -> [a] -> [b] -> [c]
function zipWith(f, xs, ys) {
return xs.length === ys.length ? (
xs.map(function (x, i) {
return f(x, ys[i]);
})
) : undefined;
}
// (a -> a -> Ordering) -> [a] -> a
function maximumBy(f, xs) {
return xs.reduce(function (a, x) {
return a === undefined ? x : (
f(x) > f(a) ? x : a
);
}, undefined)
}
// [String] -> String
function widest(lst) {
return maximumBy(length, lst)
.length;
}
// [[a]] -> [[a]]
function fullRow(lst, n) {
return lst.concat(Array.apply(null, Array(n - lst.length))
.map(function () {
return ''
}));
}
// String -> Int -> String
function nreps(s, n) {
var o = '';
if (n < 1) return o;
while (n > 1) {
if (n & 1) o += s;
n >>= 1;
s += s;
}
return o + s;
}
// [String] -> String
function unwords(xs) {
return xs.join(' ');
}
// [String] -> String
function unlines(xs) {
return xs.join('\n');
}
// [a] -> Int
function length(xs) {
return xs.length;
}
// -- Int -> [String] -> [[String]]
function padWords(n, lstWords, eAlign) {
return lstWords.map(function (w) {
var lngPad = n - w.length;
return (
(eAlign === eCenter) ? (function () {
var lngHalf = Math.floor(lngPad / 2);
return [
nreps(' ', lngHalf), w,
nreps(' ', lngPad - lngHalf)
];
})() : (eAlign === eLeft) ?
['', w, nreps(' ', lngPad)] :
[nreps(' ', lngPad), w, '']
)
.join('');
});
}
// MAIN
var eLeft = -1,
eCenter = 0,
eRight = 1;
var lstRows = strText.split('\n')
.map(function (x) {
return x.split('$');
}),
2, // minimum gap between cols
LEFT // [LEFT|CENTRE|RIGHT] or [0|1|2]
)
// TABULATION OF RESULTS IN SPACED AND ALIGNED COLUMNS
// [s] -> n -> enum -> s
function alignedTable(lstRows, lngPad, iAlignment) {
lngCols = widest(lstRows),
lstCols = transpose(lstRows.map(function (r) {
return fullRow(r, lngCols)
})),
lstColWidths = lstCols.map(widest);
// Max width of each column
var lstColWidths = range(0, lstRows.reduce(function (a, x) {
return x.length > a ? x.length : a;
}, 0) - 1).map(function (iCol) {
return lstRows.reduce(function (a, lst) {
var w = lst[iCol] ? lst[iCol].toString().length : 0;
return (w > a) ? w : a;
}, 0);
});
// THREE PARAGRAPHS, WITH VARIOUS WORD COLUMN ALIGNMENTS:
// Rows padded to equal width
return lstRows.map(function (lstRow) {
return lstRow.map(function (v, i) {
return align(v, lstColWidths[i] + lngPad, iAlignment);
}).join('')
}).join('\n');
}
return [eLeft, eRight, eCenter]
.map(function (eAlign) {
var fPad = function (n, lstWords) {
return padWords(n, lstWords, eAlign);
};
// Text padded to left and/or right: aligned (left|centre|right)
// String, number of characters, alignment 0|1|2
// s -> n -> enum -> s
function align(s, n, a) {
var mid = (1 === a),
gap = n - s.length,
pad = Array(Math.floor((gap + 1) / (mid ? 2 : 1))).join(' ');
return transpose(
zipWith(fPad, lstColWidths, lstCols)
)
.map(unwords);
})
.map(unlines)
.join('\n\n');
return (a ? pad + (!mid || gap % 2 ? '' : ' ') : '') + s +
(2 > a ? pad + (mid ? ' ' : '') : '');
}
// [m..n]
function range(m, n) {
return Array.apply(null, Array(n - m + 1)).map(function (x, i) {
return m + i;
});
}
})([
"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."
]);
})(
"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."
);

View file

@ -5,17 +5,11 @@ import java.nio.file.Files
import java.nio.file.Paths
enum class Align_function {
LEFT {
override fun invoke(s: String, length: Int) = ("%-" + length + 's').format(("%" + s.length() + 's').format(s))
},
RIGHT {
override fun invoke(s: String, length: Int) = ("%-" + length + 's').format(("%" + length + 's').format(s))
},
CENTER {
override fun invoke(s: String, length: Int) = ("%-" + length + 's').format(("%" + ((length + s.length()) / 2) + 's').format(s))
};
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)) };
abstract operator fun invoke(s: String, length: Int): String
abstract operator fun invoke(s: String, l: Int): String
}
/** Aligns fields into columns, separated by "|".
@ -23,42 +17,42 @@ enum class Align_function {
* @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 {
val result = StringBuilder()
operator fun invoke(a: Align_function) : String {
var result = ""
for (lineWords in words) {
for (i in lineWords.indices) {
if (i == 0)
result.append('|')
result.append(a(lineWords[i], column_widths[i]))
result.append('|')
result += '|'
result += a(lineWords[i], column_widths[i])
result += '|'
}
result.append('\n')
result += '\n'
}
return result.toString()
return result
}
private val words = arrayListOf<Array<String>>()
private val column_widths = arrayListOf<Int>()
init {
lines forEach {
lines.forEach {
val lineWords = java.lang.String(it).split("\\$")
words += lineWords
for (i in lineWords.indices)
if (i >= column_widths.size())
column_widths += lineWords[i].length()
if (i >= column_widths.size)
column_widths += lineWords[i].length
else
column_widths[i] = Math.max(column_widths[i], lineWords[i].length())
column_widths[i] = Math.max(column_widths[i], lineWords[i].length)
}
}
}
fun main(args: Array<String>) {
if (args.size() < 1)
if (args.size < 1)
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"
val alignment = if (args.size >= 2) args[1] else "L"
when (alignment) {
"L" -> print(ca(Align_function.LEFT))
"R" -> print(ca(Align_function.RIGHT))

View file

@ -2,7 +2,7 @@ my @lines = slurp("example.txt").lines;
my @widths;
for @lines { for .split('$').kv { @widths[$^key] max= $^word.chars; } }
for @lines { say .split('$').kv.map: { (align @widths[$^key], $^word) ~ " "; } }
for @lines { say |.split('$').kv.map: { (align @widths[$^key], $^word) ~ " "; } }
sub align($column_width, $word, $aligment = @*ARGS[0]) {
my $lr = $column_width - $word.chars;

View file

@ -0,0 +1,7 @@
sub MAIN ($alignment where 'left'|'right', $file) {
my @lines := $file.IO.lines.map(*.split: '$').List;
my @widths = roundrobin(|@lines).map(*».chars.max);
my $align = {left=>'-', right=>''}{$alignment};
my $format = @widths.map({ "%{++$}\${$align}{$_}s" }).join(" ") ~ "\n";
printf $format, |$_ for @lines;
}

View file

@ -0,0 +1,22 @@
$file =
@'
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.
'@.Split("`n")
$arr = @()
$file | foreach {
$line = $_
$i = 0
$hash = [ordered]@{}
$line.split('$') | foreach{
$hash["$i"] = "$_"
$i++
}
$arr += @([pscustomobject]$hash)
}
$arr | Format-Table -HideTableHeaders -Wrap *

View file

@ -0,0 +1,47 @@
5 BORDER 2
10 DATA 6
20 DATA "The$problem$of$Speccy$"
30 DATA "is$the$screen.$"
40 DATA "Need$adapt$text$sample$"
50 DATA "for$show$the$result$"
60 DATA "without$problem$,right?$"
70 DATA "But$see$the$code.$"
80 REM First find the maximum length of a 'word'
90 LET max=0: LET d$="$"
100 READ nlines
110 FOR l=1 TO nlines
120 READ t$
130 GO SUB 1000
150 NEXT l
155 LET s$=" "( TO max)
160 REM Now display the aligned text:
170 LET m$="l": GO SUB 2000: PRINT
180 LET m$="r": GO SUB 2000: PRINT
190 LET m$="c": GO SUB 2000
200 STOP
1000 REM Maximum length of a word
1010 LET lt=LEN t$: LET p=1: LET lw=0
1020 FOR i=1 TO lt
1030 IF t$(i)=d$ THEN LET lw=i-p: LET p=i: IF lw>max THEN LET max=lw
1040 NEXT i
1050 RETURN
2000 REM Show aligned text
2010 RESTORE 20
2020 FOR l=1 TO nlines
2030 READ t$
2040 GO SUB 3000
2050 NEXT l
2060 RETURN
3000 REM Show words
3010 LET lt=LEN t$: LET p=1: LET lw=0
3020 FOR i=1 TO lt
3030 IF t$(i)<>d$ THEN GO TO 3090
3035 LET lw=i-p
3040 LET p$=t$(p TO i-1): LET p=i+1: LET z$=s$
3050 IF m$="l" THEN LET z$( TO lw)=p$
3060 IF m$="r" THEN LET z$(max-lw+1 TO )=p$
3070 IF m$="c" THEN LET z$((max/2)-(lw/2) TO )=p$
3080 PRINT z$;
3090 NEXT i
3095 PRINT
3100 RETURN