
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.250] |
![]() |
|
Сообщ.
#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. |
Сообщ.
#16
,
|
|
|
На апдейтовом делфи рио, приведеный там код с TDictionary пашет. По моему примеру я немного ошибся с терминологией по массивам уже больно запутано в делфи ![]() Ща сек все разберу по полочкам... Итак давайте разберем их на моем примере. Есть статические и динамческие массивы: ![]() ![]() type TVoices = array [1..3] of IVoice; begin // dynamic array var voices:= TArray<IVoice>.Create(TDog.Create, TCat.Create, TDog.Create); // static array var voices: TVoices; voices[1] := TDog.Create; voices[2] := TCat.Create; voices[3] := TDog.Create; .... а также есть так называемые открытые массивы, они существуют только как параметры, например array of T в функции Map: ![]() ![]() class function TArrayHelper.Map<T, R>(const Values: array of T; //<-- open array parameter AProc: TMapProc<T, R>): TArray<R>; в него можно передать как статик так и динамик массив ![]() ![]() var funcs := TArray.Map<IVoice, TFunc<string>>(voices, //<-- dynamic or static array ..... У меня же по коду идет ни тот ни другой ![]() Какой? На новых делфи (не знаю с какой точно версии), но в Рио можно делать такую шутку: ![]() ![]() var voices:= [TDog.Create, TCat.Create, TDog.Create]; это объявление динамического массива констант (dynamic array constant), не путайте с array of const. array of const - это особый тип открытого массива, который задается как параметр функции, пример тому знаменитая функция Format ![]() Так вот я передаю динамический массив констант voices в функцию Map, которая ждет либо динамический либо статический массив, наш же имеет другой тип! Чтобы решить траблу можно сделать как я привел выше в примере, т.е указать явно тип переменой и уже потом инициализировть массив ![]() ![]() var voices: TArray<IVoice> := [TDog.Create, TCat.Create, TDog.Create]; ... или сразу создать динамич. массив через TArray<T>.Create ![]() ![]() var voices:= TArray<IVoice>.Create(TDog.Create, TCat.Create, TDog.Create); ... |
![]() |
Сообщ.
#17
,
|
|
Цитата Cfon @ У меня же по коду идет ни тот ни другой Какой? На новых делфи (не знаю с какой точно версии), но в Рио можно делать такую шутку: var voices:= [TDog.Create, TCat.Create, TDog.Create]; это объявление динамического массива констант (dynamic array constant) У меня нету Rio, не могу проверить. Сразу возникает вопрос, какой тип у переменной voices? TArray<TVarRec>? TArray<TObject>? TArray<IVoice> ? Где нить описана логика вывода типа массива? Термин "dynamic array constant" не разу не слышал. |
Сообщ.
#18
,
|
|
|
Цитата jack128 @ У меня нету Rio, не могу проверить. Сразу возникает вопрос, какой тип у переменной voices? TArray<TVarRec>? TArray<TObject>? TArray<IVoice> ? Ни один из приведенных ![]() ![]() ![]() Если я пишу следущее то ошибка компиляции ![]() ![]() var voices:= [TDog.Create, TCat.Create, TDog.Create]; var funcs := TArray.Map<IVoice, TFunc<string>>(voices, //<-- вот на эту строчку function (v: IVoice; i: integer): TFunc<string> begin .. end); // [dcc32 Error]: E2010 Incompatible types: 'array of IVoice' and 'Dynamic array' Что тут 'array of IVoice' а что 'Dynamic array' я хз ![]() по идее voices это 'Dynamic array', а параметр функции это 'array of IVoice' Цитата jack128 @ Где нить описана логика вывода типа массива? Термин "dynamic array constant" не разу не слышал. Вот тут я нашел про них http://rvelthuis.de/articles/articles-openarr.html Да еще забыл упомянуть про конструкторы открытого массива (open array constructor) они напоминают форму dynamic array constants, но передаются сразу как параметры функции, моем случае будет так ![]() ![]() var funcs := TArray.Map<IVoice, TFunc<string>>([TDog.Create, TCat.Create, TDog.Create], //<-- open array constructor function (v: IVoice; i: integer): TFunc<string> begin .... end); В такой форме код компилица. А не стоп, не только как параметр функции, но и вот тут вроде как тоже open array constructor ![]() ![]() ![]() var voices: TArray<IVoice> := [TDog.Create, TCat.Create, TDog.Create]; //<-- open array constructor Самое не понятное для меня было следущее: Я поначалу не сразу просек про параметр открытый массив (open array parameter) ![]() ![]() class function TArrayHelper.Map<T, R>(const Values: array of T; //<-- open array parameter AProc: TMapProc<T, R>): TArray<R>; ... я думал что это одно и тоже что и ![]() ![]() class function TArrayHelper.Map<T, R>(const Values: TArray<T>; //<-- dynamic array parameter AProc: TMapProc<T, R>): TArray<R>; ... поскольку TArray<T> описан как ![]() ![]() type TArray<T> = array of T но приведенный выше пример является динамич. массивом, а не открытым массивом. Разница их в том как я уже писал выше в открытый массив можно передать и статич.массив и динамич.массив и конструктор открытого массива! Ой *бать замутили ![]() ![]() |
![]() |
Сообщ.
#19
,
|
|
Цитата Cfon @ Где нить описана логика вывода типа массива? Термин "dynamic array constant" не разу не слышал. Вот тут я нашел про них http://rvelthuis.de/articles/articles-openarr.html в примере из статьи как раз все понятно, там явно указан тип переменной: ![]() ![]() var MyDynArray: array of Integer; // безымянный дин массив интов begin MyDynArray := [17, 325, 11]; В твоем случае ж случае тип переменной как то выводится компилятором, вот мне и интересно как. Цитата Cfon @ типом voices является dynamic array constants Забьем пока на тип массива, хотя бы с типом элемента массива разобраться бы. ![]() ![]() var voices:= [TDog.Create, TCat.Create, TDog.Create]; voices[0].Free; // компилируется ?? voices[1]._AddRef(); // компилируется? var x := voices[1000]; // компилируется ? |
Сообщ.
#20
,
|
|
|
Цитата jack128 @ Забьем пока на тип массива, хотя бы с типом элемента массива разобраться бы. ![]() ![]() var voices:= [TDog.Create, TCat.Create, TDog.Create]; voices[0].Free; // компилируется ?? voices[1]._AddRef(); // компилируется? var x := voices[1000]; // компилируется ? ![]() ![]() voices[0].Free; // компилируется ?? да voices[1]._AddRef(); // компилируется? нет [dcc32 Error]: E2003 Undeclared identifier: '_AddRef' var x := voices[1000]; // компилируется ? да все кроме второго компиляца и без рантайм ошибок. Если посмотреть на список Code Insight для voices[i], то он напоминает TObject, но если я передаю voices в процедуру: ![]() ![]() procedure Test(A: TArray<TObject>); begin end; begin var voices:=[TDog.Create, TCat.Create, TDog.Create]; Test(voices); end; // [dcc32 Error]: E2010 Incompatible types: 'System.TArray<System.TObject>' and 'Dynamic array' Ради интереса посмотрел в простом цикле ![]() ![]() var voices:=[TDog.Create, TCat.Create, TDog.Create]; for var I := 0 to High(voices) do begin Writeln(voices[i].ClassName); end; // Output: // TDog // TCat // TDog Пробую привести к IVoice ошибка пишет что voice[i] - TObject ![]() ![]() ![]() var voices:=[TDog.Create, TCat.Create, TDog.Create]; for var I := 0 to High(voices) do begin Writeln(IVoice(voices[i]).Voice); end; // [dcc32 Error]: E2010 Incompatible types: 'IVoice' and 'TObject' Прокатило если определить открытый массив TObject ![]() ![]() ![]() procedure Test(A: array of TObject); begin for var I := 0 to High(A) do begin Writeln(A[i].ClassName); end; end; begin Test(voices); // Output: // TDog // TCat // TDog end. Какие выводы? voices это array of TObject ![]() |
Сообщ.
#21
,
|
|
|
А теперь хардкор
![]() Предлагаю вашему вниманию версию примера с функцией Reduce. Добавляем поддержку Reduce к нашему TArrayHelper. Что делает Reduce? Она обрабатывает исходный массив и сводит (reduce) к другому типу потенциально любому типу! Давайте посмотрим как это выглядит: ![]() ![]() type TForEachProc<T> = reference to procedure (Value: T; Index: Integer); TMapProc<T, R> = reference to function (Value: T; Index: Integer): R; TReduceProc<T, R> = reference to function (Accumulator: R; Value: T): 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; class function Reduce<T, R>(const Values: array of T; AProc: TReduceProc<T, R>; InitValue: R): R; static; end; { TArrayHelper } .... class function TArrayHelper.Reduce<T, R>(const Values: array of T; AProc: TReduceProc<T, R>; InitValue: R): R; begin Result := InitValue; for var I := 0 to High(Values) do Result := AProc(Result, Values[i]); end; begin var voices:= TArray<IVoice>.Create(TDog.Create, TCat.Create, TDog.Create); var funcs := TArray.Reduce<IVoice, TArray<TFunc<string>>>(voices, function (accum: TArray<TFunc<string>>; value: IVoice): TArray<TFunc<string>> begin Result:= accum + [function: string begin Result := value.Voice end]; end, []); // test for var fn in funcs do Writeln(fn()); Readln; end. Результат тот же, но пришлось моск изрядно напрячь, код явно не для слабонервных ![]() Хотя для хакселистов он покажется простым ![]() Понятно что данную задачу Map решает проще, но сам по себе Reduce пригодиться во многих других задачах где Map будет бессилен. А Reduce это такой швейцарски нож решит любую задачу. |
Сообщ.
#23
,
|
|
|
Вообще насчет адресов методов и прочего, вот как вариант
![]() ![]() var voices: TArray<IVoice>; m: TMethod; ob: TObject; begin ob := voices[0] as TAnimal; m.Data := (ob); m.Code := (ob).MethodAddress('Voice'); memLog.Lines.Add(TFuncOfObj<string>(m)()); TMethod самодостаточный (содержит как адрес метода, так и адрес самого объекта). Единственное, что у меня почему-то MethodAddress возвращает nil даже со включенным RTTI, хотя нигде нет упоминаний, что этой функции вообще требуются какие-то условия. Вот тут инфа с примерами https://stackoverflow.com/questions/4186458...red-in-a-string |
Сообщ.
#24
,
|
|
|
Я ее давно скачал, чтобы в перспективе поюзать DI container ![]() Функционал енумератора Spring4D смотрел.. пока туман ![]() Доков нет плохо, как я понял там Select аналог Map? Цитата Fr0sT @ Вообще насчет адресов методов и прочего, вот как вариант Что то мудрено, через анонимку то изичи ![]() |
Сообщ.
#25
,
|
|
|
Варик на Spring4D
![]() ![]() ![]() uses Spring.Collections; begin var voices := TArray<IVoice>.Create(TDog.Create, TCat.Create, TDog.Create); var funcs := TEnumerable.Select<IVoice, TFunc<string>>( TCollections.CreateList<IVoice>(voices), function (v: IVoice): TFunc<string> begin Result:= function: string begin Result := v.Voice; end; end); // test for var fn in funcs do Writeln(fn()); Readln; end. |
Сообщ.
#26
,
|
|
|
Цитата Cfon @ Функционал енумератора Spring4D смотрел.. пока туман ![]() Доков нет плохо, как я понял там Select аналог Map? А все нашел примеры, это шарповый LINQ Enumerable один в один ![]() https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable?view=netframework-4.7.2 Скомуниздили из линка энумератор спринги ![]() |
Сообщ.
#27
,
|
|
|
А зачем, собственно, сохранять методы в отрыве от объектов?
|
![]() |
Сообщ.
#28
,
|
|
Цитата Fr0sT @ А зачем, собственно, сохранять методы в отрыве от объектов? Какие методы от каких объектов? |
Сообщ.
#29
,
|
|
|
Voice. От созданных
|
![]() |
Сообщ.
#30
,
|
|
Цитата Fr0sT @ Voice. От созданных Э-э-э, я думал мы про Enumerable шарповский уже говорим. По поводу Voice - никто не хотел отдельно от |