Непонятки
    
  ![]()  | 
Наши проекты:
 Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту  | 
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS | 
| [216.73.216.5] | 
 
 | 
		
  | 
    ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
  | Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) | 
    Непонятки
    
  | 
         
         
         
          
           Сообщ.
           #1
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Туплю!  
        
         непойму как сохранить адреса виртуальных фунций. Выбрасывает ошибку 'Incompatible types: 'System.SysUtils.TFunc<System.TFunc<System.string>' and 'string'' ![]() ![]() type   IVoice = interface     function Voice: string;   end;   TAnimal = class abstract (TInterfacedObject)   private     FName: string;   public     property Name: string read FName write FName;   end;   TDog = class(TAnimal, IVoice)   public     function Voice: string;   end;   TCat = class(TAnimal, IVoice)   public     function Voice: string;   end; { TDog } function TDog.Voice: string; begin   Result:= 'Arf-Arf!'; end; { TCat } function TCat.Voice: string; begin   Result:= 'Meow-Meow!'; end; var   voices: TArray<IVoice>;   funcs: TArray<TFunc<string>>;   I: Integer; begin   voices:= [TDog.Create, TCat.Create, TDog.Create];   SetLength(funcs, Length(voices));   for I := 0 to High(voices) do     funcs[i]:= voices[i].Voice;  //<--- это вызов, а надо сохранить адрес функции       Readln; end. Хелп плз!     | 
    
| 
         
         
         
          
           Сообщ.
           #2
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          А вот если убрать интерфейс IVoice и заюзать абстрактный класс TAnimal то все пашет, адреса сохраняются!   
        
        ![]() ![]() type   TAnimal = class abstract   private     FName: string;   public     function Voice: string; virtual; abstract;     property Name: string read FName write FName;   end;   TDog = class(TAnimal)   public     function Voice: string; override;   end;   TCat = class(TAnimal)   public     function Voice: string; override;   end; { TDog } function TDog.Voice: string; begin   Result:= 'Arf-Arf!'; end; { TCat } function TCat.Voice: string; begin   Result:= 'Meow-Meow!'; end; var   animals: TArray<TAnimal>;   funcs: TArray<TFunc<string>>;   I: Integer; begin   animals:= [TDog.Create, TCat.Create, TDog.Create];   SetLength(funcs, Length(animals));   for I := 0 to High(animals) do     funcs[i]:= animals[i].Voice; //<--- а вот так все пучком!   // тест   for I := 0 to High(funcs) do     Writeln(funcs[i]());   Readln; end. Я в непонятках ![]() Где я не доучил Объект Паскаль?   Кстати учу по книжке моего кента Марко Кента   	  | 
    
| 
         
         
         
          
           Сообщ.
           #3
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Cfon 
        
      Это косяк эмбаркодеров. Они толком не за документировали и как следствие толком не закодировали.  | 
    
