Как получить максимальное значение в общем TList ‹Integer›?

Как проще всего получить максимальное значение в TList<Integer>?

function GetMaximum(AList: TList<Integer>): Integer;
begin
  Assert(AList.Count > 0);
  Result := ?;
end;

Я читал, что в C # есть AList.Max, есть ли что-то подобное в Delphi?


person Jens Mühlenhoff    schedule 03.01.2013    source источник
comment
Путь .NET: stackoverflow.com/questions / 5295112 /   -  person Jens Mühlenhoff    schedule 03.01.2013
comment
Конечно, у .net есть прекрасные методы расширения для подобных вещей .......   -  person David Heffernan    schedule 03.01.2013
comment
Spring Framework для Delphi определяет интерфейс IEnumerable ‹T›, который предоставляет LINQ-подобные перечисляемые методы расширения. Рекомендуемые!   -  person Erwin    schedule 03.01.2013
comment
@Erwin Разве IEnumerable ‹T› тоже не определен в system.generics.collections? Что к этому добавляет Spring Framework?   -  person Marjan Venema    schedule 03.01.2013
comment
@Marjan Venema: В Generics.Collections определен class TEnumerable ‹T›. С другой стороны, в Spring Framework для Delphi коллекции основаны на интерфейсе. В модуле Spring.Collections.Lists определен класс TList ‹T›, который предоставляет методы расширения и, в конечном итоге, реализует IEnumerable (T). Вы можете вызвать List.Max, но также, например, List.Reversed. См. Также Demo.Spring.Enumerators проект.   -  person Erwin    schedule 04.01.2013
comment
@Marjan Venema: Ник Ходжес также написал серию замечательных статей на Внедрение зависимостей с помощью Delphi Spring Framework. У вас также есть Ленивая инициализация и Типы, допускающие значение NULL. Как я уже сказал: рекомендуется!   -  person Erwin    schedule 04.01.2013
comment
@Erwin Но это ведь не методы расширения?   -  person David Heffernan    schedule 04.01.2013
comment
Общие коллекции Delphi (к сожалению, больше не поддерживаются) от Alex Ciobanu поддерживают множество LINQ-подобных методов расширения, включая общую функцию Max: code.google.com/p/delphi-coll/wiki/EnexOperations   -  person iamjoosy    schedule 04.01.2013
comment
@ Дэвид Хеффернан: в модуле Spring.Collections.pas определен интерфейс IEnumerable ‹T›. В комментариях говорится: «Предоставляет ограниченные перечисляемые методы расширения, подобные LINQ». Я думаю, вы можете назвать их своего рода методами расширения, вам так не кажется?   -  person Erwin    schedule 04.01.2013
comment
@Erwin Я бы не стал называть их методами расширения. В Delphi их нет. Но это не значит, что я не считаю spring4d крутым!   -  person David Heffernan    schedule 04.01.2013


Ответы (4)


Вот забавный пример реализации MaxValue в универсальном контейнере:

{$APPTYPE CONSOLE}

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

type
  TMyList<T> = class(TList<T>)
  public
    function MaxValue: T;
  end;

{ TMyList<T> }

function TMyList<T>.MaxValue: T;
var
  i: Integer;
  Comparer: IComparer<T>;
begin
  if Count=0 then
    raise Exception.Create('Cannot call TMyList<T>.MaxValue on an empty list');
  Comparer := TComparer<T>.Default;
  Result := Self[0];
  for i := 1 to Count-1 do
    if Comparer.Compare(Self[i], Result)>0 then
      Result := Self[i];
end;

var
  IntList: TMyList<Integer>;
  DoubleList: TMyList<Double>;
  StringList: TMyList<string>;

begin
  IntList := TMyList<Integer>.Create;
  IntList.AddRange([10, 5, 12, -49]);
  Writeln(IntList.MaxValue);

  DoubleList := TMyList<Double>.Create;
  DoubleList.AddRange([10.0, 5.0, 12.0, -49.0]);
  Writeln(DoubleList.MaxValue);

  StringList := TMyList<string>.Create;
  StringList.AddRange(['David Heffernan', 'Uwe Raabe', 'Warren P', 'Jens Mühlenhoff']);
  Writeln(StringList.MaxValue);

  Readln;
end.

