Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add generic Max, Min, Mid routines to csdb collection. #8

Open
delphidabbler opened this issue Jan 2, 2022 · 7 comments
Open

Add generic Max, Min, Mid routines to csdb collection. #8

delphidabbler opened this issue Jan 2, 2022 · 7 comments
Assignees
Labels
considering Issue is currently under consideration enhancement New feature or request

Comments

@delphidabbler
Copy link
Owner

delphidabbler commented Jan 2, 2022

There are lots of overloaded Max, Min and Mid routines in the Maths category of the csdb collection, but it's crying out for some generic versions.

⚠️ Will need to check if CodeSnip can handle generic routines first.

@delphidabbler delphidabbler added the enhancement New feature or request label Jan 2, 2022
@delphidabbler delphidabbler self-assigned this Jan 2, 2022
@delphidabbler
Copy link
Owner Author

Here's an example function

function Max<T>(const A: array of T; Compare: TComparison<T>): T; overload;
begin
  Assert(Length(A) > 0);
  Result := A[Low(A)];
  for var I := Succ(Low(A)) to High(A) do
  begin
    if Compare(A[I], Result) > 0 then
      Result := A[I];
  end;
end;

@delphidabbler
Copy link
Owner Author

Here's an example function

function Max<T>(const A: array of T; Compare: TComparison<T>): T; overload;
begin
  Assert(Length(A) > 0);
  Result := A[Low(A)];
  for var I := Succ(Low(A)) to High(A) do
  begin
    if Compare(A[I], Result) > 0 then
      Result := A[I];
  end;
end;

From what I've read generic global functions can't be declared, so it looks as though the generic functions may need to be grouped together in a static class or record, e.g.:

type
  TMinMax = record
    class function Max<T>(const A: array of T; Compare: TComparison<T>): T; overload; static;
    class function Min<T>(const A: array of T; Compare: TComparison<T>): T; overload; static;
    class function Max<T>(const A, B: T; Compare: TComparison<T>): T; overload; static;
    class function Min<T>(const A, B: T; Compare: TComparison<T>): T; overload; static;
  end;

The reason for putting the type parameter on the metho, rather than the records is so that non-generic methods can be included. For example:

type
  TMinMax = record
    class function Max<T>(const A: array of T; Compare: TComparison<T>): T; overload; static;
    // ...
    class function Max(const A: array of Integer): Integer; overload; static;
  end;

@delphidabbler
Copy link
Owner Author

⚠️ Will need to check if CodeSnip can handle generic routines first.

Can't declare a function or procedure with generic type - instead need to declare a static class or record with generic methods.

Tested a record with generic methods in CodeSnip and it compiled in Delphi 11.

@delphidabbler delphidabbler added the considering Issue is currently under consideration label Jul 16, 2023
@delphidabbler
Copy link
Owner Author

delphidabbler commented Jul 18, 2023

Could generalise this for TArray<T> by providing a Select method that is passed a selector closure:

type
  TElemSelector = record
  public
    type
      TSelectFn<T> = reference to function(const ACurrent, ACandidate: T; AComparer: IComparer<T>): T;
    class function Select<T>(const A: array of T; ASelector: TSelectFn<T>; AComparer: IComparer<T> = nil): T; static;
    class function Max<T>(const A: array of T; AComparer: IComparer<T> = nil): T; static;
    // ... etc
  end;

class function TElemSelector.Select<T>(const A: array of T; ASelector: TSelectFn<T>; AComparer: IComparer<T> = nil): T; 
begin
  Assert(Length(A) > 0);
  if not Assigned(AComparer) then
    AComparer := TComparer<T>.Default; 
  Result := A[Low(A)];
  for var I := Succ(Low(A)) to High(A) do
  begin
    Result := ASelector(Result, A[I], AComparer);
  end;
end;

class function TElemSelector.Max<T>(const A: array of T; AComparer: IComparer<T>): T;
begin
  Result := Select(
    A, 
    function(const ACurrent, ACandidate: T; AComparer: IComparer<T>): T
    begin
      if AComparer.Compare(ACurrent, ACandidate) > 0 the
        Result := ACurrent
      else
        Result := ACandidate;
    end,
    AComparer
  );
end

@delphidabbler
Copy link
Owner Author

Could generalise this for TArray<T> by providing a Select method that is passed a selector closure:

Thinking about this further the Select<T> method doesn't generalise too well, because all the selector fn can do it can do is choose a value based on the comparer, which is pretty much to choose the smallest or greatest, which is what Min & Max do.

@delphidabbler
Copy link
Owner Author

delphidabbler commented Jul 23, 2023

Thinking about this further the Select<T> method doesn't generalise too well, because all the selector fn can do it can do is choose a value based on the comparer, which is pretty much to choose the smallest or greatest, which is what Min & Max do.

It would be better to create a differently named record with methods that is more generalised that has array operations based on those of JavaScript. E.g.s forEach(), some(), all() & map().

@delphidabbler
Copy link
Owner Author

delphidabbler commented Jul 23, 2023

It's difficult to see how to generalise Mid to an array. What if the array has an even number of elements? Do we average the middle two? If we do and the elements are integral then result may be real.

The easiest way of finding the mid value in an array is to sort the array and pick the middle element if the array length is odd or to average the middle two elements if even. This is the same as taking the median value if the array. This is dealt with in issue #16.

❗ For the above reasons we won't be adding a Mid function for arrays.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
considering Issue is currently under consideration enhancement New feature or request
Projects
Status: Considering
Development

No branches or pull requests

1 participant