RosettaCodeData/Task/Align-columns/Oz/align-columns.oz

76 lines
2.1 KiB
Plaintext

declare
%% Lines: list of strings
%% Alignment: function like fun {Left Txt ExtraSpace} ... end
%% Returns: list of aligned (virtual) strings
fun {Align Lines Alignment}
ParsedLines = {Map Lines ParseLine}
NumColumns = {Maximum {Map ParsedLines Record.width}}
%% maps column index to column width:
WidthOfColumn = {Record.map {TupleRange NumColumns}
fun {$ ColumnIndex}
fun {LengthOfThisColumn ParsedLine}
{Length {CondSelect ParsedLine ColumnIndex nil}}
end
in
{Maximum {Map ParsedLines LengthOfThisColumn}}
end}
in
{Map ParsedLines
fun {$ Columns}
{Record.mapInd Columns
fun {$ ColumnIndex ColumnText}
Extra = WidthOfColumn.ColumnIndex - {Length ColumnText}
in
{Alignment ColumnText Extra}#" "
end}
end}
end
%% A parsed line is a tuple of columns.
%% "a$b$c" -> '#'(1:"a" 2:"b" 3:"c")
fun {ParseLine Line}
{List.toTuple '#' {String.tokens Line &$}}
end
%% possible alignments:
fun {Left Txt Extra}
Txt#{Spaces Extra}
end
fun {Right Txt Extra}
{Spaces Extra}#Txt
end
fun {Center Txt Extra}
Half = Extra div 2
in
{Spaces Half}#Txt#{Spaces Half + Extra mod 2}
end
%% helpers:
%% 3 -> unit(1 2 3)
fun {TupleRange Max}
{List.toTuple unit {List.number 1 Max 1}}
end
fun {Maximum X|Xr}
{FoldL Xr Value.max X}
end
fun {Spaces N}
case N of 0 then nil
else & |{Spaces N-1}
end
end
Lines = ["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."]
in
{ForAll {Align Lines Left} System.showInfo}