Поскольку мы не можем придумать общий эквивалент low(Integer), я вызываю исключение, когда метод вызывается для пустого списка.

Результат:

12
 1.20000000000000E+0001
Warren P
person David Heffernan    schedule 03.01.2013

Вот альтернативный ответ: используйте модуль Spring.Collections.pas из фреймворка Spring4D: (находится здесь: http://code.google.com/p/delphi-spring-framework/)

program ListEnumerableDemo;

{$APPTYPE CONSOLE}

uses 
    System.SysUtils 
  , Spring.Collections;

var 
  List: IList<Integer>; 
  Enumerable: IEnumerable<Integer>;

begin 
  try 
    List := TCollections.CreateList<Integer>; 
    List.AddRange([1,6,2,9,54,3,2,7,9,1]);

    Enumerable := List; 
    WriteLn(Enumerable.Max); 
    ReadLn; 
  except 
    on E: Exception do 
      Writeln(E.ClassName, ': ', E.Message); 
  end; 
end. 
person Nick Hodges    schedule 06.01.2013
comment
+1 за использование ENumerable<T>, поскольку это не проблема TList<T>. - person Jeroen Wiert Pluimers; 06.01.2013
comment
Поскольку IList ‹T› наследуется от IEnumerable ‹T›, мы можем использовать List.Max без преобразования его в IEnumerable ‹T› :) - person Baoquan Zuo; 07.01.2013
comment
@Jeroen Я вообще не понимаю этот комментарий. В ответе используется общий список. Просто не вариант от Generics.Collections. - person David Heffernan; 07.01.2013
comment
Весной гораздо больше, чем просто IList выставляет IEnumerable<T>. В этом суть ответа Ника. - person Jeroen Wiert Pluimers; 07.01.2013
comment
@Nick Как IEnumerable<Integer>.Max обрабатывает пустой контейнер? - person David Heffernan; 07.01.2013
comment
@ Дэвид: Хороший вопрос. Я попробовал вышеуказанную демонстрацию и закомментировал AddRange, который заполняет список. Он делает то, на что я надеялся. Вызывает исключение EInvalidOpException с сообщением «Эта последовательность пуста». - person Warren P; 07.01.2013

Использование for .. in:

function GetMaximum(AList: TList<Integer>): Integer;
var
  I: Integer
begin
  Assert(AList.Count > 0);
  Result := Low(Integer);
  for I in AList do
    if I > Result then
      Result := I;
end;
person Jens Mühlenhoff    schedule 03.01.2013
comment
Можно ли сделать это таким же мощным способом, как IEnumerable в .net? Другими словами, не могли бы вы сделать общую реализацию GetMaximum, используя обобщения / черты? - person Warren P; 03.01.2013
comment
@Warren - в модуле Spring.Collections.pas есть IEnumerable ‹T›, что упростило бы задачу. - person Nick Hodges; 04.01.2013

Я согласен с тем, что использование коллекций Spring, вероятно, самый простой способ. Однако могут быть причины не использовать их (уже повсеместно используются Generics.Collections).

Итак, вот как создать новый тип, который расширяет TEnumerable<T> с помощью function Max: T.

type
  Enumerable<T> = record
  private
    source: TEnumerable<T>;
  public
    function Max: T;

    class operator Implicit(const value: TEnumerable<T>): Enumerable<T>;
  end;

class operator Enumerable<T>.Implicit(
  const value: TEnumerable<T>): Enumerable<T>;
begin
  Result.source := value;
end;

function Enumerable<T>.Max: T;
var
  default: IComparer<T>;
  x, y: T;
  flag: Boolean;
begin
  if not Assigned(source) then
    raise EArgumentNilException.Create('Source');
  default := TComparer<T>.Default;

  flag := False;
  for x in source do
  begin
    if flag then
    begin
      if default.Compare(x, y) > 0 then
        y := x;
    end
    else
    begin
      y := x;
      flag := True;
    end;
  end;
  if flag then
    Result := y
  else
    raise EListError.Create('source is empty');
end;

Код в основном является портом метода расширения .Net Enumerable.Max<T> из System.Linq. Вы можете использовать его так же, как в примере Nicks.

Интересная вещь для тех, кого интересует их размер двоичного файла: компоновщик может удалять методы, которые никогда не используются.

person Stefan Glienke    schedule 18.01.2013