🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of dgeiger

dgeiger's solution

to Matrix in the Delphi Pascal Track

Published at Sep 03 2020 · 0 comments
Instructions
Test suite
Solution

Given a string representing a matrix of numbers, return the rows and columns of that matrix.

So given a string with embedded newlines like:

9 8 7
5 3 2
6 6 7

representing this matrix:

    1  2  3
  |---------
1 | 9  8  7
2 | 5  3  2
3 | 6  6  7

your code should be able to spit out:

  • A list of the rows, reading each row left-to-right while moving top-to-bottom across the rows,
  • A list of the columns, reading each column top-to-bottom while moving from left-to-right.

The rows for our example matrix:

  • 9, 8, 7
  • 5, 3, 2
  • 6, 6, 7

And its columns:

  • 9, 5, 6
  • 8, 3, 6
  • 7, 2, 7

Testing

In order to run the tests for this track, you will need to install DUnitX. Please see the installation instructions for more information.

Loading Exercises into Delphi

If Delphi is properly installed, and *.dpr file types have been associated with Delphi, then double clicking the supplied *.dpr file will start Delphi and load the exercise/project. control + F9 is the keyboard shortcut to compile the project or pressing F9 will compile and run the project.

Alternatively you may opt to start Delphi and load your project via. the File drop down menu.

When Questions Come Up

We monitor the Pascal-Delphi support room on gitter.im to help you with any questions that might arise.

Submitting Exercises

Note that, when trying to submit an exercise, make sure the exercise file you're submitting is in the exercism/delphi/<exerciseName> directory.

For example, if you're submitting ubob.pas for the Bob exercise, the submit command would be something like exercism submit <path_to_exercism_dir>/delphi/bob/ubob.pas.

Source

Warmup to the saddle-points warmup. http://jumpstartlab.com

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you may request help from a mentor.

uMatrixTests.pas

unit uMatrixTests;

interface

uses
  DUnitX.TestFramework, System.Generics.Collections;

const
  CanonicalVersion = '1.1.0.1';

type

  [TestFixture]
  TMatrixTest = class(TObject)
  private
    ExpectedRow : TList<integer>;
    procedure CompareArrays(Array1, Array2: TArray<integer>);
  public
    [Setup]
    procedure Setup;

    [TearDown]
    procedure TearDown;

    [Test]
//    [Ignore('Comment the "[Ignore]" statement to run the test')]
    procedure extract_row_from_one_number_matrix;

    [Test]
    [Ignore]
    procedure can_extract_row;

    [Test]
    [Ignore]
    procedure extract_row_where_numbers_have_different_widths;

    [Test]
    [Ignore]
    procedure can_extract_row_from_non_square_matrix;

    [Test]
    [Ignore]
    procedure extract_column_from_one_number_matrix;

    [Test]
    [Ignore]
    procedure can_extract_column;

    [Test]
    [Ignore]
    procedure can_extract_column_from_non_square_matrix;

    [Test]
    [Ignore]
    procedure extract_column_where_numbers_have_different_widths;
  end;

implementation
uses uMatrix;

procedure TMatrixTest.can_extract_column;
var
  CUT: TMatrix;
  Expected, Actual : TArray<integer>;
  i: Integer;
begin
  ExpectedRow.AddRange([3, 6, 9]);
  Expected := ExpectedRow.ToArray;
  CUT := TMatrix.Create('1 2 3\n4 5 6\n7 8 9');
  Actual := CUT.column(3);
  CompareArrays(Expected, Actual);
end;

procedure TMatrixTest.can_extract_column_from_non_square_matrix;
var
  CUT: TMatrix;
  Expected, Actual : TArray<integer>;
  i: Integer;
begin
  ExpectedRow.AddRange([3, 6, 9, 6]);
  Expected := ExpectedRow.ToArray;
  CUT := TMatrix.Create('1 2 3\n4 5 6\n7 8 9\n8 7 6');
  Actual := CUT.column(3);
  CompareArrays(Expected, Actual);
end;

procedure TMatrixTest.can_extract_row;
var
  CUT: TMatrix;
  Expected, Actual : TArray<integer>;
  i: Integer;
