На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Пожалуйста, выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как "свернуть" программу в трей.
3. Как "скрыться" от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как прочитать список файлов, поддиректорий в директории?
5. Как запустить программу/файл?
... (продолжение следует) ...

Вопросы, подробно описанные во встроенной справочной системе Delphi, не несут полезной тематической нагрузки, поэтому будут удаляться.
Запрещается создавать темы с просьбой выполнить какую-то работу за автора темы. Форум является средством общения и общего поиска решения. Вашу работу за Вас никто выполнять не будет.


Внимание
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка - 60 дней. Последующие попытки бан.
Мат в разделе - бан на три месяца...
Модераторы: jack128, D[u]fa, Shaggy, Rouse_
  
> Spring4D , Функциональное программирование в действии
    Spring4D IEnumerable<T> in action :D
    Использование методов TEnumerable.
    ExpandedWrap disabled
      uses
        Spring.Collections;
       
      type
        TPetOwner = class
          Name: string;
          Pets: IList<string>;
          constructor Create(AName: string; APets: IList<string>);
        end;
       
      { TPetsOwner }
       
      constructor TPetOwner.Create(AName: string; APets: IList<string>);
      begin
        Name:= AName; Pets:= APets;
      end;
       
      begin
        var PetOwners := TCollections.CreateList<TPetOwner>([
            TPetOwner.Create('Higa, Sidney',
              TCollections.CreateList<string>(['Scruffy', 'Sam'])),
            TPetOwner.Create('Ashkenazi, Ronen',
              TCollections.CreateList<string>(['Walker', 'Sugar'])),
            TPetOwner.Create('Price, Vernette',
              TCollections.CreateList<string>(['Scratches', 'Diesel']))
          ]);
        // Query using SelectMany.
        var Query1 := TEnumerable.SelectMany<TPetOwner, string>(
          PetOwners, function (petOwner: TPetOwner): IEnumerable<string>
              begin
                Result:= petOwner.Pets;
              end);
       
        Writeln('Using SelectMany:');
        for var Pet in Query1 do
          Writeln(Pet);
        Writeln;
       
        // This code shows how to use Select
        // instead of SelectMany.
        var Query2:= TEnumerable.Select<TPetOwner, IEnumerable<string>>(
          PetOwners, function (petOwner: TPetOwner): IEnumerable<string>
            begin
              Result:= petOwner.Pets;
            end);
       
        Writeln('Using Select:');
        for var petList in Query2 do
          for var pet in petList do
            Writeln(pet);
       
        Readln;
      end.

    все примеры с сайта https://docs.microsoft.com/en-us/dotnet/api...framework-4.7.2
    Там на шарпе может кто не шарит, а спринг хоца юзат ну вот я вам и переведу :D

    To be continue...

    ПС. Delphi 10.3.1 Rio, Spring4D 1.2.2.
    Все примеры рабочие.
    Сообщение отредактировано: Cfon -
      Более сложный пример с SelectMany, поскольку пришлось расширять класс TEnumerable и в SelectMany идет дополнительный вызов Select, так что не сразу понять как все это работает.
      Атеншен! Хитросплетения анонимок могет взорвать моск! :ph34r:
      Думаю если сделать три вложения анонимок, то можно растаца с разумом :lool:
      ExpandedWrap disabled
        uses
          System.SysUtils
        , Spring.Collections
        , Spring.Collections.Extensions
        ;
         
        type
          TPetOwner = class
            Name: string;
            Pets: IList<string>;
            constructor Create(AName: string; APets: IList<string>);
          end;
         
          TEnumerableHelper = class helper for TEnumerable
            class function SelectMany<T, TResult>(const source: IEnumerable<T>;
              const selector: TFunc<T, Integer, IEnumerable<TResult>>): IEnumerable<TResult>; overload; static;
          end;
         
        { TPetsOwner }
         
        constructor TPetOwner.Create(AName: string; APets: IList<string>);
        begin
          Name:= AName; Pets:= APets;
        end;
         
        { TEnumerableHelper }
         
        class function TEnumerableHelper.SelectMany<T, TResult>(const source: IEnumerable<T>;
          const selector: TFunc<T, Integer, IEnumerable<TResult>>): IEnumerable<TResult>;
        begin
           Result := TSelectManyIndexIterator<T, TResult>.Create(source, selector);
        end;
         
        begin
          var PetOwners := TCollections.CreateList<TPetOwner>([
              TPetOwner.Create('Higa, Sidney',
                TCollections.CreateList<string>(['Scruffy', 'Sam'])),
              TPetOwner.Create('Ashkenazi, Ronen',
                TCollections.CreateList<string>(['Walker', 'Sugar'])),
              TPetOwner.Create('Price, Vernette',
                TCollections.CreateList<string>(['Scratches', 'Diesel'])),
              TPetOwner.Create('Hines, Patrick',
                TCollections.CreateList<string>(['Dusty']))
            ]);
         
          // Query using SelectMany with Index.
          var Query := TEnumerable.SelectMany<TPetOwner, string>(PetOwners,
                function (petOwner: TPetOwner; Index: integer): IEnumerable<string>
                begin
                  Result:= TEnumerable.Select<string, string>(petOwner.Pets,
                    function (pet: string): string
                    begin
                      Result:= Index.ToString + pet;
                    end);
                end);
         
          Writeln('Using SelectMany with Index:');
          for var pet in Query do
            Writeln(pet);
         
          Readln;
        end.
      Сообщение отредактировано: Cfon -
        А теперь дельфийски хард-кор! :D
        Очень сложный пример SelectMany тока для хард-кодеров! :wizard:
        ExpandedWrap disabled
          uses
            System.SysUtils,
            Spring.Collections,
            Spring.Collections.Extensions;
           
          type
            TEnumerableHelper = class helper for TEnumerable
              ....
              class function SelectMany<T, C, R>(const source: IEnumerable<T>;
                const collectionSelector: TFunc<T, IEnumerable<C>>;
                const resultSelector: TFunc<T, C, R>): IEnumerable<R>; overload; static;
            end;
           
            TPetOwner = record
              Name: string;
              Pets: IList<string>;  
              constructor Create(AName: string; APets: IList<string>);
            end;
           
            TOwnerAndPet = record
              owner: TPetOwner;
              petName: string;
              constructor Create(AOwner: TPetOwner; APetName: string);
            end;
           
            TOwnerNameAndPetName = record
              ownerName: string;
              petName: string;
              constructor Create(AOwnerName: string; APetName: string);
            end;
           
          { TEnumerableHelper }
           
          class function TEnumerableHelper.SelectMany<T, C, R>(const source: IEnumerable<T>;
            const collectionSelector: TFunc<T, IEnumerable<C>>;
            const resultSelector: TFunc<T, C, R>): IEnumerable<R>;
          begin
            Result:= TSelectManyIterator<T, C, R>.Create(source,
              collectionSelector,
              resultSelector);
          end;
           
          { TPetOwner }
           
          constructor TPetOwner.Create(AName: string; APets: IList<string>);
          begin
            Name:= AName;
            Pets:= APets;
          end;
           
          { TOwnerAndPet }
           
          constructor TOwnerAndPet.Create(AOwner: TPetOwner; APetName: string);
          begin
            owner:= AOwner;
            petName:= APetName;
          end;
           
          { TOwnerNameAndPetName }
           
          constructor TOwnerNameAndPetName.Create(AOwnerName, APetName: string);
          begin
            ownerName:= AOwnerName;
            petName:= APetName;
          end;
           
          begin
            var PetOwners := TCollections.CreateList<TPetOwner>([
                TPetOwner.Create('Higa',
                  TCollections.CreateList<string>(['Scruffy', 'Sam'])),
                TPetOwner.Create('Ashkenazi',
                  TCollections.CreateList<string>(['Walker', 'Sugar'])),
                TPetOwner.Create('Price',
                  TCollections.CreateList<string>(['Scratches', 'Diesel'])),
                TPetOwner.Create('Hines',
                  TCollections.CreateList<string>(['Dusty']))
              ]);
           
            // Project the pet owner's name and the pet's name.
            var Query := TEnumerable
              .SelectMany<TPetOwner, string, TOwnerAndPet>(PetOwners,
                function (petOwner: TPetOwner): IEnumerable<string>
                begin
                  Result:= petOwner.Pets;
                end,
                function (owner: TPetOwner; petName: string): TOwnerAndPet
                begin
                  Result:= TOwnerAndPet.Create(owner, petName);
                end)
              .Where(function (const ownerAndPet: TOwnerAndPet): Boolean
                begin
                  Result:= ownerAndPet.petName.StartsWith('S');
                end);
           
              var Query2:= TEnumerable.Select<TOwnerAndPet, TOwnerNameAndPetName>(Query,
                function (ownerAndPet: TOwnerAndPet): TOwnerNameAndPetName
                begin
                  Result:= TOwnerNameAndPetName.Create(
                    ownerAndPet.owner.Name, ownerAndPet.petName);
                end);
           
            // Print the results.
            for var obj in Query2 do
            begin
              Writeln(Format('{Owner=%s, Pet=%s}', [obj.ownerName, obj.petName]));
            end;
          end;
           
          // This code produces the following output:
          //
          // {Owner=Higa, Pet=Scruffy}
          // {Owner=Higa, Pet=Sam}
          // {Owner=Ashkenazi, Pet=Sugar}
          // {Owner=Price, Pet=Scratches}
           
          end.

        Переводя шарповый код на дельфи столкнулся с необходимостью определять запись на каждый чих, это очень утомляет :D
        Сравните код Делфи и Шарп:
        ExpandedWrap disabled
          var query = petOwners
              .SelectMany(petOwner => petOwner.Pets, (petOwner, petName) => new { petOwner, petName })
              .Where(ownerAndPet => ownerAndPet.petName.StartsWith("S"))
              .Select(ownerAndPet =>
                      new
                      {
                          Owner = ownerAndPet.petOwner.Name,
                          Pet = ownerAndPet.petName
                      }
              );

        Разница большая. на шарпе выглядит почти как если кодить на JS.
        Мой вердикт пока Делфи не сократит синтаксис до варика C# писать в функциональном стиле в нем это жесть! :D
        ПС. Надо хотя бы ввести вывод типов дженерик функций, чтобы писать типо:
        ExpandedWrap disabled
          var Query2:= TEnumerable.Select(Query,
                function (ownerAndPet: TOwnerAndPet): TOwnerNameAndPetName
                begin
                  Result:= TOwnerNameAndPetName.Create(
                    ownerAndPet.owner.Name, ownerAndPet.petName);
                end);

        и то короче, понимаю что убрать типизацию параметров нельзя это ж Делфи, а не JS хотя в шарпе ж в анонимках убрали както :D
        или шарп тоже стал динамическим?
        Сообщение отредактировано: Cfon -
          Ты Linq что ли ваяешь?
            Цитата Cfon @
            или шарп тоже стал динамическим?

            нет, просто в шарпе работает вывод типов в том числе и для generic параметров и для много чего.
              Цитата Fr0sT @
              Ты Linq что ли ваяешь?

              Изучаю Spring4D
              Цитата jack128 @
              Цитата Cfon @
              или шарп тоже стал динамическим?

              нет, просто в шарпе работает вывод типов в том числе и для generic параметров и для много чего.

              Хорошо б в делфи тоже сделать. А то капец как муторно исправлять ошибки ввода типов в двух а то и более местах :D
              Сообщение отредактировано: Cfon -
                ОК, немного базовых операций со Spring.Collections
                ExpandedWrap disabled
                  uses
                    System.SysUtils,
                    Spring.Collections;
                   
                  begin
                    // диапазон целых чисел от 1 до 1000
                    var numbers: IEnumerable<integer> := TEnumerable.Range(1, 1000);
                    // перемешиваем
                    var shuffled:= numbers.Shuffled;
                    // отфильтровыем четные числа
                    var evens:= shuffled.Where(function (const i: integer): boolean
                      begin
                        Result:= not Odd(i);
                      end);
                    // берем первые десять чисел
                    var firstTen:= evens.Take(10);
                    // реверсируем
                    var reversed:= firstTen.Reversed;
                    // извлекаем корень
                    var sqrts:= TEnumerable.Select<integer, extended>(reversed,
                      function (i: integer): extended
                      begin
                        Result:= sqrt(i);
                      end);
                    // выводим на консоль результат
                    sqrts.ForEach(procedure (const x: extended)
                      begin
                        Writeln(Format('Square root: %6.2f', [x]));
                      end);
                  end.
                  Упрощаем код заюзав модуль Spring.Collections.Enumerable:
                  ExpandedWrap disabled
                    uses
                      System.SysUtils,
                      Spring.Collections,
                      Spring.Collections.Enumerable;
                     
                    type
                      TPetOwner = class
                        Name: string;
                        Pets: IList<string>;
                        constructor Create(AName: string; APets: IList<string>);    
                      end;
                     
                    { TPetOwner }
                     
                    constructor TPetOwner.Create(AName: string; APets: IList<string>);
                    begin
                      Name:= AName;
                      Pets:= APets;
                    end;
                     
                    begin
                      var PetOwners := Enumerable<TPetOwner>.Create([
                         TPetOwner.Create('Higa, Sidney',
                           TCollections.CreateList<string>(['Scruffy', 'Sam'])),
                         TPetOwner.Create('Ashkenazi, Ronen',
                           TCollections.CreateList<string>(['Walker', 'Sugar'])),
                         TPetOwner.Create('Price, Vernette',
                           TCollections.CreateList<string>(['Scratches', 'Diesel']))
                       ]);
                     
                       // Query using SelectMany.
                       var Query1 := PetOwners.SelectMany<string>(
                         function (petOwner: TPetOwner): IEnumerable<string>
                         begin
                           Result:= petOwner.Pets;
                         end);
                     
                       Writeln('Using SelectMany:');
                       for var Pet in Query1 do
                         Writeln(Pet);
                       Writeln;
                     
                       // This code shows how to use Select
                       // instead of SelectMany.
                       var Query2:= PetOwners.Select<IEnumerable<string>>(
                         function (petOwner: TPetOwner): IEnumerable<string>
                         begin
                           Result:= petOwner.Pets;
                         end);
                     
                       Writeln('Using Select:');
                       for var petList in Query2 do
                         for var pet in petList do
                           Writeln(pet);
                       Writeln;  
                    end;
                     
                    end.

                  Засчет чего код упростился?
                  Все вызовы Select|SelectMany идут через PetOwners, он был вынесен из списка параметров, в отличие от TEnumerable, засчет чего теперь не надо передавать тип TPetOwner в качестве параметров шаблона. Да и в модуле Spring.Collections.Enumerable есть реализации для всех методов IEnumerable<T>, тогда как TEnumerable реализует например не все методы Select|SelectMany и мне пришлось самому писать их, хотя на самом деле добавить их не составило труда :D
                  Сравните вызов SelectMany из первого поста:
                  ExpandedWrap disabled
                    var Query1 := TEnumerable.SelectMany<TPetOwner, string>(
                      PetOwners, function (petOwner: TPetOwner): IEnumerable<string>
                         begin
                           Result:= petOwner.Pets;
                         end);

                  и новый
                  ExpandedWrap disabled
                    var Query1 := PetOwners.SelectMany<string>(
                         function (petOwner: TPetOwner): IEnumerable<string>
                         begin
                           Result:= petOwner.Pets;
                         end);

                  лучше не так ли?
                  Сообщение отредактировано: Cfon -
                    Продолжаю упрощать :D
                    Попытался переложить пример из поста #3 на рельсы Enumerable, но получаю ошибку, причем хз откуда она вылазит, дебугер стопица на асм-коде, я там не шарю не стал мучать себя поисками :D
                    Да на самом деле я сделал еще не большое упрощение кода, удалил последний Select, и сразу возращал массив TOwnerNameAndPetName. Вот тогда и стала вызалить ошибка :D, если переносить один в один то все пучком! :wacko:
                    ExpandedWrap disabled
                      uses
                        System.SysUtils,
                        Spring.Collections,
                        Spring.Collections.Enumerable;
                       
                      type
                        TOwnerNameAndPetName = record
                          ownerName: string;
                          petName: string;
                          constructor Create(AOwnerName: string; APetName: string);
                        end;
                       
                      { TOwnerNameAndPetName }
                       
                      constructor TOwnerNameAndPetName.Create(AOwnerName, APetName: string);
                      begin
                        ownerName:= AOwnerName;
                        petName:= APetName;
                      end;
                       
                      begin
                        var PetOwners := Enumerable<TPetOwner>.Create([
                          TPetOwner.Create('Higa',
                            TCollections.CreateList<string>(['Scruffy', 'Sam'])),
                          TPetOwner.Create('Ashkenazi',
                            TCollections.CreateList<string>(['Walker', 'Sugar'])),
                          TPetOwner.Create('Price',
                            TCollections.CreateList<string>(['Scratches', 'Diesel'])),
                          TPetOwner.Create('Hines',
                            TCollections.CreateList<string>(['Dusty']))
                        ]);
                       
                        var Query := PetOwners
                          .SelectMany<string, TOwnerNameAndPetName>(
                            function (owner: TPetOwner): IEnumerable<string>
                            begin
                              Result:= owner.Pets;
                            end,
                            function (owner: TPetOwner; petName: string): TOwnerNameAndPetName
                            begin
                              Result:= TOwnerNameAndPetName.Create(owner.Name, petName);
                            end)
                          .Where(function (const ownerAndPet: TOwnerNameAndPetName): Boolean
                            begin
                              Result:= ownerAndPet.petName.StartsWith('S');
                            end);
                       
                         // тут был Select и его удалил и теперь ошибка
                       
                        // Print the results.
                        for var obj in Query do
                        begin
                          Writeln(Format('{Owner=%s, Pet=%s}', [obj.ownerName, obj.petName]));
                        end;
                      end.

                    Забыл сказать что все компилица, ошибка рантайм.
                    Если заменить запись TOwnerNameAndPetName на TOwnerAndPet, то все пучком :wall:
                    ExpandedWrap disabled
                      var Query := PetOwners
                          .SelectMany<string, TOwnerAndPet>(
                            function (owner: TPetOwner): IEnumerable<string>
                            begin
                              Result:= owner.Pets;
                            end,
                            function (owner: TPetOwner; petName: string): TOwnerAndPet
                            begin
                              Result:= TOwnerAndPet.Create(owner, petName);
                            end)
                          .Where(function (const ownerAndPet: TOwnerAndPet): Boolean
                            begin
                              Result:= ownerAndPet.petName.StartsWith('S');
                            end);
                       
                        // Print the results.
                        for var obj in Query do
                        begin
                          Writeln(Format('{Owner=%s, Pet=%s}', [obj.owner.Name, obj.petName]));
                        end;

                    Ошибка в функции System._UStrAsg на асма-строке:
                    LOCK DEC [EDX-skew].StrRec.refCnt
                    че она делает я хз :blink:
                    Я так понел что какие то траблы в работе со строками.

                    Опа а если сделать копию PetOwners, то пашет! :wall:
                    ExpandedWrap disabled
                      var Query := Enumerable<TPetOwner>.Create(PetOwners.ToArray)
                          .SelectMany<string, TOwnerNameAndPetName>(
                            function (owner: TPetOwner): IEnumerable<string>
                            begin
                              Result:= owner.Pets;
                            end,
                            function (owner: TPetOwner; petName: string): TOwnerNameAndPetName
                            begin
                              Result:= TOwnerNameAndPetName.Create(owner.Name, petName);
                            end)
                          .Where(function (const ownerAndPet: TOwnerNameAndPetName): Boolean
                            begin
                              Result:= ownerAndPet.petName.StartsWith('S');
                            end)
                            ;
                    Сообщение отредактировано: Cfon -
                      Дело было в инлайн переменной obj в цикле вывода
                      ExpandedWrap disabled
                        .....
                          // Print the results.
                          for var obj in Query do
                          begin
                            Writeln(Format('{Owner=%s, Pet=%s}', [obj.ownerName, obj.petName]));
                          end;

                      переместил ее в раздел объявлений и все! :D
                      Гениально! :lool:
                      ExpandedWrap disabled
                        var
                          obj: TOwnerNameAndPetName;
                        begin
                        ...
                          // Print the results.
                          for obj in Query do
                          begin
                            Writeln(Format('{Owner=%s, Pet=%s}', [obj.ownerName, obj.petName]));
                          end;
                        end.

                      Как я обнаружил это? Все просто я установил Delphi XE2 ну ту что еще Spring4D поддерживает и так как там нет инлайна я перенес их в раздел объявлений, ну и все вдруг заработало :D
                      Почему она вылазила тока при замене TOwnerAndPet на TOwnerNameAndPetName я хз, пока не интересно :D
                      Не стабильно пока работает инлайнинг переменных :whistle:
                      ПС. другие инлайны не влияли на ошибку.
                      Сообщение отредактировано: Cfon -
                        Немного подробностей о Spring.Collections:

                        ExpandedWrap disabled
                          IEnumerable<T> = interface(IEnumerable)
                          ICollection<T> = interface(IEnumerable<T>)
                          IList<T> = interface(ICollection<T>)
                          ...
                          IMap<TKey, TValue> = interface(ICollection<TPair<TKey, TValue>>
                          IDictionary<TKey, TValue> = interface(IMap<TKey, TValue>)
                           
                          TCollections = class
                          TEnumerable = class


                        Отдельно стоит Spring.Collections.Enumerable где определен один элемент:
                        ExpandedWrap disabled
                          Enumerable = record

                        TCollections реализует статические методы создания различных структур данных, интерфейсы которых определны выше.
                        TEnumerable реализует стат. методы обработки этих данных.
                        Enumerable тоже что и TEnumerable, но тока больше :D

                        Теперь некоторые особености реализации моих примеров, например в коде из поста #9 я создаю PetOwners как Enumerable<TPetOwner>, почему?
                        Потому что так проще заюзать SelectMany, конечно Enumerable<TPetOwner> это не полноценая структура данных таких как TList и другие, но нам их функциональность была не нужена в прмерах, смотрите как это выглядит через TCollections.CreateList:
                        ExpandedWrap disabled
                          // список
                            var PetOwners := TCollections.CreateList<TPetOwner>([
                              TPetOwner.Create('Higa',
                                TCollections.CreateList<string>(['Scruffy', 'Sam'])),
                              TPetOwner.Create('Ashkenazi',
                                TCollections.CreateList<string>(['Walker', 'Sugar'])),
                              TPetOwner.Create('Price',
                                TCollections.CreateList<string>(['Scratches', 'Diesel'])),
                              TPetOwner.Create('Hines',
                                TCollections.CreateList<string>(['Dusty']))
                            ]);
                            // запрос
                            var Query := Enumerable<TPetOwner>.Create(PetOwners.ToArray) //<-- создаем
                              .SelectMany<string>(function(owner: TPetOwner): IEnumerable<string>
                                begin
                                  Result:= owner.Pets;
                                end);

                        Один фиг пришлось создать Enumerable<TPetOwner>, чтобы выполнить SelectMany запрос. Сравните
                        ExpandedWrap disabled
                           var PetOwners := Enumerable<TPetOwner>.Create([
                              TPetOwner.Create('Higa',
                                TCollections.CreateList<string>(['Scruffy', 'Sam'])),
                              TPetOwner.Create('Ashkenazi',
                                TCollections.CreateList<string>(['Walker', 'Sugar'])),
                              TPetOwner.Create('Price',
                                TCollections.CreateList<string>(['Scratches', 'Diesel'])),
                              TPetOwner.Create('Hines',
                                TCollections.CreateList<string>(['Dusty']))
                            ]);
                           
                            var Query := PetOwners.SelectMany<string>(
                                function (owner: TPetOwner): IEnumerable<string>
                                begin
                                  Result:= owner.Pets;
                                end);

                        Проще не так ли?
                        Ну а если понадобится функции например списка? Нет проблем братишка это изиж зерь сюда :D
                        ExpandedWrap disabled
                          var listPetOwners := PetOwners.ToList;

                        Все дальше работаем как со списком :D
                        - Хэ а где try finally Free чувачок ах? мемори лик не?
                        Не братэлу тут у нас интерфесное прогание без всяких Free!
                        - Как так?
                        А вот так компиль сам все контролит и когда надо делитит мусор :D
                        - Фигасе! Клево!
                        А то! Интерморда рулит! :lool:

                        Упс я чутка поспешил :D
                        Объекты что хранит Enumerable не удаляются ибо они созданы не через механизм интерфейса, поэтому да их надо ручками удалять например
                        ExpandedWrap disabled
                          for var ob in PetOwners do
                          begin
                            ob.Free;
                          end;

                        или чтобы савсем не видет циклы то так :D
                        ExpandedWrap disabled
                          PetOwners.ForEach(procedure (const obj: TPetOwner)
                            begin
                              obj.Free;
                            end);

                        Ну или юзать методы типа TCollections.CreateObjectList, они по умолчанию сами делитят свое содержимое.

                        А можно заюзать интерфес IPetOwner для TPetOwner например
                        ExpandedWrap disabled
                          type
                            IPetOwner = interface
                              function GetName: string;
                              procedure SetName(AName: string);
                              function GetPets: IList<string>;
                              property Name: string read GetName write SetName;
                              property Pets: IList<string> read GetPets;
                            end;
                           
                            TPetOwner = class(TInterfacedObject, IPetOwner)
                            private
                              FName: string;
                              FPets: IList<string>;
                              function GetName: string;
                              procedure SetName(AName: string);
                              function GetPets: IList<string>;
                            public
                              constructor Create(AName: string; APets: IList<string>);
                              property Name: string read GetName write SetName;
                              property Pets: IList<string> read GetPets;
                            end;

                        Там по коду тоже надо заменить TPetOwner на IPetOwner и все вручную уже не надо удалять :D
                        Или юзать запись вместо класса их тоже не надо удалять :lool:

                        Да и еще одна особеность примеров любое присваивание переменной выполняется один раз! Зачем?
                        ну это принципы функционального программирования, данные считаются неизмеными воооот... :D
                        Вот и я для каждого нового состояния юзал отдельную перемнную.
                        Сообщение отредактировано: Cfon -
                          Давайте закрепим тему :D
                          - Вывести список имен владельцев
                          - Вывести список имен владельцев и количество домашних животных
                          Это простые запросы, но они позволят нам понять как работает Select.
                          Данные те же список TPetOwner:
                          ExpandedWrap disabled
                            PetOwners := TCollections.CreateList<TPetOwner>([
                                TPetOwner.Create('Higa', TCollections.CreateList<string>(['Scruffy', 'Sam'])),
                                TPetOwner.Create('Ashkenazi', TCollections.CreateList<string>(['Walker', 'Sugar'])),
                                TPetOwner.Create('Price', TCollections.CreateList<string>(['Scratches', 'Diesel'])),
                                TPetOwner.Create('Hines', TCollections.CreateList<string>(['Dusty']))
                              ]);
                          Сообщение отредактировано: Cfon -
                            Цитата Cfon @
                            Дело было в инлайн переменной obj в цикле вывода
                            переместил ее в раздел объявлений и все! :D
                            Гениально! :lool:
                            ExpandedWrap disabled
                              var
                                obj: TOwnerNameAndPetName;
                              begin
                              ...
                                // Print the results.
                                for obj in Query do
                                begin
                                  Writeln(Format('{Owner=%s, Pet=%s}', [obj.ownerName, obj.petName]));
                                end;
                              end.

                            Обнаружил еще решения данного косяка:
                            1- указать тип переменной obj в цикле.
                            2- вместо класса TPetOwner юзать рекорд.
                            ExpandedWrap disabled
                              type
                                TPetOwner = record
                                  Name: string;
                                  Pets: IList<string>;
                                  constructor Create(AName: string; APets: IList<string>);
                                end;
                               
                              .....
                               
                              begin
                              .....
                                // Print the results.
                                for var obj: TOwnerNameAndPetName in Query do
                                begin
                                  Writeln(Format('{Owner=%s, Pet=%s}', [obj.ownerName, obj.petName]));
                                end;
                              end


                            Добавлено
                            Цитата Cfon @
                            Давайте закрепим тему :D
                            - Вывести список имен владельцев
                            - Вывести список имен владельцев и количество домашних животных
                            Это простые запросы, но они позволят нам понять как работает Select.

                            Решение :D
                            ExpandedWrap disabled
                              begin
                                var PetOwners := Enumerable<TPetOwner>.Create([
                                  TPetOwner.Create('Higa', TCollections.CreateList<string>(['Scruffy', 'Sam'])),
                                  TPetOwner.Create('Ashkenazi',TCollections.CreateList<string>(['Walker', 'Sugar'])),
                                  TPetOwner.Create('Price', TCollections.CreateList<string>(['Scratches', 'Diesel'])),
                                  TPetOwner.Create('Hines', TCollections.CreateList<string>(['Dusty']))
                                ]);
                               
                                var Query:= PetOwners.Select<string>(
                                  function (owner: TPetOwner): string
                                  begin
                                    Result:= owner.Name;
                                  end);
                               
                                var Query2:= PetOwners.Select<TOwnerNameAndPetCount>(
                                  function (owner: TPetOwner): TOwnerNameAndPetCount
                                  begin
                                    Result:= TOwnerNameAndPetCount.Create(owner.Name, owner.Pets.Count);
                                  end);
                              end;
                            Сообщение отредактировано: Cfon -
                              Уфф чет я завис на селектах :D
                              пора продолжать итак пишем Aggregate это аналог Reduce из JS, на сайте С# указаном выше, даны три примера, я их все сразу переложил на делфи:
                              ExpandedWrap disabled
                                uses
                                  System.SysUtils,
                                  Spring.Collections,
                                  Spring.Collections.Enumerable;
                                 
                                procedure AggregateEx1;
                                begin
                                  var ints := TArray<integer>.Create(4, 8, 8, 3, 9, 0, 7, 8, 2);
                                 
                                  // Count the even numbers in the array, using a seed value of 0.
                                  var numEven:= Enumerable<integer>.Create(ints).Aggregate<integer>(0,
                                    function(total, next: integer): integer
                                    begin
                                      Result:= total;
                                      if next mod 2 = 0 then Result:= total + 1;
                                    end);
                                 
                                  Writeln('The number of even integers is: ', numEven);
                                end;
                                 
                                {$REGION 'C# code'}
                                //  int[] ints = { 4, 8, 8, 3, 9, 0, 7, 8, 2 };
                                //
                                //  // Count the even numbers in the array, using a seed value of 0.
                                //  int numEven = ints.Aggregate(0, (total, next) =>
                                //                                      next % 2 == 0 ? total + 1 : total);
                                //
                                //  Console.WriteLine("The number of even integers is: {0}", numEven);
                                {$ENDREGION}
                                // This code produces the following output:
                                //
                                // The number of even integers is: 6
                                 
                                procedure AggregateEx2;
                                begin
                                  var sentence := 'the quick brown fox jumps over the lazy dog';
                                 
                                  // Split the string into individual.
                                  var words:= TCollections.CreateList<string>(sentence.Split([' ']));
                                 
                                  // Prepend each word to the beginning of the
                                  // new sentence to reverse the word order.
                                  var reversed:= words.Aggregate(
                                    function (workingSentence, next: string): string
                                    begin
                                      Result:= next + ' ' + workingSentence;
                                    end
                                  );
                                 
                                  Writeln(reversed);
                                end;
                                 
                                {$REGION 'C# code'}
                                // string sentence = "the quick brown fox jumps over the lazy dog";
                                //
                                // // Split the string into individual words.
                                // string[] words = sentence.Split(' ');
                                //
                                // // Prepend each word to the beginning of the
                                // // new sentence to reverse the word order.
                                // string reversed = words.Aggregate((workingSentence, next) =>
                                //                                      next + " " + workingSentence);
                                //
                                // Console.WriteLine(reversed);
                                {$ENDREGION}
                                // This code produces the following output:
                                //
                                // dog lazy the over jumps fox brown quick the
                                 
                                procedure AggregateEx3;
                                begin
                                  var fruits:= TArray<string>.Create('apple', 'mango', 'orange', 'passionfruit', 'grape');
                                 
                                  // Determine whether any string in the array is longer than "banana".
                                  var longestName:= Enumerable<string>.Create(fruits)
                                    .Aggregate<string, string>('banana',
                                      function (longest, next:string): string
                                      begin
                                        Result:= longest;
                                        if Length(next) > Length(longest) then
                                          Result:= next;
                                      end,
                                      // Return the final result as an upper case string.
                                      function (fruit: string): string
                                      begin
                                        Result:= fruit.ToUpper;
                                      end);
                                 
                                  Writeln('The fruit with the longest name is ', longestName);
                                end;
                                 
                                {$REGION 'C# code'}
                                // string[] fruits = { "apple", "mango", "orange", "passionfruit", "grape" };
                                //
                                // // Determine whether any string in the array is longer than "banana".
                                // string longestName =
                                //    fruits.Aggregate("banana",
                                //                    (longest, next) =>
                                //                        next.Length > longest.Length ? next : longest,
                                //    // Return the final result as an upper case string.
                                //                    fruit => fruit.ToUpper());
                                //
                                // Console.WriteLine(
                                //    "The fruit with the longest name is {0}.",
                                //    longestName);
                                {$ENDREGION}
                                // This code produces the following output:
                                //
                                // The fruit with the longest name is PASSIONFRUIT.
                                 
                                begin
                                  AggregateEx1;
                                  AggregateEx2;
                                  AggregateEx3;
                                end;
                                 
                                end.

                              Я также привел код на шарпе, сравнивайте.
                                Задание! :whistle:
                                Надо перенести на Delphi. Кто не понел это код JS :D
                                ExpandedWrap disabled
                                  const colors = ["red", "red", "green", "blue", "green"];
                                  const distinctColors = colors.reduce(
                                    (distinct, color) =>
                                      (distinct.indexOf(color) !== -1) ?
                                        distinct :
                                        [...distinct, color],
                                  []);
                                   
                                  console.log(distinctColors);
                                  // ["red", "green", "blue"]
                                  До этого мы юзали даные вшитые в код, давайте расширим горизонты :D
                                  Пример Spring4D с данными из JSON файла, большую часть примера занял код парсинга JSON :D
                                  ExpandedWrap disabled
                                    uses
                                      System.Classes, System.SysUtils, System.Generics.Collections, System.JSON,
                                      Spring.Collections, Spring.Collections.Enumerable;
                                     
                                    type
                                      TIngredient = record
                                        IngredientName: string;
                                        Amount: Single;
                                        Measurement: string;
                                        constructor Create(AIngredientName: string; AAmount: Single; AMeasurement: string);
                                      end;
                                     
                                      TRecipe = record
                                        Id: integer;
                                        RecipeName: string;
                                        Ingredients: IList<TIngredient>;
                                        Steps: IList<string>;
                                        constructor Create(AId: integer; ARecipeName: string;
                                          AIngredients: IList<TIngredient>; ASteps: IList<string>);
                                      end;
                                     
                                    { TRecipe }
                                     
                                    constructor TRecipe.Create(AId: integer; ARecipeName: string;
                                      AIngredients: IList<TIngredient>; ASteps: IList<string>);
                                    begin
                                      Id:= AId;
                                      RecipeName:= ARecipeName;
                                      Ingredients:= AIngredients;
                                      Steps:= ASteps;
                                    end;
                                     
                                    { TIngredient }
                                     
                                    constructor TIngredient.Create(AIngredientName: string; AAmount: Single;
                                      AMeasurement: string);
                                    begin
                                      IngredientName:= AIngredientName;
                                      Amount:= AAmount;
                                      Measurement:= AMeasurement;
                                    end;
                                     
                                    function LoadFile(fileName: string): string;
                                    begin
                                      var reader:= TStreamReader.Create(fileName);
                                      try
                                        Result:= reader.ReadToEnd;
                                      finally
                                        reader.Free;
                                      end;
                                    end;
                                     
                                    function CreateRecipes(json: string): IList<TRecipe>;
                                    begin
                                      Result:= TCollections.CreateList<TRecipe>;
                                      try
                                        var Root: TJSONValue := TJSONObject.ParseJSONValue(json);
                                        var vRecipes := Root.GetValue<TJSONArray>('recipes');
                                        try
                                          for var vRecipe in vRecipes do
                                          begin
                                            var id:= vRecipe.GetValue<integer>('id');
                                            var name:= vRecipe.GetValue<string>('name');
                                            var vIngrs := vRecipe.GetValue<TJSONArray>('ingredients');
                                            var vSteps := vRecipe.GetValue<TJSONArray>('steps');
                                            var ingredients:= TCollections.CreateList<TIngredient>;
                                            var steps:= TCollections.CreateList<string>;
                                     
                                            for var vIngr in vIngrs do
                                            begin
                                              var ingr:= TIngredient.Create(
                                                vIngr.GetValue<string>('name'),
                                                vIngr.GetValue<Single>('amount'),
                                                vIngr.GetValue<string>('measurement')
                                              );
                                              ingredients.Add(ingr);
                                            end;
                                     
                                            for var vStep in vSteps do
                                            begin
                                              steps.Add(vStep.Value);
                                            end;
                                     
                                            Result.Add(TRecipe.Create(id, name, ingredients, steps));
                                          end;
                                        finally
                                          vRecipes.Free;
                                        end;
                                      except
                                        on E: EJSONException do
                                          Writeln('Error parsing JSON. Check file format.');
                                      end;
                                    end;
                                     
                                    begin
                                      // load json file.
                                      var s:= LoadFile('recipes.json');
                                     
                                      // create recipes list.
                                      var recipes: IList<TRecipe> := CreateRecipes(s);
                                     
                                      // query: recipe name list
                                      var query := TEnumerable.Select<TRecipe, string>(recipes,
                                        function (recipe: TRecipe): string
                                        begin
                                          Result:= recipe.RecipeName;
                                        end
                                      );
                                     
                                      // print the results.
                                      query.ForEach(procedure (const s: string)
                                      begin
                                        Writeln(s);
                                      end);
                                    end.

                                  часть recipe.json:
                                  ExpandedWrap disabled
                                    {"recipes": [{
                                      "id": 1,
                                      "name": "Baked Salmon",
                                      "ingredients": [{
                                        "name": "Salmon",
                                        "amount": 1,
                                        "measurement": "lb"
                                      }, {
                                        "name": "Pine Nuts",
                                        "amount": 1,
                                        "measurement": "cup"
                                      }, {
                                        "name": "Butter Lettuce",
                                        "amount": 2,
                                        "measurement": "cups"
                                      }, {
                                        "name": "Yellow Squash",
                                        "amount": 1,
                                        "measurement": "med"
                                      }, {
                                        "name": "Olive Oil",
                                        "amount": 0.5,
                                        "measurement": "cup"
                                      }, {
                                        "name": "Garlic",
                                        "amount": 3,
                                        "measurement": "cloves"
                                      }],
                                      "steps": [
                                        "Preheat the oven to 350 degrees",
                                        "Spread the olive oil around a glass baking dish.",
                                        "Add the salmon, garlic, and pine nuts to the dish.",
                                        "Bake for 15 minutes.",
                                        "Add the yellow squash and put back in the oven for 30 minutes.",
                                        "Remove from oven and let cool for 15 minutes. Add the lettuce and serve."
                                      ]
                                    }, {
                                      "id": 2,
                                      "name": "Fish Tacos",
                                      "ingredients": [{
                                        "name": "Whitefish",
                                        "amount": 1,
                                        "measurement": "lb"
                                      }, {
                                        "name": "Cheese",
                                        "amount": 1,
                                        "measurement": "cup"
                                      }, {
                                        "name": "Iceberg Lettuce",
                                        "amount": 2,
                                        "measurement": "cups"
                                      }, {
                                        "name": "Tomatoes",
                                        "amount": 2,
                                        "measurement": "large"
                                      }, {
                                        "name": "Tortillas",
                                        "amount": 3,
                                        "measurement": "med"
                                      }],
                                      "steps": [
                                        "Cook the fish on the grill until hot.",
                                        "Place the fish on the 3 tortillas.",
                                        "Top them with lettuce, tomatoes, and cheese."
                                      ]
                                    }, {
                                      "id": 3,
                                      "name": "Checken Noodle Soup",
                                      "ingredients": [{
                                        "name": "Extra-virgin olive oil",
                                        "amount": 2,
                                        "measurement": "tablespoons"
                                      }, {
                                        "name": "Yello Onion",
                                        "amount": 1,
                                        "measurement": ""
                                      }, {
                                        "name": "Carrots",
                                        "amount": 2,
                                        "measurement": "meduim"
                                      }, {
                                        "name": "Celery",
                                        "amount": 2,
                                        "measurement": "stalks"
                                      }, {
                                        "name": "Chiken Broth",
                                        "amount": 8,
                                        "measurement": "cups"
                                      }, {
                                        "name": "Wide Egg Noodles",
                                        "amount": 1,
                                        "measurement": "package"
                                      }, {
                                        "name": "Cooked Chicken",
                                        "amount": 1,
                                        "measurement": "cup"
                                      }],
                                      "steps": [
                                        "Chop and saute onion, carrots, and celery in olive oil until soft.",
                                        "Add chicken  broth and bring to boil.",
                                        "Add egg noodles and cook until soft.",
                                        "Add chicken and simmer."
                                      ]
                                    }]}

                                  Теперь можно вдоволь попрактиковаться в запросах
                                  Сообщение отредактировано: Cfon -
                                    Цитата Cfon @
                                    Задание! :whistle:
                                    Надо перенести на Delphi. Кто не понел это код JS :D
                                    ExpandedWrap disabled
                                      const colors = ["red", "red", "green", "blue", "green"];
                                      const distinctColors = colors.reduce(
                                        (distinct, color) =>
                                          (distinct.indexOf(color) !== -1) ?
                                            distinct :
                                            [...distinct, color],
                                      []);
                                       
                                      console.log(distinctColors);
                                      // ["red", "green", "blue"]

                                    на самом деле это задание средней сложности.
                                    ща разбермся почему, смотрите вот что у меня получилось
                                    ExpandedWrap disabled
                                      begin
                                        var colors:= Enumerable<string>.Create(['red', 'red', 'green', 'blue', 'green']);
                                       
                                        var distinctColors := colors.Aggregate<IList<string>>(
                                            TCollections.CreateList<string>,
                                            function (distinct: IList<string>; color: string): IList<string>
                                            begin
                                              Result:= distinct;
                                              if distinct.IndexOf(color) = -1 then
                                              begin
                                                Result:= TCollections.CreateList<string>(distinct);
                                                Result.Add(color);
                                              end;
                                            end);
                                       
                                        // test
                                        distinctColors.ForEach(procedure (const s: string)
                                        begin
                                          Writeln(s, ', ');
                                        end);
                                        // red, green, blue,
                                      end.

                                    тут много чего интересного...
                                    Во первых Aggregate вызывается из объекта colors, который является записью Enumerable. Enumerable предоставлят реализации большого набора методов, в том числе Aggregate<TAccumulate>, реализации которой нет в других структурах данных таких как TList<T> и тд. Поэтому не получится создать например список и обратится к Aggregate<TAccumulate>, его там нет.
                                    Во-вторых колбэк (анонимка) возвращает копию списка, а не ссылку не исходный список. Тоже происходит и в коде на JS каждый раз возвращается копия массива:
                                    ExpandedWrap disabled
                                      [...distinct, color]

                                    а так в ДелФи, пока не придумали синтаксис по короче :D
                                    ExpandedWrap disabled
                                      Result:= TCollections.CreateList<string>(distinct);
                                      Result.Add(color);

                                    Зачем это надо? Это создает более безопасный код, следуя одному из принципов функционального программирования - неизменяемости данных, например:
                                    ExpandedWrap disabled
                                      begin
                                        var colors:= Enumerable<string>.Create(['red', 'red', 'green', 'blue', 'green']);
                                        var colors2:= TCollections.CreateList<string>;
                                       
                                        var distinctColors := colors.Aggregate<IList<string>>(
                                            colors2, //<--- передаем я качестве начального значения colors2
                                            function (distinct: IList<string>; color: string): IList<string>
                                            begin
                                              Result:= distinct;
                                              if distinct.IndexOf(color) = -1 then
                                              begin
                                                Result.Add(color); //<-- изменяем непосредствено colors2
                                      //          Result:= TCollections.CreateList<string>(distinct);
                                      //          Result.Add(color);
                                              end;
                                            end);
                                       
                                        // меняем список colors2
                                        colors2.AddRange(['white','black']);
                                       
                                        // test
                                        distinctColors.ForEach(procedure (const s: string)
                                        begin
                                          Write(s, ', ');
                                        end);
                                        // red, green, blue, white, black,
                                       
                                        writeln;
                                        // test
                                        colors2.ForEach(procedure (const s: string)
                                        begin
                                          Writeln(s);
                                        end);
                                        // red, green, blue, white, black,  
                                      end.

                                    и что мы видим изменения colors2 повлияли на distinctColors! Их содержимое одинаковое. Это как вы понимаете может в более сложных случаях внести баг, который сложно обнаружить.
                                    Собствено на этом все. Там еще может возникнуть вопрос почему возвращал IList<string>, а не Enumerable<string>? Ну тут все просто, Enumerable<string> не предоставляет метод IndexOf как в примере JS :D
                                    Сообщение отредактировано: Cfon -
                                      Цитата Cfon @
                                      Ну тут все просто, Enumerable<string> не предоставляет метод IndexOf как в примере JS

                                      А Contains у него есть
                                        Цитата jack128 @
                                        Цитата Cfon @
                                        Ну тут все просто, Enumerable<string> не предоставляет метод IndexOf как в примере JS

                                        А Contains у него есть

                                        О не заметил :D
                                        Тогда буде даже лучше если вернуть Enumerable<string>, ибо его нельзя никак изменить только копирование:
                                        ExpandedWrap disabled
                                          begin
                                            var colors := Enumerable<string>.Create(['red', 'red', 'green', 'blue', 'green']);
                                           
                                            var distinctColors := colors.Aggregate<Enumerable<string>>(
                                                TCollections.CreateList<string>,
                                                function (distinct: Enumerable<string>; color: string): Enumerable<string>
                                                begin
                                                  Result:= distinct;
                                                  if not distinct.Contains(color) then
                                                  begin
                                          //          Result.Add(color); //<-- никак не изменить Enumerable только копирование
                                                    Result:= Enumerable<string>.Create(distinct.ToArray + [color]);
                                                  end;
                                                end);
                                           
                                            // test
                                            distinctColors.ForEach(procedure (const s: string)
                                            begin
                                              Write(s, ', ');
                                            end);
                                            // red, green, blue,
                                          end;

                                        Правда мы тут имеем лишнее копирование массива distinct.ToArray, но подругому никак :D
                                        Возможно Enumerable это дело как то оптимизирует и на самом деле ничего не копируется :D

                                        Кста у Enumerable есть метод Distint, так шо данный код можно заменить на простой
                                        ExpandedWrap disabled
                                          colors:= Enumerable<string>.Create(['red', 'red', 'green', 'blue', 'green']);
                                          distinctColors:= colors.Distinct;

                                        но суть примера показать как работает Aggregate<TAccumulate>.

                                        Далее тут я опять передал в качестве сида (seed) IList<string> :D
                                        это не опечатка, Enumerable<T> имеет оператор Implicit, который выполняет неявное преобразование типа.
                                        Сообщение отредактировано: Cfon -
                                          Возращаемся к посту #16
                                          Предположим у нас добавился рейтинг рецептов, шо делать? :o
                                          Фигня если код нормально писан то изи :D зерте шо я делаю :D
                                          Сначало изменяю TRecipe
                                          ExpandedWrap disabled
                                            type
                                              TRecipe = record
                                                Id: integer;
                                                RecipeName: string;
                                                Ingredients: IList<TIngredient>;
                                                Steps: IList<string>;
                                                Rating: integer;   //<-- 1
                                                constructor Create(AId: integer; ARecipeName: string;
                                                  AIngredients: IList<TIngredient>; ASteps: IList<string>; ARating: integer);
                                                                                                         // ^^^ 2
                                              end;
                                             
                                            { TRecipe }
                                             
                                            constructor TRecipe.Create(AId: integer; ARecipeName: string;
                                              AIngredients: IList<TIngredient>; ASteps: IList<string>; ARating: integer);
                                            begin
                                              Id:= AId;
                                              RecipeName:= ARecipeName;
                                              Ingredients:= AIngredients;
                                              Steps:= ASteps;
                                              Rating:= ARating; //<-- 3
                                            end;

                                          Далее изменяю метод парсинга JSON CreateRecipes, тут ващ изичи :D
                                          ExpandedWrap disabled
                                            .....
                                                  for var vRecipe in vRecipes do
                                                  begin
                                                    var id:= vRecipe.GetValue<integer>('id');
                                                    var name:= vRecipe.GetValue<string>('name');
                                                    var vIngrs := vRecipe.GetValue<TJSONArray>('ingredients');
                                                    var vSteps := vRecipe.GetValue<TJSONArray>('steps');
                                                    var ingredients:= TCollections.CreateList<TIngredient>;
                                                    var steps:= TCollections.CreateList<string>;
                                                    var rating:=  vRecipe.GetValue<integer>('rating'); //<-- 4
                                             
                                                    .....
                                             
                                                    Result.Add(TRecipe.Create(id, name, ingredients, steps, rating));
                                                                                                          // ^^^ 5
                                                  end;
                                            .....

                                          тестируем
                                          ExpandedWrap disabled
                                            begin
                                              // load json file.
                                              var s:= LoadFile('recipes.json');
                                             
                                              // create recipes list.
                                              var recipes: IList<TRecipe> := CreateRecipes(s);
                                             
                                              // query: recipe name and rating list
                                               var query := TEnumerable.Select<TRecipe, string>(recipes,
                                                function (recipe: TRecipe): string
                                                begin
                                                  Result:= Format('Recipe: %s, Rating: %d', [recipe.RecipeName, recipe.Rating]);
                                                end
                                              );
                                             
                                              // test
                                              query.ForEach(procedure (const s: string)
                                              begin
                                                Writeln(s);
                                              end);
                                             
                                            // Recipe: Baked Salmon, Rating: 3
                                            // Recipe: Fish Tacos, Rating: 5
                                            // Recipe: Checken Noodle Soup, Rating: 0
                                            end.

                                          ВСЕ! :blush:
                                          Сообщение отредактировано: Cfon -
                                            Цитата Cfon @
                                            ExpandedWrap disabled
                                              const colors = ["red", "red", "green", "blue", "green"];
                                              const distinctColors = colors.reduce(
                                                (distinct, color) =>
                                                  (distinct.indexOf(color) !== -1) ?
                                                    distinct :
                                                    [...distinct, color],
                                              []);
                                               
                                              console.log(distinctColors);
                                              // ["red", "green", "blue"]

                                            Не все йогурты одинаково полезны.
                                            На мой консервативный взгляд, этот код - отличное доказательство того, как простые операции превращаются в малопонятную хрень из тысяч анонимок, методов, перераспределений и тому подобной муры.
                                            ExpandedWrap disabled
                                              const colors = ["red", "red", "green", "blue", "green"];
                                              const distinctColors = [];
                                              for (let col of colors)
                                                if (distinctColors.indexOf(col) == -1)
                                                  distinctColors.push(col);


                                            Добавлено
                                            То есть это мало того что write-only-код, так он еще и не даст заскучать процессору на массивах чуть больше игрушечных размеров. При N элементах N вызовов функции + M (M - количество уникальных элементов) выделений нового массива с копированием элементов и удалений прежнего. В общем, ответ на вопрос, почему компы имеют 3 ГГц процы и 16 Гб оперативы, а браузер с одной вкладкой по-прежнему тормозит
                                            Сообщение отредактировано: Fr0sT -
                                              Задание! :D
                                              Надо перевести код JS на делфи:
                                              ExpandedWrap disabled
                                                const compose = (...fns) =>
                                                 (arg) =>
                                                 fns.reduce(
                                                 (composed, f) => f(composed),
                                                 arg
                                                 )

                                              - Что это за мура? :lool:
                                              Это не мура! Это функция высшего порядка композиция! :D
                                              - Что она делает? :blink:
                                              Композиция получает список функций, выполняет последовательно над агрументом действия и возвращает результат.
                                              Сообщение отредактировано: Cfon -
                                                Задание оказалось очень сложным :wall:
                                                Вот что я осилил, комплятор ругается ошибка типов, я даже не буду приводить ее тут ибо один фиг не понятная :D
                                                Все я пас, сами мучайтесь :D
                                                Кто решит я буду плюсовать пока я жив :lool:
                                                ExpandedWrap disabled
                                                  type
                                                    TFunctionalUtils = record
                                                      class function Compose<T,R>(fns: Enumerable<TFunc<T,R>>): TFunc<T, R>; static;
                                                    end;
                                                   
                                                  { TFunctionalUtils }
                                                   
                                                  class function TFunctionalUtils.Compose<T, R>(
                                                    fns: Enumerable<TFunc<T, R>>): TFunc<T, R>;
                                                  begin
                                                    Result:= function (arg: T): R
                                                    begin
                                                      Result:= fns.Aggregate<T>(arg,
                                                        function (composed: T; f: TFunc<T, R>): R
                                                        begin
                                                          Result:= f(composed);
                                                        end);
                                                    end;
                                                  end;


                                                Добавлено
                                                Не я пока тут малеха перекурил и меня осенило чекануть на конкрит дата :D
                                                Проверил для целых чисел и что вы думаете работает! Как же все это дело обобщить :wacko:
                                                ExpandedWrap disabled
                                                  class function TFunctionalUtils.Compose(
                                                    fns: Enumerable<TFunc<integer, integer>>): TFunc<integer, integer>;
                                                  begin
                                                    Result:= function (arg: integer): integer
                                                    begin
                                                      Result:= fns.Aggregate<integer>(arg,
                                                        function (composed: integer; f: TFunc<integer, integer>): integer
                                                        begin
                                                          Result:= f(composed);
                                                        end);
                                                    end;
                                                  end;
                                                   
                                                  function Add5(i: integer): integer;
                                                  begin
                                                    Result:= i + 5;
                                                  end;
                                                   
                                                  function Add7(i: integer): integer;
                                                  begin
                                                    Result:= i + 7;
                                                  end;
                                                   
                                                  var
                                                    i: integer;
                                                    funcs: Enumerable<TFunc<integer,integer>>;
                                                    func: TFunc<integer,integer>;
                                                  begin
                                                    funcs:= Enumerable<TFunc<integer,integer>>.Create([Add5, Add7]);
                                                    func:=  TFunctionalUtils.Compose(funcs);
                                                    i:= func(10);
                                                    Writeln(i);
                                                  // Output: 22
                                                  end.


                                                Добавлено
                                                О обобщил для одного типа :D
                                                ExpandedWrap disabled
                                                  class function TFunctionalUtils.Compose<T>(
                                                    fns: Enumerable<TFunc<T, T>>): TFunc<T, T>;
                                                  begin
                                                    Result:= function (arg: T): T
                                                    begin
                                                      Result:= fns.Aggregate<T>(arg,
                                                        function (composed: T; f: TFunc<T, T>): T
                                                        begin
                                                          Result:= f(composed);
                                                        end);
                                                    end;
                                                  end;

                                                Как же обобщить для двух разных типов :wacko:
                                                Сообщение отредактировано: Cfon -
                                                  Цитата Cfon @
                                                  Как же обобщить для двух разных типов

                                                  конкретно в дельфи - никак. Возможно в плюсах можно (гугли variadic templates) или в соседнем подформуе спрашивай
                                                    Цитата Cfon @
                                                    Как же обобщить для двух разных типов :wacko:

                                                    Все понел, в Spring.Collections.Enumerable нет метода Aggregate с соотвест. сигнатурой там тока для TFunc<T,T>.
                                                    Да и в моем случае TFunc<T,R> тоже не катит, тут надо не через обобщения, а нужна динамическая типизация как в JS.
                                                    Так что пока варик TFunc<T,T> покатит.

                                                    Ну а пока продолжаю выкладывать другую муру :D
                                                    ExpandedWrap disabled
                                                      uses
                                                        System.SysUtils,
                                                        Spring.Collections;
                                                       
                                                      type
                                                        TPet = record
                                                          Name: string;
                                                          Age: integer;
                                                          constructor Create(AName: string; AAge:integer);
                                                        end;
                                                       
                                                      { TPet }
                                                       
                                                      constructor TPet.Create(AName: string; AAge: integer);
                                                      begin
                                                        Name:= AName;
                                                        Age:= AAge;
                                                      end;
                                                       
                                                      begin
                                                        // Create an array of Pets.
                                                        var pets:= TCollections.CreateList<TPet>([
                                                          TPet.Create('Barley', 10),
                                                          TPet.Create('Boots', 4),
                                                          TPet.Create('Whiskers', 6)
                                                        ]);
                                                       
                                                        // Determine whether all pet names
                                                        // in the array start with 'B'.
                                                        var allStartWithB:= pets.All(function (const pet: TPet): boolean
                                                          begin
                                                            Result:= pet.Name.StartsWith('B');
                                                          end);
                                                       
                                                        if allStartWithB then
                                                          WriteLn('All pet names start with "B".')
                                                        else
                                                          WriteLn('Not all pet names start with "B".');
                                                      end;
                                                      (* C# code:
                                                      // Create an array of Pets.
                                                          Pet[] pets = { new Pet { Name="Barley", Age=10 },
                                                                         new Pet { Name="Boots", Age=4 },
                                                                         new Pet { Name="Whiskers", Age=6 } };
                                                       
                                                          // Determine whether all pet names
                                                          // in the array start with 'B'.
                                                          bool allStartWithB = pets.All(pet =>
                                                                                            pet.Name.StartsWith("B"));
                                                       
                                                          Console.WriteLine(
                                                              "{0} pet names start with 'B'.",
                                                              allStartWithB ? "All" : "Not all");
                                                       
                                                      // This code produces the following output:
                                                      //
                                                      //  Not all pet names start with 'B'.*)
                                                      end.


                                                    Добавлено
                                                    Цитата jack128 @
                                                    Цитата Cfon @
                                                    Как же обобщить для двух разных типов

                                                    конкретно в дельфи - никак. Возможно в плюсах можно (гугли variadic templates) или в соседнем подформуе спрашивай

                                                    ОК.
                                                    Сообщение отредактировано: Cfon -
                                                      А вот пример с IEnumerable<T>.All поинтереснее, тут All заюзан с Where и завершен Select'ом. Сам пример на C# приводится в Linq синтаксисе, так что не сразу понятно как это будет на Делфях:
                                                      ExpandedWrap disabled
                                                        begin
                                                          var People:= TCollections.CreateList<TPerson>([
                                                            TPerson.Create('Haas',
                                                              TCollections.CreateList<TPet>([TPet.Create('Barley', 10),
                                                                                             TPet.Create('Boots', 14),
                                                                                             TPet.Create('Whiskley', 6)])),
                                                            TPerson.Create('Fakhouri',
                                                              TCollections.CreateList<TPet>([TPet.Create('Snowball', 1)])),
                                                            TPerson.Create('Antebi',
                                                              TCollections.CreateList<TPet>([TPet.Create('Belle', 8)])),
                                                            TPerson.Create('Philips',
                                                              TCollections.CreateList<TPet>([TPet.Create('Sweetie', 2),
                                                                                             TPet.Create('Rover', 13)]))
                                                          ]);
                                                         
                                                          // Determine which people have pets that are all older than 5.
                                                          var Persons := People.Where(function (const person: TPerson): Boolean
                                                            begin
                                                              Result:= person.Pets.All(function (const pet: TPet): boolean
                                                                begin
                                                                  Result:= pet.Age > 5;
                                                                end);
                                                            end);
                                                         
                                                          var Names := TEnumerable.Select<TPerson, string>(Persons,
                                                            function (person: TPerson): string
                                                            begin
                                                              Result:= person.LastName;
                                                            end
                                                          );
                                                         
                                                          Names.ForEach(procedure (const name: string)
                                                            begin
                                                              Writeln(name);
                                                            end
                                                          );
                                                        end.
                                                        (*List<Person> people = new List<Person>
                                                            { new Person { LastName = "Haas",
                                                                           Pets = new Pet[] { new Pet { Name="Barley", Age=10 },
                                                                                              new Pet { Name="Boots", Age=14 },
                                                                                              new Pet { Name="Whiskers", Age=6 }}},
                                                              new Person { LastName = "Fakhouri",
                                                                           Pets = new Pet[] { new Pet { Name = "Snowball", Age = 1}}},
                                                              new Person { LastName = "Antebi",
                                                                           Pets = new Pet[] { new Pet { Name = "Belle", Age = 8} }},
                                                              new Person { LastName = "Philips",
                                                                           Pets = new Pet[] { new Pet { Name = "Sweetie", Age = 2},
                                                                                              new Pet { Name = "Rover", Age = 13}} }
                                                            };
                                                         
                                                        // Determine which people have pets that are all older than 5.
                                                        IEnumerable<string> names = from person in people
                                                                                    where person.Pets.All(pet => pet.Age > 5)
                                                                                    select person.LastName;
                                                         
                                                        foreach (string name in names)
                                                        {
                                                            Console.WriteLine(name);
                                                        }
                                                         
                                                        /* This code produces the following output:
                                                         *
                                                         * Haas
                                                         * Antebi
                                                         */*)

                                                      Я ща буду в основном юзать списки (IList<T>) вместо массивов (Enumerable<T>), по причине непонятных тупяков Делфи :D
                                                      Каких? У меня на Enumerable часто вылетают каки :D
                                                      Не буду их описывать, возможно кто юзал тот в курсе :crazy:

                                                      Да и не путайте IEnumerable<T> c Enumerable<T>.
                                                      IEnumerable<T> это интерфес, который реализуют различные структуры данных такие как TList<T>, а Enumerable<T> это запись, которая агрегирует этот интерфейс, и реализует его как итератор массива, ну и реализует большой набор методов для работы над этим массивом.

                                                      Вот что еще интересно, обратите внимание на то что в предикат передается константный параметр (см. All, Where), а вот в анонимке Select'а передается неконстантный параметр, это позволит изменить сам список, если в качестве значения передается объект класса, а не запись, например допустим что TPerson = class, тогда
                                                      ExpandedWrap disabled
                                                        var Names := TEnumerable.Select<TPerson, string>(Persons,
                                                            function (person: TPerson): string
                                                            begin
                                                              person.LastName := person.LastName.ToUpper; //<-- меняем значение LastName
                                                              Result:= person.LastName;
                                                            end
                                                          );

                                                      в итоге имеем измененый список People и Persons, почему в селектах так я беспонятий :D
                                                      Сообщение отредактировано: Cfon -
                                                        Обнаружил случайно один интресный момент в работе коллекций Spring4D, суть вот в чем, давайте расмотрим внимательно кусок пред кода
                                                        ExpandedWrap disabled
                                                          .....
                                                            // Determine which people have pets that are all older than 5.
                                                            var Persons := People.Where(function (const person: TPerson): Boolean
                                                              begin
                                                                Result:= person.Pets.All(function (const pet: TPet): boolean
                                                                  begin
                                                                    Result:= pet.Age > 5;
                                                                  end);
                                                              end);
                                                           
                                                            var Names := TEnumerable.Select<TPerson, string>(Persons,
                                                              function (person: TPerson): string
                                                              begin
                                                                Result:= person.LastName;
                                                              end
                                                            );
                                                           
                                                            Names.ForEach(procedure (const name: string)
                                                              begin
                                                                Writeln(name);
                                                              end
                                                            );
                                                          end.

                                                        тут в анонимке Where явно задан значение 5, как правило в рилкоде там будет переменная, давайте изменим это участок кода
                                                        ExpandedWrap disabled
                                                          .....
                                                            var age:= 5;
                                                            // Determine which people have pets that are all older than 5.
                                                            var Persons := People.Where(function (const person: TPerson): Boolean
                                                              begin
                                                                Result:= person.Pets.All(function (const pet: TPet): boolean
                                                                  begin
                                                                    Result:= pet.Age > age;
                                                                  end);
                                                              end);
                                                          .....

                                                        ОК, тут вроде все понятно age замкнулась (захвачена) в анонимке All. Все супер, а теперь давайте изменим значение age где-нибудь ниже по коду:
                                                        ExpandedWrap disabled
                                                          .....
                                                            var age:= 5;
                                                            // Determine which people have pets that are all older than 5.
                                                            var Persons := People.Where(function (const person: TPerson): Boolean
                                                              begin
                                                                Result:= person.Pets.All(function (const pet: TPet): boolean
                                                                  begin
                                                                    Result:= pet.Age > age;
                                                                  end);
                                                              end);
                                                            
                                                            age:=10;
                                                          .....

                                                        и как вы думаете каков будет результат запроса?
                                                        Name.ForEach выведет список имен персон, у которых возраст петс нет не > 5, a age > 10! :D
                                                        Что происходит? :o
                                                        А происходит так называемое отложенное вычисление!
                                                        Круто да? :D
                                                        Т.е реальный запрос будет выполнен только тогда когда будет первое обращение нет даже не к Persons, а к Names! :hang:
                                                        Сообщение отредактировано: Cfon -
                                                          Цитата Cfon @
                                                          Т.е реальный запрос будет выполнен только тогда когда будет первое обращение нет даже не к Persons, а к Names! :hang:

                                                          На самом деле при обращении к Persons тоже запрос выполниться, просто в селекте нет обращения к Persons, поэтому наш запрос будет выполнен тока в строке Names.ForEach, это я и имел ввиду.

                                                          - А что делать если нам надо, чтобы значение age было то что определено перед фильтрацией? :huh:
                                                          Решение простое надо зафиксировать значение age :D
                                                          - Как?! :blink:
                                                          Например вот так :D
                                                          ExpandedWrap disabled
                                                              var age:= 5;
                                                              // Determine which people have pets that are all older than 5.
                                                              var Persons := (function (age: integer): IEnumerable<TPerson>
                                                                begin
                                                                  Result:= People.Where(function (const person: TPerson): Boolean
                                                                    begin
                                                                      Result:= person.Pets.All(function (const pet: TPet): boolean
                                                                        begin
                                                                          Result:= pet.Age > age;
                                                                        end);
                                                                    end);
                                                                end)(age);
                                                              age:= 10; //это уже не влияет на предыдущий результат

                                                          Ну или определить отдельно функцию и передавать туда age, в принципе тоже что и щас :D

                                                          А можно еще такую тему зафигачить :D
                                                          ExpandedWrap disabled
                                                            var PersonFilter := function (APersons: IEnumerable<TPerson>):
                                                                  TFunc<integer, IEnumerable<TPerson>>
                                                                begin
                                                                  Result:= function (age: integer): IEnumerable<TPerson>
                                                                  begin
                                                                    Result := APersons.Where(function (const person: TPerson): Boolean
                                                                    begin
                                                                      Result:= person.Pets.All(function (const pet: TPet): boolean
                                                                        begin
                                                                          Result:= pet.Age > age;
                                                                        end);
                                                                    end);
                                                                  end;
                                                                end;
                                                             
                                                              var FilterByPeople:= PersonFilter(People);
                                                              var Persons:= FilterByPeople(age);

                                                          Ну тут я заюзал частичное применение для фиксации первого параметра, в данном случае ссылки на список People.
                                                          - Зачем нам его фиксировать? :huh:
                                                          Ну это удобно когда нам надо часто вызывать одну и туже фильтрацию для разных значений age :D
                                                          В данном случае в принципе можно и два передать не страшно, а если к примеру у нас есть функция где много параметров? Причем все кроме одного остальные не меняются, вот тогда можно заюзать частичное применение и создать функцию с меньшим числом параметром.
                                                          Сообщение отредактировано: Cfon -
                                                            Теперь мой пример с рецептами, юзаем IEnumerable<T>.Any, дополнительно юзанул анонимку, ну так для примера :D
                                                            ExpandedWrap disabled
                                                              procedure ProcessRecipes;
                                                              begin
                                                                // load json file.
                                                                var s:= LoadFile('recipes.json');
                                                               
                                                                // create recipes list.
                                                                var recipes := CreateRecipes(s);
                                                              ...
                                                               
                                                                var recipeByIngr:= function (ingredientName: string): IEnumerable<TRecipe>
                                                                  begin
                                                                    Result:= recipes.Where(function (const recipe: TRecipe): Boolean
                                                                    begin
                                                                      Result:= recipe.Ingredients.Any(function (const ingr: TIngredient): Boolean
                                                                      begin
                                                                        Result:= ingr.IngredientName.ToLower.Contains(ingredientName.ToLower);
                                                                      end);
                                                                    end);
                                                                  end;
                                                               
                                                                // получаем список рецептов, где название
                                                                // ингредиента содержит слово 'lettuce'
                                                                var query3:= recipeByIngr('lettuce');
                                                               
                                                                // print the results.
                                                                query3.ForEach(procedure (const recipe: TRecipe)
                                                                begin
                                                                  Writeln(recipe.RecipeName);
                                                                end);
                                                                // Baked Salmon
                                                                // Fish Tacos
                                                              end;

                                                            Название ингредиента передается как параметр, а recipes через замыкание.
                                                            Сообщение отредактировано: Cfon -
                                                              Еще одно замечание по поводу след.кода
                                                              ExpandedWrap disabled
                                                                .....
                                                                  // Determine which people have pets that are all older than 5.
                                                                  var Persons := People.Where(function (const person: TPerson): Boolean
                                                                    begin
                                                                      Result:= person.Pets.All(function (const pet: TPet): boolean
                                                                        begin
                                                                          Result:= pet.Age > 5;
                                                                        end);
                                                                    end);
                                                                 
                                                                  var Names := TEnumerable.Select<TPerson, string>(Persons,
                                                                    function (person: TPerson): string
                                                                    begin
                                                                      Result:= person.LastName;
                                                                    end);
                                                                 
                                                                  Names.ForEach(procedure (const name: string)
                                                                  begin
                                                                    Writeln(name);
                                                                  end);
                                                                end.

                                                              Тут на самом деле выполнятся только один цикл! Так что нет никаких многоразовых проходов по одному и тому же списку.
                                                              Where и Select задают операции которые надо произвести, а сам цикл по списку уже выполнит ForEach! Крутяк да? :D
                                                              - А как быть если результат нужена сохранить без ForEach? :huh:
                                                              Кол метод ToArray :D
                                                                Рефакторим CreateRecipes в примере из поста #16:
                                                                ExpandedWrap disabled
                                                                  ...
                                                                    type TMapFunc<T> = reference to function (Value: TJSONValue): T;
                                                                   
                                                                    TRecipesManager = record
                                                                      class function LoadFile(fileName: string): string; static;
                                                                      class function CreateRecipes(json: string): IList<TRecipe>; static;
                                                                      class function CreateIngredients(AValues: TJSONArray): IList<TIngredient>; static;
                                                                      class function CreateSteps(AValues: TJSONArray): IList<string>; static;
                                                                      class function Map<T>(const AValues: TJSONArray; AFunc: TMapFunc<T>): IList<T>; static;
                                                                    end;
                                                                   
                                                                    TJSONParser = record
                                                                      class function GetRoot(json: string): TJSONValue; static;
                                                                      class function GetNumber(AValue: TJSONValue; APath: string): integer; static;
                                                                      class function GetDouble(AValue: TJSONValue; APath: string): Double; static;
                                                                      class function GetString(AValue: TJSONValue; APath: string): string; static;
                                                                      class function GetArray(AValue: TJSONValue; APath: string): TJSONArray; static;
                                                                    end;
                                                                   
                                                                  ....
                                                                   
                                                                  { TJSONParser }
                                                                   
                                                                  class function TJSONParser.GetArray(AValue: TJSONValue;
                                                                    APath: string): TJSONArray;
                                                                  begin
                                                                    Result:= AValue.GetValue<TJSONArray>(APath);
                                                                  end;
                                                                   
                                                                  class function TJSONParser.GetDouble(AValue: TJSONValue; APath: string): Double;
                                                                  begin
                                                                    Result:= AValue.GetValue<Double>(APath);
                                                                  end;
                                                                   
                                                                  class function TJSONParser.GetNumber(AValue: TJSONValue;
                                                                    APath: string): integer;
                                                                  begin
                                                                    Result:= AValue.GetValue<integer>(APath);
                                                                  end;
                                                                   
                                                                  class function TJSONParser.GetRoot(json: string): TJSONValue;
                                                                  begin
                                                                    Result:= TJSONObject.ParseJSONValue(json);
                                                                  end;
                                                                   
                                                                  class function TJSONParser.GetString(AValue: TJSONValue; APath: string): string;
                                                                  begin
                                                                    Result:= AValue.GetValue<string>(APath);
                                                                  end;
                                                                   
                                                                  { TRecipesManager }
                                                                   
                                                                  class function TRecipesManager.Map<T>(const AValues: TJSONArray;
                                                                    AFunc: TMapFunc<T>): IList<T>;
                                                                  begin
                                                                    Result:= TCollections.CreateList<T>;
                                                                    for var val in AValues do
                                                                      Result.Add(AFunc(val));
                                                                  end;
                                                                   
                                                                  class function TRecipesManager.CreateIngredients(
                                                                    AValues: TJSONArray): IList<TIngredient>;
                                                                  begin
                                                                    Result:= TRecipesManager.Map<TIngredient>(AValues,
                                                                      function (ingr: TJSONValue): TIngredient
                                                                      begin
                                                                        Result:= TIngredient.Create(
                                                                          TJSONParser.GetString(ingr, 'name'),
                                                                          TJSONParser.GetDouble(ingr, 'amount'),
                                                                          TJSONParser.GetString(ingr, 'measurement'));
                                                                      end);
                                                                  end;
                                                                   
                                                                  class function TRecipesManager.CreateRecipes(json: string): IList<TRecipe>;
                                                                  begin
                                                                    Result:= TCollections.CreateList<TRecipe>;
                                                                    try
                                                                      var Root:= TJSONParser.GetRoot(json);
                                                                      var Recipes := TJSONParser.GetArray(Root, 'recipes');
                                                                      try
                                                                        Result:= TRecipesManager.Map<TRecipe>(Recipes,
                                                                          function (recipe: TJSONValue): TRecipe
                                                                          begin
                                                                            var id:= TJSONParser.GetNumber(recipe, 'id');
                                                                            var name:= TJSONParser.GetString(recipe, 'name');
                                                                            var rating:=  TJSONParser.GetNumber(recipe, 'rating');
                                                                            var ingredients:= CreateIngredients(
                                                                                            TJSONParser.GetArray(recipe, 'ingredients'));
                                                                            var steps:= CreateSteps(TJSONParser.GetArray(recipe, 'steps'));
                                                                            Result:= TRecipe.Create(id, name, ingredients, steps, rating);
                                                                          end);
                                                                      finally
                                                                        Recipes.Free;
                                                                      end;
                                                                    except
                                                                      on EJSONException do
                                                                        Writeln('Error parsing JSON. Check file format.');
                                                                    end;
                                                                  end;
                                                                   
                                                                  class function TRecipesManager.CreateSteps(AValues: TJSONArray): IList<string>;
                                                                  begin
                                                                    Result:= TRecipesManager.Map<string>(AValues,
                                                                      function (step: TJSONValue): string
                                                                      begin
                                                                        Result:= step.Value;
                                                                      end);
                                                                  end;
                                                                   
                                                                  class function TRecipesManager.LoadFile(fileName: string): string;
                                                                  begin
                                                                    var reader:= TStreamReader.Create(fileName);
                                                                    try
                                                                      Result:= reader.ReadToEnd;
                                                                    finally
                                                                      reader.Free;
                                                                    end;
                                                                  end;
                                                                   
                                                                   
                                                                  begin
                                                                    // load json file.
                                                                    var s:= TRecipesManager.LoadFile(TPath.Combine(
                                                                                    TPath.GetDocumentsPath, 'recipes.json'));
                                                                   
                                                                    // create recipes list.
                                                                    var recipes: IList<TRecipe> := TRecipesManager.CreateRecipes(s);
                                                                   
                                                                  ....
                                                                   
                                                                  end.

                                                                - А зачем в CreateRecipes лишний начальный Result:= TCollections.CreateList<TRecipe> получается дважды выделяется память! :huh:
                                                                Это мой стиль программирования! Я не лублу проверять результат операции типо
                                                                if not Assigned(recipes) then Exit на случай ошибки :D
                                                                - Надо думаю лучше все же вставить проверку :huh:
                                                                Ага себе вставь ее :lool:
                                                                Моя стиль прогить с позиции изначально нет ошибок! Я это называю "оптимистиское программирование"! О надо бы зарегить термин за авторством :D
                                                                - Спасибо маста вы гений! :scratch:
                                                                А то :D
                                                                Сообщение отредактировано: Cfon -
                                                                  IEnumerable<T>.Max
                                                                  ExpandedWrap disabled
                                                                    uses
                                                                      Spring.Collections;
                                                                     
                                                                    type
                                                                      TPet = record
                                                                        Name: string;
                                                                        Age: integer;
                                                                        constructor Create(AName: string; AAge: integer);
                                                                      end;
                                                                     
                                                                    { TPet }
                                                                     
                                                                    constructor TPet.Create(AName: string; AAge: integer);
                                                                    begin
                                                                      Name:= AName; Age:=AAge;
                                                                    end;
                                                                     
                                                                    begin
                                                                      var Ints: IList<integer>:= TCollections.CreateList<integer>([1,2,3,4,5]);
                                                                      var maxInt:= Ints.Max;
                                                                      Writeln(maxInt);
                                                                     
                                                                      var Pets: IList<TPet>:= TCollections.CreateList<TPet>([
                                                                            TPet.Create('Barley', 8),
                                                                            TPet.Create('Boots', 4),
                                                                            TPet.Create('Whiskers', 1)
                                                                      ]);
                                                                      
                                                                      var max: integer:= Pets.Max(function ( pet: TPet): integer
                                                                        begin
                                                                          Result:= pet.Age + Length(pet.Name);
                                                                        end
                                                                      );
                                                                      Writeln(max);
                                                                     
                                                                      Readln;
                                                                    end.
                                                                    (*C# code
                                                                      Pet[] pets = { new Pet { Name="Barley", Age=8 },
                                                                                         new Pet { Name="Boots", Age=4 },
                                                                                         new Pet { Name="Whiskers", Age=1 } };
                                                                     
                                                                          int max = pets.Max(pet => pet.Age + pet.Name.Length);
                                                                     
                                                                          Console.WriteLine(
                                                                              "The maximum pet age plus name length is {0}.",
                                                                              max);
                                                                     
                                                                      /*
                                                                       This code produces the following output:
                                                                     
                                                                       The maximum pet age plus name length is 14.
                                                                      */
                                                                    *)

                                                                  Вот немного сложнее
                                                                  ExpandedWrap disabled
                                                                    ...
                                                                      TPetComparer = class(TComparer<TPet>)
                                                                        function Compare(const Left, Right: TPet): Integer; override;
                                                                      end;
                                                                     
                                                                    { TPetComparer }
                                                                     
                                                                    function TPetComparer.Compare(const Left, Right: TPet): Integer;
                                                                    begin
                                                                      var sumLeft:= Left.Age + Length(Left.Name);
                                                                      var sumRight:= Right.Age + Length(Right.Name);
                                                                     
                                                                      if sumLeft < sumRight then
                                                                        Result:= -1
                                                                      else if sumLeft = sumRight then
                                                                        Result:= 0
                                                                      else
                                                                        Result:= 1;
                                                                    end;
                                                                     
                                                                    begin
                                                                      var Pets: IList<TPet>:= TCollections.CreateList<TPet>([
                                                                            TPet.Create('Barley', 8),
                                                                            TPet.Create('Boots', 4),
                                                                            TPet.Create('Whiskers', 1)
                                                                      ]);
                                                                     
                                                                      // comparer:  TComparison<T>
                                                                      var max: TPet:= Pets.Max(function (const Left, Right: TPet): integer
                                                                        begin
                                                                          var sumLeft:= Left.Age + Length(Left.Name);
                                                                          var sumRight:= Right.Age + Length(Right.Name);
                                                                     
                                                                          if sumLeft < sumRight then
                                                                            Result:= -1
                                                                          else if sumLeft = sumRight then
                                                                            Result:= 0
                                                                          else
                                                                            Result:= 1;
                                                                        end
                                                                      );
                                                                     
                                                                      Writeln('The "maximum" animal is ', max.Name);
                                                                      // The "maximum" animal is Barley.
                                                                     
                                                                      // ну или через IComparer<T>
                                                                      var comparer: IComparer<TPet>:= TPetComparer.Create;
                                                                      var max: TPet:= Pets.Max(comparer);
                                                                     
                                                                      Writeln('The "maximum" animal is ', max.Name);
                                                                      // The "maximum" animal is Barley.  
                                                                    end.
                                                                    (* C# code
                                                                    /// <summary>
                                                                      /// This class implements IComparable to be able to
                                                                      /// compare one Pet to another Pet.
                                                                      /// </summary>
                                                                      class Pet : IComparable<Pet>
                                                                      {
                                                                          public string Name { get; set; }
                                                                          public int Age { get; set; }
                                                                     
                                                                          /// <summary>
                                                                          /// Compares this Pet to another Pet by
                                                                          /// summing each Pet's age and name length.
                                                                          /// </summary>
                                                                          /// <param name="other">The Pet to compare this Pet to.</param>
                                                                          /// <returns>-1 if this Pet is 'less' than the other Pet,
                                                                          /// 0 if they are equal,
                                                                          /// or 1 if this Pet is 'greater' than the other Pet.</returns>
                                                                          int IComparable<Pet>.CompareTo(Pet other)
                                                                          {
                                                                              int sumOther = other.Age + other.Name.Length;
                                                                              int sumThis = this.Age + this.Name.Length;
                                                                     
                                                                              if (sumOther > sumThis)
                                                                                  return -1;
                                                                              else if (sumOther == sumThis)
                                                                                  return 0;
                                                                              else
                                                                                  return 1;
                                                                          }
                                                                      }
                                                                     
                                                                      public static void MaxEx3()
                                                                      {
                                                                          Pet[] pets = { new Pet { Name="Barley", Age=8 },
                                                                                         new Pet { Name="Boots", Age=4 },
                                                                                         new Pet { Name="Whiskers", Age=1 } };
                                                                     
                                                                          Pet max = pets.Max();
                                                                     
                                                                          Console.WriteLine(
                                                                              "The 'maximum' animal is {0}.",
                                                                              max.Name);
                                                                      }
                                                                     
                                                                      /*
                                                                       This code produces the following output:
                                                                     
                                                                       The 'maximum' animal is Barley.
                                                                      */*)

                                                                  Шарповый пример показан на IComparable, с ним Spring4D не работает.

                                                                  Добавлено
                                                                  Тест произодительности Spring4D vs простой цикл :D
                                                                  тестил на массиве из ~2000 слов, Spring4D Distint быстрее обычного цикла :blink:
                                                                  В чем я ошибся или у меня массив не правильный? :D
                                                                  ExpandedWrap disabled
                                                                    uses
                                                                      System.Classes, System.SysUtils, System.Diagnostics, System.IOUtils,
                                                                      Spring.Collections;
                                                                     
                                                                    begin
                                                                      var s := LoadFile(TPath.Combine(TPath.GetDocumentsPath, 'latin-lipsum.txt'));
                                                                      var words := TCollections.CreateList<string>(s.Split([' ']));  
                                                                     
                                                                      var sw:= TStopwatch.StartNew;
                                                                      var distinctWords:= TEnumerable.Distinct<string>(words);
                                                                      var arrayWords:= distinctWords.ToArray;
                                                                      sw.Stop;
                                                                     
                                                                      Writeln(Format('Distinct Words: %d, %d ms', [distinctWords.Count, sw.ElapsedMilliseconds]));
                                                                    // Distinct Words: 781, 1 ms
                                                                     
                                                                      var distinctWords2 := TCollections.CreateList<string>;
                                                                      sw.Reset;
                                                                      sw.Start;
                                                                      for var word in words do
                                                                      begin
                                                                        if distinctWords2.IndexOf(word) = -1 then
                                                                          distinctWords2.Add(word);
                                                                      end;
                                                                      sw.Stop;
                                                                      Writeln(Format('Distinct2 Words: %d, %d ms', [distinctWords2.Count, sw.ElapsedMilliseconds]));
                                                                    // Distinct Words: 781, 12 ms
                                                                    end;
                                                                  Сообщение отредактировано: Cfon -
                                                                    Цитата Cfon @
                                                                    Тест произодительности Spring4D vs простой цикл :D
                                                                    тестил на массиве из ~2000 слов, Spring4D Distint быстрее обычного цикла :blink:
                                                                    В чем я ошибся или у меня массив не правильный? :D

                                                                    Все понел! :D
                                                                    Дело в том что Spring Distinct юзает хешсет для создания уникального списка слов, т.е он фастом в один проход создает список, а в примере на цикле идет два цикла, один на добавление другой на поиск совпадения, что естесно ведет к низкой производительности. Как то так :D
                                                                      Enumerable<T>.ToLookup весьма интересная функция группировки данных, пришлось снова заюзать Enumerable по причине отсутчвия реализации в TEnumerable :D
                                                                      Не самая сложная в переводе, возможно по причине уже набитости руки :blush:
                                                                      ExpandedWrap disabled
                                                                        uses
                                                                          System.SysUtils,
                                                                          Spring.Collections,
                                                                          Spring.Collections.Enumerable;
                                                                         
                                                                        type
                                                                          TPackage = record
                                                                            Company: string;
                                                                            Weight: double;
                                                                            TrackingNumber: uint64;
                                                                            constructor Create(ACompany: string; AWeight: double; ATrackingNumber: uint64);
                                                                          end;
                                                                         
                                                                        { TPackage }
                                                                         
                                                                        constructor TPackage.Create(ACompany: string; AWeight: double;
                                                                          ATrackingNumber: uint64);
                                                                        begin
                                                                          Company:= ACompany;
                                                                          Weight:= AWeight;
                                                                          TrackingNumber:= ATrackingNumber;
                                                                        end;
                                                                         
                                                                         
                                                                        begin
                                                                          // Create a list of Packages.
                                                                          var Packages := TCollections.CreateList<TPackage>([
                                                                            TPackage.Create('Coho Vineyard', 25.2, 89453312),
                                                                            TPackage.Create('Lucerne Publishing', 18.7, 89112755),
                                                                            TPackage.Create('Wingtip Toys', 6.0, 299456122),
                                                                            TPackage.Create('Contoso Pharmaceuticals', 9.3, 670053128),
                                                                            TPackage.Create('Wide World Importers', 33.8, 4665518773)
                                                                          ]);
                                                                         
                                                                          // Create a Lookup to organize the packages.
                                                                          // Use the first character of Company as the key value.
                                                                          // Select Company appended to TrackingNumber
                                                                          // as the element values of the Lookup.
                                                                          var lookup := Enumerable<TPackage>.Create(Packages.ToArray)
                                                                            .ToLookup<Char, string>(
                                                                              function (p: TPackage): Char
                                                                              begin
                                                                                Result:= p.Company.Substring(0, 1)[1];
                                                                              end,
                                                                              function (p: TPackage): string
                                                                              begin
                                                                                Result:= p.Company + ' ' + p.TrackingNumber.ToString;
                                                                              end
                                                                            );
                                                                         
                                                                            // Iterate through each IGrouping in the Lookup.
                                                                            for var packageGroup in lookup do
                                                                            begin
                                                                              // Print the key value of the IGrouping.
                                                                              Writeln(packageGroup.Key);
                                                                              // Iterate through each value in the
                                                                              // IGrouping and print its value.
                                                                              for var str in packageGroup do
                                                                                Writeln('    ', str);
                                                                            end;
                                                                        end;
                                                                        (* C# code
                                                                        // Create a list of Packages.
                                                                            List<Package> packages =
                                                                                new List<Package>
                                                                                    { new Package { Company = "Coho Vineyard",
                                                                                          Weight = 25.2, TrackingNumber = 89453312L },
                                                                                      new Package { Company = "Lucerne Publishing",
                                                                                          Weight = 18.7, TrackingNumber = 89112755L },
                                                                                      new Package { Company = "Wingtip Toys",
                                                                                          Weight = 6.0, TrackingNumber = 299456122L },
                                                                                      new Package { Company = "Contoso Pharmaceuticals",
                                                                                          Weight = 9.3, TrackingNumber = 670053128L },
                                                                                      new Package { Company = "Wide World Importers",
                                                                                          Weight = 33.8, TrackingNumber = 4665518773L } };
                                                                         
                                                                            // Create a Lookup to organize the packages.
                                                                            // Use the first character of Company as the key value.
                                                                            // Select Company appended to TrackingNumber
                                                                            // as the element values of the Lookup.
                                                                            ILookup<char, string> lookup =
                                                                                packages
                                                                                .ToLookup(p => Convert.ToChar(p.Company.Substring(0, 1)),
                                                                                          p => p.Company + " " + p.TrackingNumber);
                                                                         
                                                                            // Iterate through each IGrouping in the Lookup.
                                                                            foreach (IGrouping<char, string> packageGroup in lookup)
                                                                            {
                                                                                // Print the key value of the IGrouping.
                                                                                Console.WriteLine(packageGroup.Key);
                                                                                // Iterate through each value in the
                                                                                // IGrouping and print its value.
                                                                                foreach (string str in packageGroup)
                                                                                    Console.WriteLine("    {0}", str);
                                                                            }
                                                                        }
                                                                         
                                                                        /*
                                                                         This code produces the following output:
                                                                         
                                                                         C
                                                                             Coho Vineyard 89453312
                                                                             Contoso Pharmaceuticals 670053128
                                                                         L
                                                                             Lucerne Publishing 89112755
                                                                         W
                                                                             Wingtip Toys 299456122
                                                                             Wide World Importers 4665518773
                                                                        */*)
                                                                        end.
                                                                      Сообщение отредактировано: Cfon -
                                                                        Enumerable<T>.ToDictionary - выполняет трансформацию массива в словарь. Тоже полезная вещь, ее кстати можно селектом зафигачить :D
                                                                        ExpandedWrap disabled
                                                                          uses
                                                                            System.SysUtils,
                                                                            Spring.Collections,
                                                                            Spring.Collections.Enumerable;
                                                                           
                                                                          type
                                                                            TPackage = record
                                                                              Company: string;
                                                                              Weight: double;
                                                                              TrackingNumber: uint64;
                                                                              constructor Create(ACompany: string; AWeight: double; ATrackingNumber: uint64);
                                                                            end;
                                                                           
                                                                          { TPackage }
                                                                           
                                                                          constructor TPackage.Create(ACompany: string; AWeight: double;
                                                                            ATrackingNumber: uint64);
                                                                          begin
                                                                            Company:= ACompany;
                                                                            Weight:= AWeight;
                                                                            TrackingNumber:= ATrackingNumber;
                                                                          end;
                                                                           
                                                                          begin
                                                                            // Create a list of Packages.
                                                                            var Packages := Enumerable<TPackage>.Create([
                                                                              TPackage.Create('Coho Vineyard', 25.2, 89453312),
                                                                              TPackage.Create('Lucerne Publishing', 18.7, 89112755),
                                                                              TPackage.Create('Wingtip Toys', 6.0, 299456122),
                                                                              TPackage.Create('Adventure Works', 33.8, 4665518773)
                                                                            ]);
                                                                           
                                                                            // Create a Dictionary of Package objects,
                                                                            // using TrackingNumber as the key.
                                                                            var dictionary:= Packages.ToDictionary<UInt64>(
                                                                                function (p: TPackage): UInt64
                                                                                begin
                                                                                  Result:= p.TrackingNumber;
                                                                                end
                                                                            );
                                                                           
                                                                            for var kvp in dictionary do
                                                                            begin
                                                                              Writeln(Format('Key %d: %s, %f pounds', [
                                                                                      kvp.Key, kvp.Value.Company, kvp.Value.Weight]));
                                                                            end;
                                                                          end.
                                                                          (*C# code
                                                                            List<Package> packages =
                                                                                    new List<Package>
                                                                                        { new Package { Company = "Coho Vineyard", Weight = 25.2, TrackingNumber = 89453312L },
                                                                                          new Package { Company = "Lucerne Publishing", Weight = 18.7, TrackingNumber = 89112755L },
                                                                                          new Package { Company = "Wingtip Toys", Weight = 6.0, TrackingNumber = 299456122L },
                                                                                          new Package { Company = "Adventure Works", Weight = 33.8, TrackingNumber = 4665518773L } };
                                                                           
                                                                                // Create a Dictionary of Package objects,
                                                                                // using TrackingNumber as the key.
                                                                                Dictionary<long, Package> dictionary =
                                                                                    packages.ToDictionary(p => p.TrackingNumber);
                                                                           
                                                                                foreach (KeyValuePair<long, Package> kvp in dictionary)
                                                                                {
                                                                                    Console.WriteLine(
                                                                                        "Key {0}: {1}, {2} pounds",
                                                                                        kvp.Key,
                                                                                        kvp.Value.Company,
                                                                                        kvp.Value.Weight);
                                                                                }
                                                                            }
                                                                           
                                                                            /*
                                                                             This code produces the following output:
                                                                           
                                                                             Key 89453312: Coho Vineyard, 25.2 pounds
                                                                             Key 89112755: Lucerne Publishing, 18.7 pounds
                                                                             Key 299456122: Wingtip Toys, 6 pounds
                                                                             Key 4665518773: Adventure Works, 33.8 pounds
                                                                            */*)


                                                                        Кстати изучая Enumerable<T> обнаружил что у него есть метод Concat так что можно добавить данные и тут, хотя скорее не добавить, а создать новый Enumerable<T>, ибо Concat не изменяет массив как IList.Add:
                                                                        ExpandedWrap disabled
                                                                          var Packages2: Enumerable<TPackage>;
                                                                            Packages2:= Packages.Concat([TPackage.Create('Contoso Pharmaceuticals', 9.3, 670053128)]);

                                                                        вот так не пашет, точнее пашет но тип получается IEnumerable<TPackage>:
                                                                        ExpandedWrap disabled
                                                                          var Packages2:= Packages.Concat([TPackage.Create('Contoso Pharmaceuticals', 9.3, 670053128)]);

                                                                        надо кастить
                                                                        ExpandedWrap disabled
                                                                          var Packages2:= Enumerable<TPackage>(Packages.Concat([TPackage.Create('Contoso Pharmaceuticals', 9.3, 670053128)]));

                                                                        ну или так
                                                                        ExpandedWrap disabled
                                                                          Packages := Packages.Concat([TPackage.Create('Contoso Pharmaceuticals', 9.3, 670053128)]);

                                                                        но это не в духе функционального прогинга, но кто запретит :D
                                                                        Думаю если это не будет привычкой, то все можно если осторожно :rake:
                                                                        Сообщение отредактировано: Cfon -
                                                                          еще два вызова ToDictionary, первый в качестве значения добавляет строку, а не запись TPackage, а второй юзает дефолтный сравниватель, что по идее является вариантом первого вызова :D
                                                                          ExpandedWrap disabled
                                                                            ...
                                                                              // Result: IDictionary<UInt64, STRING>
                                                                              var dictionary := Packages.ToDictionary<UInt64, STRING>(
                                                                                  function (p: TPackage): UInt64
                                                                                  begin
                                                                                    Result:= p.TrackingNumber;
                                                                                  end,
                                                                                  function (p: TPackage): STRING
                                                                                  begin
                                                                                    Result:= p.Company;
                                                                                  end
                                                                              );
                                                                            ....

                                                                          ExpandedWrap disabled
                                                                            ...
                                                                              // Result: IDictionary<UInt64, TPackage>
                                                                              var dictionary := Packages.ToDictionary<UInt64>(
                                                                                  function (p: TPackage): UInt64
                                                                                  begin
                                                                                    Result:= p.TrackingNumber;
                                                                                  end,
                                                                                  TEqualityComparer<UInt64>.Default
                                                                              );
                                                                            ...


                                                                          Добавлено
                                                                          Пример со словарем IDictionary<TKey, TValue>
                                                                          ExpandedWrap disabled
                                                                            uses
                                                                              System.SysUtils,
                                                                              System.Generics.Defaults,
                                                                              Spring.Collections;
                                                                             
                                                                            type
                                                                              TBox = class
                                                                                Height: integer;
                                                                                Width: integer;
                                                                                Length: integer;
                                                                                constructor Create(AHeight, ALength, AWidth: integer);
                                                                                function ToString: string; override;
                                                                              end;
                                                                             
                                                                              TBoxEqualityComparer = class(TInterfacedObject, IEqualityComparer<TBox>)
                                                                                function Equals(const Left, Right: TBox): Boolean; reintroduce;
                                                                                function GetHashCode(const Value: TBox): Integer; reintroduce;
                                                                              end;
                                                                             
                                                                              TBoxManager = record
                                                                                class procedure AddBox(dict: IDictionary<TBox, String>; box: TBox; name: string); static;
                                                                              end;
                                                                             
                                                                            { TBox }
                                                                             
                                                                            constructor TBox.Create(AHeight, ALength, AWidth: integer);
                                                                            begin
                                                                              Height := AHeight;
                                                                              Length := ALength;
                                                                              Width := AWidth;
                                                                            end;
                                                                             
                                                                            function TBox.ToString: string;
                                                                            begin
                                                                              Result:= Format('(%d, %d, %d)', [Height, Length, Width]);
                                                                            end;
                                                                             
                                                                            { TBoxEqualityComparer }
                                                                             
                                                                            function TBoxEqualityComparer.Equals(const Left, Right: TBox): Boolean;
                                                                            begin
                                                                               if (Left.Height = Right.Height)
                                                                                      and (Left.Length = Right.Length)
                                                                                      and (Left.Width = Right.Width)  then
                                                                                Result:= True
                                                                              else
                                                                                Result:= False;
                                                                            end;
                                                                             
                                                                            function TBoxEqualityComparer.GetHashCode(const Value: TBox): Integer;
                                                                            begin
                                                                              var hCode := Value.Height xor Value.Length xor Value.Width;
                                                                              Result:= hCode;
                                                                            end;
                                                                             
                                                                            { TBoxManager }
                                                                             
                                                                            class procedure TBoxManager.AddBox(dict: IDictionary<TBox, String>; box: TBox;
                                                                              name: string);
                                                                            begin
                                                                              try
                                                                                dict.Add(box, name);
                                                                              except
                                                                                on E: EListError do
                                                                                  WriteLn(Format('Unable to add %s: %s', [box.ToString, E.Message]));
                                                                              end;
                                                                            end;
                                                                             
                                                                            { DictionaryExample }
                                                                             
                                                                            procedure DictionaryExample;
                                                                            begin
                                                                              var comparer:= TBoxEqualityComparer.Create;
                                                                              var boxes:= TCollections.CreateDictionary<TBox, string>(
                                                                                comparer
                                                                            // TEqualityComparer<TBox>.Default
                                                                              );
                                                                             
                                                                              var redBox:= TBox.Create(4,3,4);
                                                                              TBoxManager.AddBox(boxes, redBox, 'red');
                                                                             
                                                                              var blueBox:= TBox.Create(4,3,4);
                                                                              TBoxManager.AddBox(boxes, blueBox, 'blue');
                                                                             
                                                                              var greenBox:= TBox.Create(3,4,3);
                                                                              TBoxManager.AddBox(boxes, greenBox, 'green');
                                                                             
                                                                              Writeln;
                                                                              Writeln(Format('The dictionary contains %d Box objects.', [boxes.Count]));
                                                                            end;
                                                                            // The example displays the following output:
                                                                            //    Unable to add (4, 3, 4): Duplicates not allowed
                                                                            //
                                                                            //    The dictionary contains 2 Box objects.
                                                                             
                                                                            end.

                                                                          Тут дефолтный сравнитель не катит, ибо он сравнивает адреса объектов TBox, поэтому пишем кастомный сравнитель.
                                                                          Если же заменить класс TBox на запись, то дефотный прокатит.
                                                                          Да если скорость добавления объектов в словарь критична, то в TBoxEqualityComparer.Equals можно добавить в начало проверку адресов объектов.
                                                                          Думаю на 100 тыщах будет заметен эффект :D
                                                                          Сообщение отредактировано: Cfon -
                                                                            Цитата Cfon @
                                                                            Enumerable<T>.ToDictionary - выполняет трансформацию массива в словарь. Тоже полезная вещь, ее кстати можно селектом зафигачить :D

                                                                            ошибочка не селектом, а агригейтом :D
                                                                            селект возварщает тот же тип контейнера его нельзя изменить, а вот агригейтом можно что угодно вернуть
                                                                            ExpandedWrap disabled
                                                                              // Create a Dictionary of Package objects,
                                                                                // using TrackingNumber as the key.
                                                                                // Result:  IDictionary<Uint64, TPackage>
                                                                                var dictionary:= Packages.Aggregate<IDictionary<Uint64, TPackage>>(
                                                                                  TCollections.CreateDictionary<Uint64, TPackage>,
                                                                                  function (dict: IDictionary<Uint64, TPackage>; p: TPackage): IDictionary<Uint64, TPackage>
                                                                                  begin
                                                                                    dict.Add(p.TrackingNumber, p);
                                                                                    Result:= dict;
                                                                                  end
                                                                                );


                                                                            Добавлено
                                                                            Пример со словарем функциональном стиле, тут мы определяем объект сразу по месту, а не в виде отдельного класса:
                                                                            ExpandedWrap disabled
                                                                              begin
                                                                                // Result: IDictionary<TBox, string>
                                                                                var boxes:= TCollections.CreateDictionary<TBox, string>(
                                                                                  TEqualityComparer<TBox>.Construct(
                                                                                    function(const Left, Right: TBox): Boolean
                                                                                    begin
                                                                                      if  Left = Right then
                                                                                        Result:= True
                                                                                      else if (Left.Height = Right.Height) and
                                                                                          (Left.Length = Right.Length) and
                                                                                          (Left.Width = Right.Width)  then
                                                                                        Result:= True
                                                                                      else
                                                                                        Result:= False;
                                                                                    end,
                                                                                    function(const Value: TBox): Integer
                                                                                    begin
                                                                                      Result:= Value.Height xor Value.Length xor Value.Width;
                                                                                    end
                                                                                  )
                                                                                );
                                                                               
                                                                                var redBox:= TBox.Create(4,3,4);
                                                                                TBoxManager.AddBox(boxes, redBox, 'red');
                                                                                TBoxManager.AddBox(boxes, redBox, 'red2');
                                                                               
                                                                                var blueBox:= TBox.Create(4,3,4);
                                                                                TBoxManager.AddBox(boxes, blueBox, 'blue');
                                                                               
                                                                                var greenBox:= TBox.Create(3,4,3);
                                                                                TBoxManager.AddBox(boxes, greenBox, 'green');
                                                                               
                                                                                Writeln;
                                                                                Writeln(Format('The dictionary contains %d Box objects.', [boxes.Count]));
                                                                              end;
                                                                              // The example displays the following output:
                                                                              //    Unable to add (4, 3, 4): Duplicates not allowed
                                                                              //    Unable to add (4, 3, 4): Duplicates not allowed
                                                                              //
                                                                              //    The dictionary contains 2 Box objects.

                                                                            Замечу что надо отдельно удалять объекты TBox, их словарь не чистит.

                                                                            Добавлено
                                                                            Цитата Cfon @
                                                                            Да если скорость добавления объектов в словарь критична, то в TBoxEqualityComparer.Equals можно добавить в начало проверку адресов объектов.
                                                                            Думаю на 100 тыщах будет заметен эффект :D

                                                                            Оуоу.. неа, тока если одинаковых объектов будет много, например, 50% :D
                                                                            Почему? Потому шо сравнитель запускает проверку на equals тока если хешер вернет одинаковое значение для двух объектов. Поэтому время выполнения вставки данных зависит от скорости хешера, а не от проверки на идентичность.
                                                                            Сообщение отредактировано: Cfon -
                                                                              А вот и сюрприз! Чекал OrderBy все путем полет нормальный, далее ThenBy и все тупик :D
                                                                              Короче объясняю OrderBy всем понятно сортировка, а вот ThenBy это дополнение к сортировке заданой OrderBy, т.е. например нам надо сортировать по длине строки и в алфавитном порядке, но в Spring делфи это не пашет! Следущий пример сначало сортирует по длине строки, ну а потом в алфавитном порядке:
                                                                              ExpandedWrap disabled
                                                                                begin
                                                                                  var fruits := Enumerable<string>.Create([
                                                                                    'grape', 'passionfruit', 'banana', 'mango',
                                                                                    'orange', 'raspberry', 'apple', 'blueberry'
                                                                                  ]);
                                                                                 
                                                                                  // Sort the strings first by their length and then
                                                                                  //alphabetically by passing the identity selector function.
                                                                                  var query := fruits.OrderBy<integer>(
                                                                                    function (fruit: string): integer
                                                                                    begin
                                                                                      Result:= Length(fruit);
                                                                                    end
                                                                                  );
                                                                                 
                                                                                  var query2:= Enumerable<string>.Create(query.ToArray)
                                                                                    .ThenBy<string>(
                                                                                      function (fruit: string): string
                                                                                      begin
                                                                                        Result:= fruit;
                                                                                      end
                                                                                    );
                                                                                 
                                                                                  query2.ForEach(procedure (const fruit: string)
                                                                                    begin
                                                                                      Writeln(fruit);
                                                                                    end
                                                                                  );
                                                                                  (* у меня output:
                                                                                    apple
                                                                                    banana
                                                                                    blueberry
                                                                                    grape
                                                                                    mango
                                                                                    orange
                                                                                    passionfruit
                                                                                    raspberry
                                                                                  *)
                                                                                  (* а надо:
                                                                                    apple
                                                                                    grape
                                                                                    mango
                                                                                    banana
                                                                                    orange
                                                                                    blueberry
                                                                                    raspberry
                                                                                    passionfruit
                                                                                  *)
                                                                                end;
                                                                                (*C# code
                                                                                  string[] fruits = { "grape", "passionfruit", "banana", "mango",
                                                                                                        "orange", "raspberry", "apple", "blueberry" };
                                                                                 
                                                                                  // Sort the strings first by their length and then
                                                                                  //alphabetically by passing the identity selector function.
                                                                                  IEnumerable<string> query =
                                                                                      fruits.OrderBy(fruit => fruit.Length).ThenBy(fruit => fruit);
                                                                                 
                                                                                  foreach (string fruit in query)
                                                                                  {
                                                                                      Console.WriteLine(fruit);
                                                                                  }
                                                                                 
                                                                                  /*
                                                                                      This code produces the following output:
                                                                                 
                                                                                      apple
                                                                                      grape
                                                                                      mango
                                                                                      banana
                                                                                      orange
                                                                                      blueberry
                                                                                      raspberry
                                                                                      passionfruit
                                                                                  */*)

                                                                              Я хз зачем тогда ваще нужен ThenBy :wacko:

                                                                              Добавлено
                                                                              Все понел в чем трабла :D
                                                                              Дело в том что в предыдущем примере имел место разрыв цепочки отложенного выполнения и вызов запроса сортировки через query.ToArray, следущий запрос query2 уже выполнял сортировку на уже отсортированых данных.
                                                                              Как я решил траблу? Все гениальное просто :blush: определил функции ThenBy в классе TEnumerableHelper для TEnumerable и юзал их :D
                                                                              ExpandedWrap disabled
                                                                                type
                                                                                  TEnumerableHelper = class helper for TEnumerable
                                                                                    ...
                                                                                 
                                                                                    class function ThenBy<T, TKey>(const source: IEnumerable<T>;
                                                                                      const keySelector: TFunc<T, TKey>) : IEnumerable<T>; overload; static;
                                                                                 
                                                                                    class function ThenBy<T, TKey>(const source: IEnumerable<T>;
                                                                                      const keySelector: TFunc<T, TKey>;
                                                                                      const comparer: IComparer<TKey>): IEnumerable<T>; overload; static;
                                                                                  end;
                                                                                 
                                                                                { TEnumerableHelper }
                                                                                 
                                                                                class function TEnumerableHelper.ThenBy<T, TKey>(const source: IEnumerable<T>;
                                                                                  const keySelector: TFunc<T, TKey>;
                                                                                  const comparer: IComparer<TKey>): IEnumerable<T>;
                                                                                begin
                                                                                //  Guard.CheckNotNull(Assigned(source), 'source');
                                                                                //  Guard.CheckNotNull(Assigned(keySelector), 'keySelector');
                                                                                 
                                                                                  Result := TOrderedEnumerable<T, TKey>.Create(source, keySelector, comparer, False);
                                                                                end;
                                                                                 
                                                                                class function TEnumerableHelper.ThenBy<T, TKey>(const source: IEnumerable<T>;
                                                                                  const keySelector: TFunc<T, TKey>): IEnumerable<T>;
                                                                                begin
                                                                                  Result := ThenBy<T, TKey>(source, keySelector, nil);
                                                                                end;

                                                                              Теперь все пучком :dance: :hang:
                                                                              ExpandedWrap disabled
                                                                                begin
                                                                                  var fruits := TCollections.CreateList<string>([
                                                                                    'grape', 'passionfruit', 'banana', 'mango',
                                                                                    'orange', 'raspberry', 'apple', 'blueberry'
                                                                                  ]);
                                                                                  
                                                                                  // Sort the strings first by their length and then
                                                                                  //alphabetically by passing the identity selector function.
                                                                                  var query := TEnumerable.OrderBy<string, integer>(fruits,
                                                                                    function (fruit: string): integer
                                                                                    begin
                                                                                      Result:= Length(fruit);
                                                                                    end
                                                                                  );
                                                                                 
                                                                                  // отложеное выполнение, ибо нет вызова query.ToArray
                                                                                  var query2:= TEnumerable.ThenBy<string, string>(query,
                                                                                    function (fruit: string): string
                                                                                    begin
                                                                                      Result:= fruit;
                                                                                    end
                                                                                  );
                                                                                 
                                                                                  // а теперь сам запрос
                                                                                  query2.ForEach(procedure (const fruit: string)
                                                                                    begin
                                                                                      Writeln(fruit);
                                                                                    end
                                                                                  );  
                                                                                  (*
                                                                                      apple
                                                                                      grape
                                                                                      mango
                                                                                      banana
                                                                                      orange
                                                                                      blueberry
                                                                                      raspberry
                                                                                      passionfruit
                                                                                  *)
                                                                                end.

                                                                              - Маста так а в чем проблема Enumerable<T>? Почему на TEnumerable пашет то? :huh:
                                                                              Проблема в разрыве цепочки отложеного выполнения, Enumerable<T>.OrderBy возвращает IEnumerable<T> у которого нет ThenBy, но который есть в Enumerable<T>.
                                                                              А чтобы юзать Enumerable<T> надо снова его создать и передать предыдущий запрос, но поскольку в конструктор Enumerable<T> принимает тока динамический массив, а не ссылку на IEnumerable<T>, то надо копировать его через ToArray, что приводит к выполнению запроса и как следствие прерывание отложенного выполнения. Последующий запрос уже будет сортировать другие данные. Поэтому я определил в TEnumerableHelper ThenBy и передавал ему ссылку на IEnumerable<T>, что не прервало отложенного выполнения. :D
                                                                              - Маста вы, вы, вы... ну... просто маста! :jokingly:
                                                                              Думаешь? :D
                                                                              - Определено :jokingly:
                                                                              Ну ок :popcorn:

                                                                              - Маста это снова я! А почему б не определить хелпер для Enumerable<T>? :huh:
                                                                              Оуоу! Не получится Enumerable<T> это генерик, а на генерики хелпер нельзя наложить.
                                                                              - А зачем ваще хелпер для TEnumerable? Можно просто создать функцию ThenBy.. :huh:
                                                                              Ну да можно, но мы ж пишем не гавнокод верно? :D
                                                                              В Spring.Collections уже есть TEnumerable и в нем уже были определния некоторых функций из Enumerable<T>, вот я и добавляю еще через хелпер.
                                                                              Хотя стоп эти функции параметризованы, их нельзя создать вне класса или записи, поэтому надо по любому создавать отдельно класс или запись.

                                                                              ПС. Так я скоро на книжку напечатаю :lool:
                                                                              Сообщение отредактировано: Cfon -
                                                                                ОК. Задание!
                                                                                Надо попрактиковаться на примере, возьмем пример с рецептами.
                                                                                Вывести данные в виде:
                                                                                - название рецепта
                                                                                -- список названий отсортированных инградиентов.
                                                                                Сообщение отредактировано: Cfon -
                                                                                  Ответ :D
                                                                                  ExpandedWrap disabled
                                                                                    type
                                                                                      TRecipeNameAndIngredientNames = record
                                                                                        RecipeName: string;
                                                                                        IngredientNames: IList<string>;
                                                                                        constructor Create(ARecipeName: string; AIngredientNames: IList<string>);
                                                                                      end;
                                                                                     
                                                                                    { TRecipeNameAndIngredientNames }
                                                                                     
                                                                                    constructor TRecipeNameAndIngredientNames.Create(ARecipeName: string;
                                                                                      AIngredientNames: IList<string>);
                                                                                    begin
                                                                                      RecipeName:= ARecipeName;
                                                                                      IngredientNames:= AIngredientNames;
                                                                                    end;
                                                                                     
                                                                                    begin
                                                                                      // load json file.
                                                                                      var s:= TRecipesManager.LoadFile(TPath.Combine(
                                                                                                      TPath.GetDocumentsPath, 'recipes.json'));
                                                                                     
                                                                                      // create recipes list.
                                                                                      var recipes: IList<TRecipe> := TRecipesManager.CreateRecipes(s);
                                                                                    ....
                                                                                     
                                                                                      var query := TEnumerable.Select<TRecipe, TRecipeNameAndIngredientNames>(recipes,
                                                                                          function (recipe: TRecipe): TRecipeNameAndIngredientNames
                                                                                          begin
                                                                                            var ingredientNames := TEnumerable.Select<TIngredient, string>(
                                                                                              recipe.Ingredients,
                                                                                              function (ingredient: TIngredient): string
                                                                                              begin
                                                                                                Result:= ingredient.IngredientName;
                                                                                              end
                                                                                            );
                                                                                            var sorted:= TEnumerable.OrderBy<string, string>(ingredientNames,
                                                                                              function (ingredientName: string): string
                                                                                              begin
                                                                                                Result:= ingredientName;
                                                                                              end
                                                                                            );
                                                                                     
                                                                                            Result:= TRecipeNameAndIngredientNames.Create(recipe.RecipeName, IList<string>(sorted));
                                                                                          end
                                                                                        );
                                                                                     
                                                                                      // print the results.
                                                                                      query.ForEach(procedure (const value: TRecipeNameAndIngredientNames)
                                                                                        begin
                                                                                          Writeln(value.RecipeName);
                                                                                          value.IngredientNames.ForEach(procedure (const name: string)
                                                                                            begin
                                                                                              Writeln('    ', name);
                                                                                            end
                                                                                          );
                                                                                        end
                                                                                      );  
                                                                                    (*
                                                                                     Baked Salmon
                                                                                        Butter Lettuce
                                                                                        Garlic
                                                                                        Olive Oil
                                                                                        Pine Nuts
                                                                                        Salmon
                                                                                        Yellow Squash
                                                                                    Fish Tacos
                                                                                        Cheese
                                                                                        Iceberg Lettuce
                                                                                        Tomatoes
                                                                                        Tortillas
                                                                                        Whitefish
                                                                                    Checken Noodle Soup
                                                                                        Carrots
                                                                                        Celery
                                                                                        Chiken Broth
                                                                                        Cooked Chicken
                                                                                        Extra-virgin olive oil
                                                                                        Wide Egg Noodles
                                                                                        Yello Onion
                                                                                    *)
                                                                                    end.

                                                                                  У кого возник тихий ужос? Понимаю! :crazy:
                                                                                  Тут нужна практика, это в основном из-за избыточного синтаксиса параметров генериков Делфи, в сравнении с JS или тем же C# это мягко говоря неудобно :jokingly:

                                                                                  Вот более удобоваримый код, тут просто вывод на консоль без создания списка, генериков тут нет и читать проще :D
                                                                                  Точнее генерики то есть параметризованые анонимки, нет угловых скобок :D
                                                                                  ExpandedWrap disabled
                                                                                    recipes.ForEach(procedure (const recipe: TRecipe)
                                                                                      begin
                                                                                        Writeln(recipe.RecipeName);
                                                                                        recipe.Ingredients.Ordered(
                                                                                          function(const Left, Right: TIngredient): Integer
                                                                                          begin
                                                                                            if Left.IngredientName < Right.IngredientName then
                                                                                              Result:= -1
                                                                                            else if Left.IngredientName = Right.IngredientName then
                                                                                              Result:= 0
                                                                                            else
                                                                                              Result:= 1;
                                                                                          end)
                                                                                          .ForEach(procedure (const ingredient: TIngredient)
                                                                                          begin
                                                                                            Writeln('   ', ingredient.IngredientName);
                                                                                          end);
                                                                                      end);
                                                                                  Сообщение отредактировано: Cfon -
                                                                                    Давайте сравним императивный подход с функциональным:
                                                                                    ExpandedWrap disabled
                                                                                        // Императивный стиль
                                                                                        var resultList: TArray<TRecipeNameAndIngredientNames>;
                                                                                        for var recipe: TRecipe in recipes do
                                                                                        begin
                                                                                          var listIngrNames: TArray<string>;
                                                                                          for var ingr in recipe.Ingredients do
                                                                                          begin
                                                                                            listIngrNames:= listIngrNames + [ingr.IngredientName];
                                                                                          end;
                                                                                          TArray.Sort<string>(listIngrNames);
                                                                                          resultList:= resultList + [TRecipeNameAndIngredientNames.Create(recipe.RecipeName, listIngrNames)];
                                                                                        end;
                                                                                       
                                                                                        // Функциональный
                                                                                        const resultList = TEnumerable.Select<TRecipe, TRecipeNameAndIngredientNames>(recipes,
                                                                                          function (recipe: TRecipe): TRecipeNameAndIngredientNames
                                                                                          begin
                                                                                            const ingredientNames := TEnumerable.Select<TIngredient, string>(recipe.Ingredients,
                                                                                              function (ingredient: TIngredient): string
                                                                                              begin
                                                                                                Result:= ingredient.IngredientName;
                                                                                              end);
                                                                                            const sorted:= TEnumerable.OrderBy<string, string>(ingredientNames,
                                                                                              function (ingredientName: string): string
                                                                                              begin
                                                                                                Result:= ingredientName;
                                                                                              end);
                                                                                            Result:= TRecipeNameAndIngredientNames.Create(recipe.RecipeName, IList<string>(sorted));
                                                                                          end);

                                                                                    Как по мне императивный сложно сразу понять что он делает, особенно без комментов, а вот функциональный читается без коментов. Плас функц. имеет на один меньше цикл, у него цикл сортировки и селекта объединен в силу отложеного выполнения. Т.е. потенционально он будет быстрее при большом числе записей.
                                                                                    Кроме того Функц. легко распарралелить, поскольку данные неизменяемы.
                                                                                    Плас императивного он немного короче, хотя он короче чем на Делфи, в отношении C# сами видели не короче :D
                                                                                    Сообщение отредактировано: Cfon -
                                                                                      Немного лекций :writer:
                                                                                      Что отличает функциональный код от императивного?
                                                                                      - Неизменяемые данные
                                                                                      - Чистые функции (идемпотентные)
                                                                                      - Функции высшего порядка (преобразование данных, композиция, рекурсия, каррирование).

                                                                                      Ну с неизменяемостью данных понятно. В чисто функц. языках они не меняются, но делфи не является чисто функциональным языком, он просто поддерживает некоторые его концепции, но только не консистентность данных. Данные в делфи можно легко случайно изменить, поэтому надо быть внимтельным, хотя можно юзать везде константы это защитит от случайного изменения данных :D

                                                                                      - Маста я не пойму, как прогать если данные не меняются? :wacko:
                                                                                      Новичок, данные меняются, но не изменяются :blink: :lool:
                                                                                      - Это как? :huh:
                                                                                      Если мы объявим переменную, то сразу записываем туда значение, потом его не изменяем. Если мы хотим изменить эти данные, то просто передаем его преобразователю данных.
                                                                                      - Это что за хрень? :jokingly:
                                                                                      Так называют функции высшего порядка :punish:
                                                                                      Например нам надо изменить массив строк, что мы делаем мы передаем его в функцию map или reduce в качестве источника истины :D, а также передаем вторым параметром функцию первого класса, которая собственно преобразует данные, далее результат возвращается и присваивается другой переменной все епт :D
                                                                                      Другими словами исходные данные не меняются, создается новые измененые данные.
                                                                                      - Маста а можно все тоже, но на примере? :scratch:
                                                                                      Угу ща Новичок сообразимс! :D
                                                                                      - Подождем
                                                                                      Уфф вот написал, тут я юзанул Select из Spring это аналог map:
                                                                                      ExpandedWrap disabled
                                                                                        begin
                                                                                          var strs := TCollections.CreateList<string>([
                                                                                            'grape', 'passionfruit', 'banana', 'mango',
                                                                                            'orange', 'raspberry', 'apple', 'blueberry'
                                                                                          ]);
                                                                                         
                                                                                          var ints:= TEnumerable.Select<string, integer>(strs,
                                                                                            function (s: string): integer
                                                                                            begin
                                                                                              Result:= Length(s);
                                                                                            end
                                                                                          );
                                                                                         
                                                                                          // print
                                                                                          for var i in ints do
                                                                                          begin
                                                                                            Write(i, ', ');
                                                                                          end;
                                                                                          // 5, 12, 6, 5, 6, 9, 5, 9,
                                                                                         
                                                                                           Writeln;
                                                                                         
                                                                                          for var s in strs do
                                                                                          begin
                                                                                            Write(s, ', ');
                                                                                          end;
                                                                                          // grape, passionfruit, banana, mango, orange, raspberry, apple, blueberry,
                                                                                        end.

                                                                                      Как видишь имеем два массива с разными данными.
                                                                                      - Маста а что надо всегда менять тип данных? :huh:
                                                                                      Нет конешно можно не менять, например если нам надо удалить слово "банан", то пишем следущее:
                                                                                      ExpandedWrap disabled
                                                                                        var deletedBananas:= strs.Where(
                                                                                            function (const s: string): Boolean
                                                                                            begin
                                                                                              if s = 'banana' then
                                                                                                Result:= False
                                                                                              else
                                                                                                Result:= True;
                                                                                            end
                                                                                          );
                                                                                         
                                                                                          // print
                                                                                          for var s in deletedBananas do
                                                                                          begin
                                                                                            Write(s, ', ');
                                                                                          end;
                                                                                          // grape, passionfruit, mango, orange, raspberry, apple, blueberry,

                                                                                      Тут я заюзал функцию Where тоже и Spring4D это аналог filter.
                                                                                      - Ого начинаю понимать, а если нам надо удалить не банан, а passionfruit :D
                                                                                      - Че нам опять все писать Where? :huh:
                                                                                      Зачем? Надо определить функцию и передавать туда значение которое надо удалить! Давай напиши ее а я заценю.
                                                                                      Сообщение отредактировано: Cfon -
                                                                                        - Маста вот что у меня получилось, я заюзал анонимку для простоты, все как вы учили, есть вопрос
                                                                                        ExpandedWrap disabled
                                                                                          var Deleter := function (AValue: string): //<-- что возвращать???
                                                                                              begin
                                                                                                Result:= strs.Where(
                                                                                                  function (const s: string): Boolean
                                                                                                  begin
                                                                                                    Result:= not (s = AValue);
                                                                                                  end
                                                                                                );
                                                                                              end;

                                                                                        Какой?
                                                                                        - Что возвращать то? :huh:
                                                                                        Хороший вопрос :D
                                                                                        Возвращать можно что угодно, главное вернуть! :D это один из принципов функционального программирования. Функции принимают что-то и возвращают что-то. Только тогда они могут быть чистыми.
                                                                                        Ок, коль скоро мы программируем в Spring4D, то вернуть надо интерфейс IEnumerable<T>. В твоем случае IEnumerable<string>, поскольку именно его возвращает Where видишь ты ее вызов присвоил Result? Вот поэтому возвращаем IEnumerable<string>.
                                                                                        - Е-е все работает!
                                                                                        ExpandedWrap disabled
                                                                                          var deletedBananas:= Deleter('banana');

                                                                                        Добже Новичок, а теперь давай сделаем еще один шаг и унифицируем нашу функцию. Ну т.е. сделаем из нее дженерик, чтобы можно было вызвать на любых данных. Сможешь? :)
                                                                                        - Попробую :huh:

                                                                                        Добавлено
                                                                                        - Вот че получилось, ругается :huh:
                                                                                        ExpandedWrap disabled
                                                                                          type
                                                                                            TExample = record
                                                                                              class function Delete<T>(ASource: IEnumerable<T>; AValue: T): IEnumerable<T>; static;
                                                                                            end;
                                                                                           
                                                                                          class function TExample.Delete<T>(ASource: IEnumerable<T>; AValue: T): IEnumerable<T>;
                                                                                          begin
                                                                                            Result := ASource.Where(
                                                                                              function (const s: T): Boolean
                                                                                              begin
                                                                                                if s = AValue then //<-- тут ошибка "E2015 Operator not applicable to this operand type"
                                                                                                  Result:= False
                                                                                                else
                                                                                                  Result:= True;
                                                                                              end
                                                                                            );
                                                                                          end;

                                                                                        Новичок красавчек! Ну ты уже не новичок раз такое написал! :D
                                                                                        На счет ошибки все верно там у тебя идет проверка на равенство, компилятору неизвестно какого типа является T и как их сравнивать.
                                                                                        - А че делать? :huh:
                                                                                        Юзать дефотный компарер
                                                                                        ExpandedWrap disabled
                                                                                          class function TExample.Delete<T>(ASource: IEnumerable<T>; AValue: T): IEnumerable<T>;
                                                                                          begin
                                                                                            Result := ASource.Where(
                                                                                              function (const ALeft: T): Boolean
                                                                                              begin
                                                                                                if TComparer<T>.Default.Compare(ALeft, AValue) = 0 then
                                                                                                  Result:= False
                                                                                                else
                                                                                                  Result:= True;
                                                                                              end
                                                                                            );
                                                                                          end;

                                                                                        Начинку Default не иследовал, оставляю тебе для самостоятельного изучения :D
                                                                                        - Ух ты! Работает! :good:

                                                                                        - Маста функционнально прогать конешно прикольно, но память то не резиновая, это сколько же объектов будет в итоге если их все копировать и менять :wacko:
                                                                                        Новичок ты меня удивляешь, ты что ли их копируешь? Тебе че больше всех надо? :lool:
                                                                                        - :huh:
                                                                                        Лана я пошутил :jokingly:
                                                                                        Все дело в том что объекты не копятся не нужные объекты сразу или почти сразу удаляются! Вот к примеру наш код, тут strs и deletedBananas будут удалены, как только ссылки на них буду равны nil или они выйдут из области видимости.
                                                                                        Сообщение отредактировано: Cfon -
                                                                                          - Маста, а как насчет времени затраченое на копирование данных? :huh:
                                                                                          Ну тут тоже есть свои секреты :D
                                                                                          Как в Делфи не знаю точно, но например в Хаскеле данные, если к примеру брать два списка, не копируются, а создаются ссылки на одни и те же данные. Само копирование происходит тока тогда когда данные изменяются, вот тогда создается копия и затем эта копия изменяется. Так что никаких лишних копий нет. Заметь копия не всех данных, а ток тех что надо изменить.
                                                                                          Надо почитать про то как Делфи управляет памятью. Думаю что делфиразрабы тоже это учитывают ибо не просто так они добавили функциональные возможности типо ананимок, вывод типов, паралельное програмирование и тд.
                                                                                          - Ясно почитаю, но в Хаскеле хитро придумано :)
                                                                                          Ну как бэ не пальцем деланый язык то :D
                                                                                          да и к слову сразу про рекурсии в Хаскеле они в итоге преобразуются на уровне бинарного кода в обычные циклы :D так что у тут нет замедления от их юзания.
                                                                                          Ну естестно поэтому компилятор Хаскеля на порядок медленнее например чем в Делфи :D
                                                                                          В Делфи как я вижу походу самый быстрый компилятор из тех что я видел.
                                                                                          Сообщение отредактировано: Cfon -
                                                                                            - Маста, а если дефолтное сравнение не катит, что делать? :huh:
                                                                                            Тогда надо вынести логику сравнения наружу и дать возможность самим определить как сравнивать объекты :)
                                                                                            - А пример можно?
                                                                                            Ок, смотри я немного изменил твое определение дженерика
                                                                                            ExpandedWrap disabled
                                                                                              class function TExample.Delete<T>(ASource: IEnumerable<T>; AValue: T;
                                                                                                const AComparison: TEqualityComparison<T>): IEnumerable<T>;
                                                                                              begin
                                                                                                Result := ASource.Where(
                                                                                                  function (const ALeft: T): Boolean
                                                                                                  begin
                                                                                                    Result:= AComparison(ALeft, AValue); //<-- тут наша логика вызывается
                                                                                                  end
                                                                                                );
                                                                                              end;
                                                                                               
                                                                                              var deletedBananas:= TExample.Delete<string>(strs, 'banana',
                                                                                                  function (const Left, Right: string): integer //<-- тут определяем логику через анонимку
                                                                                                  begin
                                                                                                    Result:= not (Left = Right);
                                                                                                  end
                                                                                                );

                                                                                            - Вау! Маста вы вы вы..
                                                                                            Что? Гений? :popcorn:
                                                                                            - Да однозначно :jokingly:
                                                                                            Сообщение отредактировано: Cfon -
                                                                                              Новичок! :D
                                                                                              - Да учитель? :huh:
                                                                                              Учитель :lool:
                                                                                              Зови меня Коуч :D
                                                                                              - Ок Коуч :jokingly:
                                                                                              - А кто это пардон? :huh:
                                                                                              :blink: неважно :D
                                                                                              Короче студент пока ты спал я тут немного код поменял в посте #45. Заменил TComparison<T> на TEqualityComparison<T> ибо TComparison возвращал integer, а нам надо boolean что приводило к лишним проверкам.
                                                                                              - О Коуч так действительно более логично :)
                                                                                              - У меня вопрос
                                                                                              Какой?
                                                                                              - Что такое чистые функции? Вы вначале писали.
                                                                                              Чистыми называют те функции, которые что-то получают и что-то возвращяют, при этом они не должны изменять какие-то внешние состояния.
                                                                                              - Примерчик можно? :huh:
                                                                                              Да вот например пример чистой анонимной функции:
                                                                                              ExpandedWrap disabled
                                                                                                function (s: string): integer
                                                                                                begin
                                                                                                  Result:= Length(s);
                                                                                                end

                                                                                              она пулучает строку, а возвращает число, ее результат полнойстью зависит от параметра и всегда возращает одно и тоже значение для одного и того же агрумента. При это в теле функции нет обращений к глобальным переменным или функциям, которые бы были не чистыми. Length тоже чистая функция, поскольку получает строку и возвращает ее длину, я просто обернул ее в анонимку :D
                                                                                              - Угу а почему они так важны в функционльном програмировании?
                                                                                              По причине что не меняют внешнее состояние, что как я уже говорил является первм условием функциональной парадигмы. Кроме того чистые функции легко тестировать.
                                                                                              - А пример грязной функции :jokingly:
                                                                                              Ну вот например пример :D
                                                                                              ExpandedWrap disabled
                                                                                                function(i: integer): integer
                                                                                                begin
                                                                                                  Result:= Random(i);
                                                                                                end

                                                                                              Хотя она принимает один аргумент и возвращает значение, в ней есть вызов грязной функции Random, которая каждый раз возвращает разные значения для одного и того же аргумента. Или вот еще пример
                                                                                              ExpandedWrap disabled
                                                                                                var i:= 100;
                                                                                                function: integer
                                                                                                begin
                                                                                                  Result:= Inc(i);
                                                                                                end

                                                                                              Тут мы имеем дело с изменением внешней переменной.
                                                                                              - Понятно Коуч! :D
                                                                                              - Еще вопрос... я тут пытался понять как читать вызовы методов Spring, но тщетно :wacko: не могли бы вы объяснить как их читать?
                                                                                              Новичок, я сам до недавнего времени разбирался как их читать :D
                                                                                              Сообщение отредактировано: Cfon -
                                                                                                - Коуч, при всем уважении, но я думаю что лучше удалять прямо из списка sts :huh:
                                                                                                ExpandedWrap disabled
                                                                                                  strs.Remove('banana');

                                                                                                Да Новичок ты прав! Черт побери! :D
                                                                                                При всех преимуществах функционального подхода, не стоит игнорировать императивный стиль, ибо в конце концов нам придеться взаимодействовать с гуем например, чтобы изменять состояние в разных его окошках.
                                                                                                В данном случае чтоб удалить банан :D естественно надо юзать Remove, при условии конешно, если нам надо изменить сам strs. Преимущество Делфи в том что тут можно совместит два смежной методики, програмируя бизнес-логики приложения в функциональном стиле, а гуй в императивном.
                                                                                                Сообщение отредактировано: Cfon -
                                                                                                  Пример с уже знакомым Aggregate + Union + Empty.
                                                                                                  Aggregate - сведение, Union - объдинение, Empty - пустой массив.
                                                                                                  ExpandedWrap disabled
                                                                                                    uses
                                                                                                      Spring.Collections,
                                                                                                      Spring.Collections.Enumerable;
                                                                                                     
                                                                                                    begin
                                                                                                      var names1 := TCollections.CreateList<string>([
                                                                                                        'Hartono, Tommy'
                                                                                                      ]);
                                                                                                      var names2 := TCollections.CreateList<string>([
                                                                                                        'Adams, Terry',
                                                                                                        'Andersen, Henriette Thaulow',
                                                                                                        'Hedlund, Magnus',
                                                                                                        'Ito, Shu'
                                                                                                      ]);
                                                                                                      var names3 := TCollections.CreateList<string>([
                                                                                                        'Solanki, Ajay',
                                                                                                        'Hoeing, Helge',
                                                                                                        'Andersen, Henriette Thaulow',
                                                                                                        'Potra, Cristina',
                                                                                                        'Iallo, Lucio'
                                                                                                      ]);
                                                                                                      var NamesList:= TCollections.CreateList<IList<string>>([names1, names2, names3]);
                                                                                                     
                                                                                                      // Only include arrays that have four or more elements
                                                                                                      var AllNames:= Enumerable<IList<string>>.Create(NamesList.ToArray)
                                                                                                        .Aggregate<IEnumerable<string>>(TEnumerable.Empty<string>,
                                                                                                          function (current: IEnumerable<string>; next: IList<string>): IEnumerable<string>
                                                                                                          begin
                                                                                                            Result:= current;
                                                                                                            if next.Count > 3 then
                                                                                                              Result:= TEnumerable.Union<string>(current, next);
                                                                                                          end
                                                                                                        );
                                                                                                     
                                                                                                      // print
                                                                                                      for var name in AllNames do
                                                                                                      begin
                                                                                                        Writeln(name);
                                                                                                      end;
                                                                                                     
                                                                                                    (*C# code
                                                                                                      string[] names1 = { "Hartono, Tommy" };
                                                                                                      string[] names2 = { "Adams, Terry", "Andersen, Henriette Thaulow",
                                                                                                                            "Hedlund, Magnus", "Ito, Shu" };
                                                                                                      string[] names3 = { "Solanki, Ajay", "Hoeing, Helge",
                                                                                                                            "Andersen, Henriette Thaulow",
                                                                                                                            "Potra, Cristina", "Iallo, Lucio" };
                                                                                                     
                                                                                                      List<string[]> namesList =
                                                                                                          new List<string[]> { names1, names2, names3 };
                                                                                                     
                                                                                                      // Only include arrays that have four or more elements
                                                                                                      IEnumerable<string> allNames =
                                                                                                          namesList.Aggregate(Enumerable.Empty<string>(),
                                                                                                          (current, next) => next.Length > 3 ? current.Union(next) : current);
                                                                                                     
                                                                                                      foreach (string name in allNames)
                                                                                                      {
                                                                                                          Console.WriteLine(name);
                                                                                                      }
                                                                                                     
                                                                                                      /*
                                                                                                       This code produces the following output:
                                                                                                     
                                                                                                       Adams, Terry
                                                                                                       Andersen, Henriette Thaulow
                                                                                                       Hedlund, Magnus
                                                                                                       Ito, Shu
                                                                                                       Solanki, Ajay
                                                                                                       Hoeing, Helge
                                                                                                       Potra, Cristina
                                                                                                       Iallo, Lucio
                                                                                                      */*)
                                                                                                    end.

                                                                                                  Что то подобное мы уже делали...
                                                                                                  Сообщение отредактировано: Cfon -
                                                                                                    Пример средней сложности GroupBy:
                                                                                                    ExpandedWrap disabled
                                                                                                      uses
                                                                                                        System.SysUtils,
                                                                                                        System.Math,
                                                                                                        Spring.Collections;
                                                                                                       
                                                                                                      type
                                                                                                        TPet = record
                                                                                                          Name: string;
                                                                                                          Age: double;
                                                                                                          constructor Create(AName: string; AAge: double);
                                                                                                        end;
                                                                                                       
                                                                                                        TTotalInfo = record
                                                                                                          Key: integer;
                                                                                                          Count: integer;
                                                                                                          Min: double;
                                                                                                          Max: double;
                                                                                                          constructor Create(AKey, ACount: integer; AMin, AMax: double);
                                                                                                        end;
                                                                                                       
                                                                                                      { TPet }
                                                                                                       
                                                                                                      constructor TPet.Create(AName: string; AAge: double);
                                                                                                      begin
                                                                                                        Name:= AName;
                                                                                                        Age:= AAge;
                                                                                                      end;
                                                                                                       
                                                                                                      { TTotalInfo }
                                                                                                       
                                                                                                      constructor TTotalInfo.Create(AKey, ACount: integer; AMin, AMax: double);
                                                                                                      begin
                                                                                                        Key:= AKey;
                                                                                                        Count:= ACount;
                                                                                                        Min:= AMin;
                                                                                                        Max:= AMax;
                                                                                                      end;
                                                                                                       
                                                                                                      begin
                                                                                                        // Create a list of pets.
                                                                                                        var PetsList:= TCollections.CreateList<TPet>([
                                                                                                          TPet.Create('Barley', 8.3),
                                                                                                          TPet.Create('Boots', 4.9),
                                                                                                          TPet.Create('Whislers', 1.5),
                                                                                                          TPet.Create('Daisy', 4.3)
                                                                                                        ]);
                                                                                                       
                                                                                                        // Group Pet.Age values by the Math.Floor of the age.
                                                                                                        // Then project an anonymous type from each group
                                                                                                        // that consists of the key, the count of the group's
                                                                                                        // elements, and the minimum and maximum age in the group.
                                                                                                        var query:= TEnumerable.GroupBy<TPet, integer, double, TTotalInfo>(PetsList,
                                                                                                          function (pet: TPet): integer
                                                                                                          begin
                                                                                                            Result:= Floor(pet.Age);
                                                                                                          end,
                                                                                                          function (pet: TPet): double
                                                                                                          begin
                                                                                                            Result:= pet.Age;
                                                                                                          end,
                                                                                                          function (baseAge: integer; ages: IEnumerable<double>): TTotalInfo
                                                                                                          begin
                                                                                                            Result:= TTotalInfo.Create(baseAge, ages.Count, ages.Min, ages.Max);
                                                                                                          end
                                                                                                        );
                                                                                                       
                                                                                                        // Iterate over each anonymous type.
                                                                                                        for var result in query do
                                                                                                        begin
                                                                                                          Writeln;
                                                                                                          WriteLn('Age group: ', result.Key);
                                                                                                          WriteLn('Number of pets in this age group: ', result.Count);
                                                                                                          WriteLn(Format('Minimum age: %.1f', [result.Min]));
                                                                                                          WriteLn(Format('Maximum age: %.1f', [result.Max]));
                                                                                                        end;
                                                                                                      end;
                                                                                                      (* C# code
                                                                                                          // Create a list of pets.
                                                                                                          List<Pet> petsList =
                                                                                                              new List<Pet>{ new Pet { Name="Barley", Age=8.3 },
                                                                                                                             new Pet { Name="Boots", Age=4.9 },
                                                                                                                             new Pet { Name="Whiskers", Age=1.5 },
                                                                                                                             new Pet { Name="Daisy", Age=4.3 } };
                                                                                                       
                                                                                                          // Group Pet.Age values by the Math.Floor of the age.
                                                                                                          // Then project an anonymous type from each group
                                                                                                          // that consists of the key, the count of the group's
                                                                                                          // elements, and the minimum and maximum age in the group.
                                                                                                          var query = petsList.GroupBy(
                                                                                                              pet => Math.Floor(pet.Age),
                                                                                                              pet => pet.Age,
                                                                                                              (baseAge, ages) => new
                                                                                                              {
                                                                                                                  Key = baseAge,
                                                                                                                  Count = ages.Count(),
                                                                                                                  Min = ages.Min(),
                                                                                                                  Max = ages.Max()
                                                                                                              });
                                                                                                       
                                                                                                          // Iterate over each type.
                                                                                                          foreach (var result in query)
                                                                                                          {
                                                                                                              Console.WriteLine("\nAge group: " + result.Key);
                                                                                                              Console.WriteLine("Number of pets in this age group: " + result.Count);
                                                                                                              Console.WriteLine("Minimum age: " + result.Min);
                                                                                                              Console.WriteLine("Maximum age: " + result.Max);
                                                                                                          }
                                                                                                       
                                                                                                          /*  This code produces the following output:
                                                                                                       
                                                                                                              Age group: 8
                                                                                                              Number of pets in this age group: 1
                                                                                                              Minimum age: 8.3
                                                                                                              Maximum age: 8.3
                                                                                                       
                                                                                                              Age group: 4
                                                                                                              Number of pets in this age group: 2
                                                                                                              Minimum age: 4.3
                                                                                                              Maximum age: 4.9
                                                                                                       
                                                                                                              Age group: 1
                                                                                                              Number of pets in this age group: 1
                                                                                                              Minimum age: 1.5
                                                                                                              Maximum age: 1.5
                                                                                                          */*)
                                                                                                      end.
                                                                                                    Сообщение отредактировано: Cfon -
                                                                                                      Тот же пример на Ньютоне
                                                                                                      ExpandedWrap disabled
                                                                                                        импорт
                                                                                                          Система.СисУтилиты,
                                                                                                          Система.Мат,
                                                                                                          Спринг.Коллекции;
                                                                                                         
                                                                                                        ТПет: запись
                                                                                                          Имя: строка;
                                                                                                          Возраст: дабл;
                                                                                                          конструктор: (ПИмя: строка; ПВозраст: дабл) -- Имя = ПИмя; Возраст = ПВозраст;
                                                                                                        конец;
                                                                                                         
                                                                                                        ТТоталИнфо: запись
                                                                                                          Ключ: целое;
                                                                                                          Колво: целое;
                                                                                                          Мин: дабл;
                                                                                                          Макс: дабл;
                                                                                                          конструктор: (ПКлюч, ПКолво: целое; ПМин, ПМакс: дабл)
                                                                                                          начать
                                                                                                            Ключ = ПКлюч;
                                                                                                            Колво = ПКолво;
                                                                                                            Мин = ПМин;
                                                                                                            Макс = ПМакс;
                                                                                                          конец;
                                                                                                        конец;
                                                                                                         
                                                                                                        ГруппироватьПоПример: функция
                                                                                                        начать
                                                                                                          // Создать список домашних животных.
                                                                                                          пер ПетСписок = ТКоллекции.Список(
                                                                                                            ТПет('Барли', 8.3),
                                                                                                            ТПет('Бутс', 4.9),
                                                                                                            ТПет('Вислерс', 1.5),
                                                                                                            ТПет('Дайзи', 4.3)
                                                                                                          );
                                                                                                         
                                                                                                          пер запрос = ТПеречислимый.ГруппироватьПо(ПетСписок,
                                                                                                            (пет: ТПет) -- вернуть Флор(пет.Возраст),
                                                                                                            (пет: ТПет) -- вернуть пет.Возраст,
                                                                                                            (базВозраст: целое; возрасты: ИПеречислимый<дабл>) --
                                                                                                              вернуть ТТоталИнфо(базВозраст, возрасты.Колво, возрасты.Мин, возрасты.Макс)
                                                                                                          );
                                                                                                          
                                                                                                          цикл по запрос -- результат
                                                                                                          начать
                                                                                                            Вывод('Группа возраст: ', результат.Ключ);
                                                                                                            Вывод('Число петс в этой возрастной группе: ', результат.Колво);
                                                                                                            Вывод(Формат('Минимальный возраст: *', [результат.Мин]));
                                                                                                            Вывод(Формат('Максимальный возраст: *', [результат.Макс]));
                                                                                                          конец;
                                                                                                        конец;

                                                                                                      - Коуч что это? :blink:
                                                                                                      Это друг мой новий язык программирования НЬЮТОН! :jokingly: :jokingly:
                                                                                                      Давно хотел понять что ощущают англоязычные коллеги :blush:
                                                                                                      Сообщение отредактировано: Cfon -
                                                                                                      0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                                                                      0 пользователей:


                                                                                                      Рейтинг@Mail.ru
                                                                                                      [ Script execution time: 0,2650 ]   [ 17 queries used ]   [ Generated: 24.04.24, 00:30 GMT ]