Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.221.187.121] |
|
Сообщ.
#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> ? Ни один из приведенных , типом voices является dynamic array constants или я хз какой он там на самом деле (в переводе по гугл транслейту это константы динамического массива ) Если я пишу следущее то ошибка компиляции 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 - никто не хотел отдельно от |