begin
  ExpectedRow.AddRange([3, 4]);
  Expected := ExpectedRow.ToArray;
  CUT := TMatrix.Create('1 2\n3 4');
  Actual := CUT.Row(2);
  CompareArrays(Expected, Actual);
end;

procedure TMatrixTest.can_extract_row_from_non_square_matrix;
var
  CUT: TMatrix;
  Expected, Actual : TArray<integer>;
  i: Integer;
begin
  ExpectedRow.AddRange([7, 8, 9]);
  Expected := ExpectedRow.ToArray;
  CUT := TMatrix.Create('1 2 3\n4 5 6\n7 8 9\n8 7 6');
  Actual := CUT.Row(3);
  CompareArrays(Expected, Actual);
end;

procedure TMatrixTest.extract_column_from_one_number_matrix;
var
  CUT: TMatrix;
  Expected, Actual : TArray<integer>;
  i: Integer;
begin
  ExpectedRow.AddRange([1]);
  Expected := ExpectedRow.ToArray;
  CUT := TMatrix.Create('1');
  Actual := CUT.column(1);
  CompareArrays(Expected, Actual);
end;

procedure TMatrixTest.extract_column_where_numbers_have_different_widths;
var
  CUT: TMatrix;
  Expected, Actual : TArray<integer>;
  i: Integer;
begin
  ExpectedRow.AddRange([1903, 3, 4]);
  Expected := ExpectedRow.ToArray;
  CUT := TMatrix.Create('89 1903 3\n18 3 1\n9 4 800');
  Actual := CUT.column(2);
  CompareArrays(Expected, Actual);
end;

procedure TMatrixTest.extract_row_from_one_number_matrix;
var
  CUT: TMatrix;
  Expected, Actual : TArray<integer>;
  i: Integer;
begin
  ExpectedRow.AddRange([1]);
  Expected := ExpectedRow.ToArray;
  CUT := TMatrix.Create('1');
  Actual := CUT.Row(1);
  CompareArrays(Expected, Actual);
end;

procedure TMatrixTest.extract_row_where_numbers_have_different_widths;
var
  CUT: TMatrix;
  Expected, Actual : TArray<integer>;
  i: Integer;
begin
  ExpectedRow.AddRange([10, 20]);
  Expected := ExpectedRow.ToArray;
  CUT := TMatrix.Create('1 2\n10 20');
  Actual := CUT.Row(2);
  CompareArrays(Expected, Actual);
end;

procedure TMatrixTest.CompareArrays(Array1, Array2: TArray<integer>);
var
  i: integer;
begin
  Assert.AreEqual(Length(Array1), Length(Array2));
  for i := Low(Array1) to High(Array1) do
    Assert.AreEqual(Array1[i], Array2[i]);
end;

procedure TMatrixTest.Setup;
begin
  ExpectedRow := TList<integer>.Create;
end;

procedure TMatrixTest.TearDown;
begin
  ExpectedRow.DisposeOf;
end;

initialization
  TDUnitX.RegisterTestFixture(TMatrixTest);
end.
unit uMatrix;

interface

uses
  System.Classes, System.Generics.Collections, System.SysUtils;

type
  TMatrix = class
    private
      FMatrix: TArray<TArray<Integer>>;
      FMatrixString: String;
      FColumns: Integer;
      FRows: Integer;
      FRow: Integer;

      procedure ExtractRows;
      procedure ExtractElements;

    public
      constructor Create(MatrixString: String);

      function Column(ColumnNo: Integer): TArray<Integer>;
      function Row(RowNo: Integer): TArray<Integer>;

  end;

implementation

{ TMatrix }

function TMatrix.Column(ColumnNo: Integer): TArray<Integer>;
var
  Row: Integer;
begin
  // Set the length of the result TArray to the number of rows
  SetLength(Result, FRows);

  // If ColumnNo is outside the range of columms in the matrix (when scaled)
  // it can't be processed, so quit.
  if (ColumnNo < 1) or (ColumnNo > Length(FMatrix[0])) then
    Exit;

  // Now, we need to cycle through the rows
  for Row := 0 to FRows - 1 do
    // adding the element in the specified column to the result.
    Result[Row] := FMatrix[Row, ColumnNo - 1];