| 
         
         
         
          
           Сообщ.
           #4
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Pavia @  Cfon Это косяк эмбаркодеров. Они толком не за документировали и как следствие толком не закодировали. Решил их косяк через анонимку   ![]() ![]() begin   voices:= [TDog.Create, TCat.Create, TDog.Create];   SetLength(funcs, Length(voices));   for I := 0 to High(voices) do     (procedure (i: integer)  //<--- iife        funcs[i]:= function: string //<--- anonimous function       begin         Result := voices[i].Voice;       end;     end)(i);   for I := 0 to High(funcs) do     Writeln(funcs[i]());   Readln; end. в JS стиле   ![]() ![]() begin   var voices: TArray<IVoice>:= [TDog.Create, TCat.Create, TDog.Create];   var funcs: TArray<TFunc<string>>;   SetLength(funcs, Length(voices));   for var I := 0 to High(voices) do     (procedure (i: integer)       begin       funcs[i]:= function: string         begin           Result := voices[i].Voice;         end;      end)(i);   for var I := 0 to High(funcs) do     Writeln(funcs[i]());   Readln; end. похоже тока в JS не надо указывать типы ![]() да и память не надо в ручную выделять ![]() ПС. Меня за уши теперь от Делфи не отянуть ![]() Формочки набиваются на раз два + код паскаля простой, легко читаемый, это просто мечта прогера   	  | 
    
| 
         
         
         
          
           Сообщ.
           #5
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
        ![]() ![]() for var I := 0 to High(voices) do     (procedure (i: integer)  //<--- iife       funcs[i]:= function: string //<--- anonimous function       begin         Result := voices[i].Voice;       end;     end)(i); Отдельно хочу пояснить про iife (самовызываемая функция) во втором цикле for, она создает локальную область видимости. Зачем? Затем чтобы анонимная функция сохранила переменную i в ней! Я хз почему, но анонимка не замыкает локальную переменную i цикла for! Мои эксперементы с ней показали что она удалятся после цикла   В итоге если не создать вручную локалку мы имеем эксепшен при доступе к массиву funcs. Наверно очередной косяк Эмбаркадэро ![]() А вот на JS найди отличия   ![]() ![]() for (var i=0; i < voices.length; i++)      (function (i) { //<--- iife       funcs.push( function () { //<--- anonimous function         return voices[i].Voice;       }     })(i);  | 
    
| 
         
         
         
          
           Сообщ.
           #6
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Cfon @  Мои эксперементы с ней показали что она удалятся после цикла   В итоге если не создать вручную локалку мы имеем эксепшен при доступе к массиву funcs. Тикет qc создал надеюсь? Я б проголосовал.  | 
    
| 
         
         
         
          
           Сообщ.
           #7
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата jack128 @  Цитата Cfon @  Мои эксперементы с ней показали что она удалятся после цикла   В итоге если не создать вручную локалку мы имеем эксепшен при доступе к массиву funcs. Тикет qc создал надеюсь? Я б проголосовал. Упс, пардон ми   Я поспешил с выводами переменная i не удалятся, замыкание есть, но все 3 вызова в цикле захватывают одну и туже i и итоге при обращении у массиву функций идет обращение по индексу i=3 что ведет к эксепшену обращение за пределы массива. Похожая ситуация есть в JS тока там var i определенная в цикле имеет глобальную область действия, а в Delphi она локальная. Короче тут все без косяков, это просто фича цикла for и анонимок. Переменные захватываются по ссылке, а не по значению, чтобы решить траблу надо заюзать iife создавая на каждой итерации новую локалку. В JS ввели let для определения локальной переменной, как она работает я не разбрал, кажись анонимки захватывают ее по значению точно не помню, потому в JS можно решить это так ![]() ![]() ![]() for (let i=0; i < voices.length; i++)  // iife не надо ибо захват i по значению        funcs.push( function () {        return voices[i].Voice;     }  | 
    
| 
         
         
         
          
           Сообщ.
           #8
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Cfon @  Я поспешил с выводами переменная i не удалятся, замыкание есть, но все 3 вызова в цикле захватывают одну и туже i и итоге при обращении у массиву функций идет обращение по индексу i=3 что ведет к эксепшену обращение за пределы массива. по идее - это тоже касяк. То есть, если мы объявили i в var блоке функции - то такое поведение логично, а если мы объявили i в цикле, то захвачено должно на каждой итерации - отдельное значение. Вообще смешно это, абракадабра заимствует многое из C# при этом повторяя их же ошибки. В шарпе изначально было такое же поведение, но несколько лет назад они фиксанули это и теперь захват значения в лямбду происходит как будто у нас на каждой итерации новая переменная i с новым значением (как c let в js)  | 
    
| 
         
         
         
          
           Сообщ.
           #9
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата  ![]() ![]() funcs[i]:= voices[i].Voice;  //<--- это вызов, а надо сохранить адрес функции Эээ, а так разве не то, что требуется? ![]() ![]() funcs[i]:= @voices[i].Voice;  | 
    
| 
         
         
         
          
           Сообщ.
           #10
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Fr0sT @  Эээ, а так разве не то, что требуется? funcs[i]:= @voices[i].Voice; так - это то что даже не компилится  | 
    
| 
         
         
         
          
           Сообщ.
           #11
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Ну ему нужен адрес функции - он берется вот так.   
        
       | 
    
| 
         
         
         
          
           Сообщ.
           #12
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Нет. Чтобы взять адрес функции, справа от оператора @ должна быть функция, а Voices[I].voice - это метод интерфейса. в Дельфи нет возможности взять его адрес. 
        
      upd: встроенной возможности.  | 
    
| 
         
         
         
          
           Сообщ.
           #13
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
         Цитата Fr0sT @  Цитата  ![]() ![]() funcs[i]:= voices[i].Voice;  //<--- это вызов, а надо сохранить адрес функции Эээ, а так разве не то, что требуется? ![]() ![]() funcs[i]:= @voices[i].Voice; Ошибка [dcc32 Error] : E2036 Variable required Ну да фиг с ним   Продолжаю улучшения скрываем цикл for, функциональщина так и прет   ![]() ![]() type   TForEachProc<T> = reference to procedure (Value: T; Index: Integer);   TArrayHelper = class helper for TArray     class procedure ForEach<T>(const Values: array of T; AProc: TForEachProc<T>); static;   end; { TArrayHelper } class procedure TArrayHelper.ForEach<T>(const Values: array of T; AProc: TForEachProc<T>); begin   for var I := 0 to High(Values) do     AProc(Values[I], I); end; var   voices: TArray<IVoice>;   funcs: TArray<TFunc<string>>; begin   voices:= [TDog.Create, TCat.Create, TDog.Create];   SetLength(funcs, Length(voices));   TArray.ForEach<IVoice>(voices, procedure (elem: IVoice; i: integer)   begin     funcs[i]:= function: string       begin         Result:= elem.Voice;       end;   end);   // тест   for var i := 0 to High(funcs) do     Writeln(funcs[i]());   Readln; end. ПС. обратите внимание тут iife уже не требуется поскольку наши операторы заключены в анонимку.  | 
    
| 
         
         
         
          
           Сообщ.
           #14
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Улучшаем предыдущий код, убираем выделение памяти для массива funcs!   
        
        Для этого допишем TArrayHelper, а именно создадим функцию Map и заменим в коде ForEach на Map. Кто не в теме функциональной парадигмы поясню: Map получает на входе исходный массив а возвращает другой массив, попутно выполняя какие либо преобразования исходного массива. ![]() ![]() type   TForEachProc<T> = reference to procedure (Value: T; Index: Integer);   TMapProc<T, R> = reference to function (Value: T; Index: Integer): R;     TArrayHelper = class helper for TArray     class procedure ForEach<T>(const Values: array of T; AProc: TForEachProc<T>); static;     class function Map<T, R>(const Values: array of T; AProc: TMapProc<T, R>): TArray<R>; static;       end; { TArrayHelper } ..... class function TArrayHelper.Map<T, R>(const Values: array of T;   AProc: TMapProc<T, R>): TArray<R>; begin   Result:= [];   for var I := 0 to High(Values) do     Result:= Result + [AProc(Values[i], i)]; end; begin   var voices: TArray<IVoice> := [TDog.Create, TCat.Create, TDog.Create];   var funcs := TArray.Map<IVoice, TFunc<string>>(voices,     function (v: IVoice; i: integer): TFunc<string>     begin       Result:= function: string         begin           Result := v.Voice;         end;     end);   // test   for var fn in funcs do Writeln(fn());   Readln; end. В итоге никаких тебе явных выделений памяти и циклов. Обращаю внимание что при объявлении переменная funcs не указан её тип, тут работает выведение типов, а вот при объявление voices требуется указать тип ибо тут выведеный тип не подходит, поскольку нам надо TArray<IVoice>, а компилятор нам предлагает array of const, что делать? укажем вручную ![]() ПС. в мапе я заюзал конкатенацию массивов, но можно и через SetLength.  |