Task

Define a function/method/subroutine which sorts a sequence ("table") of sequences ("rows") of strings ("cells"), by one of the strings. Besides the input to be sorted, it shall have the following optional parameters:





 ordering
 A function specifying the ordering of strings; lexicographic by default.
 column
 An integer specifying which string of each row to compare; the first by default.
 reverse
 Reverses the ordering.


This task should be considered to include both positional and named optional parameters, as well as overloading on argument count as in Java or selector name as in Smalltalk, or, in the extreme, using different function names. Provide these variations of sorting in whatever way is most natural to your language. If the language supports both methods naturally, you are encouraged to describe both.

Do not implement a sorting algorithm; this task is about the interface. If you can't use a built-in sort routine, just omit the implementation (with a comment).

See also:

 Named Arguments


program Optional_parameters;
 
{$APPTYPE CONSOLE}
 
uses
  System.SysUtils;
 
type
  TRow = TArray<string>;
 
  TOrderingFun = TFunc<TRow, TRow, Boolean>;
 
  TTable = array of TRow;
 
  TRowHelper = record helper for TRow
  public
    procedure Swap(var other: TRow);
    function ToString: string;
    function Length: Integer;
  end;
 
  TTableHelper = record helper for TTable
  private
    procedure ExchangeRow(i, j: Integer);
  public
    procedure Sort(OrderingFun: TOrderingFun);
    procedure Reverse;
    function ToString: string;
  end;
 
function Max(a, b: Integer): Integer;
begin
  if a > b then
    exit(a);
  Result := b;
end;
 
{ TRowHelper }
 
function TRowHelper.Length: Integer;
begin
  Result := System.Length(self);
end;
 
procedure TRowHelper.Swap(var other: TRow);
var
  aLengthOther, aLengthSelf, aLength: Integer;
  tmp: string;
  i: Integer;
begin
  aLengthOther := other.Length;
  aLengthSelf := self.Length;
  aLength := max(aLengthOther, aLengthSelf);
  if aLength = 0 then
    exit;
 
  SetLength(self, aLength);
  SetLength(other, aLength);
 
  for i := 0 to aLength - 1 do
  begin
    tmp := self[i];
    self[i] := other[i];
    other[i] := tmp;
  end;
 
  SetLength(self, aLengthOther);
  SetLength(other, aLengthSelf);
end;
 
function TRowHelper.ToString: string;
var
  i: Integer;
begin
  Result := '[';
  for i := 0 to High(self) do
  begin
    if i > 0 then
      Result := Result + ', ';
    Result := Result + '"' + self[i] + '"';
  end;
  Result := Result + ']';
end;
 
{ TTableHelper }
 
procedure TTableHelper.ExchangeRow(i, j: Integer);
begin
  Self[i].Swap(self[j]);
end;
 
procedure TTableHelper.reverse;
var
  aLength, aHalfLength: Integer;
  i: Integer;
begin
  aLength := Length(self);
  aHalfLength := aLength div 2;
  for i := 0 to aHalfLength - 1 do
    ExchangeRow(i, aLength - i - 1);
end;
 
procedure TTableHelper.Sort(OrderingFun: TOrderingFun);
var
  i, j, aLength: Integer;
begin
  if not Assigned(OrderingFun) then
    exit;
 
  aLength := Length(self);
  for i := 0 to aLength - 2 do
    for j := i + 1 to aLength - 1 do
      if OrderingFun(self[i], self[j]) then
        ExchangeRow(i, j);
end;
 
function TTableHelper.ToString: string;
var
  i: Integer;
begin
  Result := '[';
  for i := 0 to High(self) do
  begin
    if i > 0 then
      Result := Result + #10;
    Result := Result + self[i].ToString;
  end;
  Result := Result + ']';
end;
 
function SortTable(table: TTable; Ordering: TOrderingFun = nil; column: Integer
  = 0; reverse: Boolean = false): TTable;
var
  acolumn: Integer;
begin
  acolumn := column;
  if not Assigned(Ordering) then
    Ordering :=
      function(left, right: TRow): Boolean
      begin
        Result := left[acolumn] > right[acolumn];
      end;
 
  table.Sort(Ordering);
  if (reverse) then
    table.reverse();
  Result := table;
end;
 
var
  data: TTable = [['a', 'b', 'c'], ['', 'q', 'z'], ['zap', 'zip', 'Zot']];
 
begin
  Writeln(data.ToString, #10);
  Writeln(SortTable(data).ToString, #10);
  Writeln(SortTable(data).ToString, #10);
  Writeln(SortTable(data, nil, 2).ToString, #10);
  Writeln(SortTable(data, nil, 1).ToString, #10);
  Writeln(SortTable(data, nil, 1, True).ToString, #10);
  Writeln(SortTable(data,
    function(left, right: TRow): Boolean
    begin
      Result := left.Length > right.Length;
    end).ToString, #10);
  Readln;
end.