end;

constructor TMatrix.Create(MatrixString: String);
begin
  // Start of by saving the passed string
  FMatrixString := Trim(MatrixString);

  // Then extract the elements from the string and insert them into the matrix
  ExtractRows;
end;

procedure TMatrix.ExtractElements;
var
  Elements: TList<Integer>;
  Position: Integer;
  ElementString: String;
  Column: Integer;
begin
  // Create the list of elements found
  Elements := TList<Integer>.Create;

  // Start with no columns in the row - we'll figure out the size later
  FColumns := 0;

  // We look for elements until the string is empty
  while Length(FMatrixString) > 0 do
    begin
      // Look for a space between elements
      Position := Pos(' ', FMatrixString);

      if Position = 0 then
        // There was no space, so this is the last element
        begin
          // Save it's value
          ElementString := FMatrixString;

          // and empty the string, signaling that we're done here.
          FMatrixString := '';
        end
      else
        // We found the gap between two elements
        begin
          // Save the element's value
          ElementString := Copy(FMatrixString, 1, Position - 1);

          // Remove the element from the string
          Delete(FMatrixString, 1, Position);

          // Make sute there aren't any spaces in the front of the string
          FMatrixString := Trim(FMatrixString);
        end;

        // Add the element to the string. In the real world, I'd be checking
        // to make sure the element was an integer, but I'm omitting it for
        // this exercixe.
        Elements.Add(StrToInt(ElementString));

        // Add to the column count
        Inc(FColumns);
    end;

  // Set the matrix row's size
  SetLength(FMatrix[FRow], FColumns);

  // Now it's time to put the elements into the matrix row
  for Column := 0 to FColumns - 1 do
    begin
      // Set the cell to the element value
      FMatrix[FRow, Column] := Elements[0];

      // and remove the element value from the list.
      Elements.Delete(0);
    end;

  // Clean up
  Elements.Destroy;
end;

procedure TMatrix.ExtractRows;
var
  Rows: TStringList;
  Position: Integer;
  RowString: String;
  Index: Integer;
begin
  // Create the list of rows found
  Rows := TStringList.Create;

  // Start with no rows in the matrix - we'll figure out the size later
  FRows := 0;

  // We look for rows until the string is empty
  while Length(FMatrixString) > 0 do
    begin
      // Look for the new-line sequence between rows
      Position := Pos('\', FMatrixString);

      // There was no new-line sequence, so this is the last row
      if Position = 0 then
        begin
          // Save it's value
          RowString := FMatrixString;

          // and empty the string, signaling that we're done here.
          FMatrixString := '';
        end
      else
        // There was no new-line sequence, so this is the last element
        begin
          // Save the row text for later processing
          RowString := Copy(FMatrixString, 1, Position - 1);

          // and remove it from the string.
          Delete(FMatrixString, 1, Position + 1);

          // Make sute there aren't any spaces in the front of the string
          FMatrixString := Trim(FMatrixString);
        end;

        // Add the row text to the list of rows
        Rows.Add(RowString);

        // Increment the row count
        Inc(FRows);
    end;

  // Now that we know how many rows are in the matrix, set it's length
  SetLength(FMatrix, FRows);

  // Now let's break the rows into columns, and put the cell values in the matrix
  for Index := 0 to Rows.Count - 1 do
    begin
      // Set which row is being processed
      FRow := Index;

      // Get it's string
      FMatrixString := Rows[Index];

      // and put the elements into the matrix.
      ExtractElements;
    end;

  // Clean up
  Rows.Destroy;
end;

function TMatrix.Row(RowNo: Integer): TArray<Integer>;
var
  ColumnNo: Integer;
begin
  // Set the length of the result TArray to the number of columns
  SetLength(Result, FColumns);

  // If RowNo is outside the range of rows in the matrix (when scaled)
  // it can't be processed, so quit.
  if (RowNo < 1) or (RowNo > Length(FMatrix)) then
    Exit;

  // Now, we need to cycle through the columns
  for ColumnNo := 0 to FColumns - 1 do
    // adding the element in the specified row to the result.
    Result[ColumnNo] := FMatrix[RowNo - 1, ColumnNo];
end;

end.

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?