На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania 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_
Страницы: (4) 1 2 [3] 4  все  ( Перейти к последнему сообщению )  
> Spring4D, Функциональное программирование в действии
Рефакторим 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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
Цитата Cfon @
Тест произодительности Spring4D vs простой цикл :D
тестил на массиве из ~2000 слов, Spring4D Distint быстрее обычного цикла :blink:
В чем я ошибся или у меня массив не правильный? :D

Все понел! :D
Дело в том что Spring Distinct юзает хешсет для создания уникального списка слов, т.е он фастом в один проход создает список, а в примере на цикле идет два цикла, один на добавление другой на поиск совпадения, что естесно ведет к низкой производительности. Как то так :D
Rabbit don't come easy: https://github.com/Cfon/ :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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
еще два вызова 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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
Цитата 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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
А вот и сюрприз! Чекал 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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
ОК. Задание!
Надо попрактиковаться на примере, возьмем пример с рецептами.
Вывести данные в виде:
- название рецепта
-- список названий отсортированных инградиентов.
Сообщение отредактировано: Cfon -
Rabbit don't come easy: https://github.com/Cfon/ :D
Ответ :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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
Давайте сравним императивный подход с функциональным:
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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
Немного лекций :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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
- Маста вот что у меня получилось, я заюзал анонимку для простоты, все как вы учили, есть вопрос
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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
- Маста, а как насчет времени затраченое на копирование данных? :huh:
Ну тут тоже есть свои секреты :D
Как в Делфи не знаю точно, но например в Хаскеле данные, если к примеру брать два списка, не копируются, а создаются ссылки на одни и те же данные. Само копирование происходит тока тогда когда данные изменяются, вот тогда создается копия и затем эта копия изменяется. Так что никаких лишних копий нет. Заметь копия не всех данных, а ток тех что надо изменить.
Надо почитать про то как Делфи управляет памятью. Думаю что делфиразрабы тоже это учитывают ибо не просто так они добавили функциональные возможности типо ананимок, вывод типов, паралельное програмирование и тд.
- Ясно почитаю, но в Хаскеле хитро придумано :)
Ну как бэ не пальцем деланый язык то :D
да и к слову сразу про рекурсии в Хаскеле они в итоге преобразуются на уровне бинарного кода в обычные циклы :D так что у тут нет замедления от их юзания.
Ну естестно поэтому компилятор Хаскеля на порядок медленнее например чем в Делфи :D
В Делфи как я вижу походу самый быстрый компилятор из тех что я видел.
Сообщение отредактировано: Cfon -
Rabbit don't come easy: https://github.com/Cfon/ :D
- Маста, а если дефолтное сравнение не катит, что делать? :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 -
Rabbit don't come easy: https://github.com/Cfon/ :D
1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
0 пользователей:
Страницы: (4) 1 2 [3] 4  все


Рейтинг@Mail.ru
[ Script Execution time: 0,2546 ]   [ 20 queries used ]   [ Generated: 20.04.19, 04:39 GMT ]