Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.222.37.22] |
|
Сообщ.
#1
,
|
|
|
Есть задача: найти в списке элемент, удовлетворяющий заданному условию (предикату). Представим себе, что у нас уже есть структура данных «список» и функция поиска, принимающая предикат, который также является функцией, возвращающей значений булевского типа.
Наваял такой код: ... var Form1: TForm1; type TIntList = array of integer; TPredicate = function(x: Integer): Boolean; implementation {$R *.dfm} function Find(list: TIntList; f: TPredicate): Integer; var b: boolean; i: integer; begin for i := 0 to high(list) do begin b := f(list[i]); if b then begin Result := list[i]; Exit; end; end; Result := 0; end; procedure Test; var t: TIntList; n: Integer; v: integer; procedure InitT; begin setLength(t, 2); t[0] := 3; t[1] := 6; end; function GreaterN(x: Integer): Boolean; begin if x >= n then result := true else result := false; end; begin n := 5; initT; ShowMessage(IntToStr(Find(t, @GreaterN))); end; procedure TForm1.Button1Click(Sender: TObject); begin test; end; Прикол в том, что ф-ция GreaterN, вызов которой происходит из Find, всегда возвращает false, хотя второй элемент списка (t[1] := 6) передаваемый в качестве параметра > n (где n = 5). И как это объяснить? |
Сообщ.
#2
,
|
|
|
о_О и вправду делфи "чудит" с определением переменной n явно проблемы .. если вынести её глобально ок
не знаю как можно разрулить..мб написать маленький клас? |
Сообщ.
#3
,
|
|
|
ShowMessage(IntToStr(Find(t, @GreaterN))); убери @ и читай справку по появившейся ошибке |
Сообщ.
#4
,
|
|
|
О_о
А с чего вы взяли, что ваш код должен работать? Вы взяли адрес локальной процедуры и передали его в Find. Окей, согласно справке: Цитата If you take any procedure or function heading and remove the identifier after the word procedure or function, what's left is the name of a procedural type. You can use such type names directly in variable declarations (as in the previous example) or to declare new types: type TIntegerFunction = function: Integer; procedure FuncProc(P: TIntegerFunction); { FuncProc is a procedure whose only parameter is a parameterless integer-valued function } On Win32, the variables shown in the previous example are all procedure pointers - that is, pointers to the address of a procedure or function. Внимание, вопрос: если в Find передаётся указатель на код, то как этот код получит доступ к локальной переменной внутри другой функции? Локальные переменные имеют смысл только внутри своей процедуры, но не для внешних процедур. Беря указатель на функцию, вы делаете её внешней по отношению к Test. Следовательно, как у внешней процедуры, у неё нет доступа к локальным переменным процедуры Test. Да, код GreaterN компилируется, т.к. он корректен. Да, указатель на функцию компилятор разрешает брать для любой функции. Но при этом используется тот же самый код. Т.е. при @GreaterN не создаётся новой функции - используется уже готовый код. Получается, что GreaterN в качестве локальной процедуры работает с n. GreaterN в качестве внешней процедуры обращается куда-то. Когда в GreaterN вы обращаетесь к n, вы на самом деле используете относительную ссылку, т.е. говорите: прочитать второй параметр у вышестоящей процедуры. Когда GreaterN вызывается из Find на этом месте находятся какие-то другие данные. Вам, на самом деле, нужно передавать не только код, но и данные, с которыми он будет работать. Если читать справку дальше: Цитата If you want to reference a method of an instance object (see Classes and objects), you need to add the words of object to the procedural type name. For example type TMethod = procedure of object; TNotifyEvent = procedure(Sender: TObject) of object; These types represent method pointers. A method pointer is really a pair of pointers; the first stores the address of a method, and the second stores a reference to the object the method belongs to. Таким, образом, у вас есть несколько вариантов правильного действия: 1. Использовать глобальные переменные. 2. Передавать вместе с процедурой указатель на данные, с которыми процедура будет работать. 3. Вместо процедур использовать методы. 4. Использовать анонимные процедуры. Они позволяют автоматически "копировать к себе" нужные переменные из стека. Функционально все эти подходы равны. Отличается лишь внешний вид, а суть одна: передаётся код и данные. |
Сообщ.
#5
,
|
|
|
Цитата CodeMonkey @ Получается, что GreaterN в качестве локальной процедуры работает с n. GreaterN в качестве внешней процедуры обращается куда-то. НО если посмотреть в отладчике и побегать по F7, то увидим n определяется правильно = 5. Цитата CodeMonkey @ Использовать анонимные процедуры. Они позволяют автоматически "копировать к себе" нужные переменные из стека. Собственно из-за них то я и затеял тему смотрим здесь http://forum.vingrad.ru/topic-96077.html Да.. глюки то одназначно будут т.к. когда я делаю так @GreaterN, то GreaterN по отношению к Test становиться в Find внешней ф-цией, а значит и доступа к локальным переменным т.е. в данном случак к n будет неправильным. Но это все фигня, меня удивляет почему компилятор не ругается на очевидные ошибки? |
Сообщ.
#6
,
|
|
|
Цитата DelphiLexx @ Но это все фигня, меня удивляет почему компилятор не ругается на очевидные ошибки? а тебя не смущает, что компилер не ругается например на такой код: PInteger(@StrVar)^ := 10; ??? ты сам забил на компилер, когда стал пользоваться нетипизированными указателями. Написал бы так ShowMessage(IntToStr(Find(t, GreaterN))); компилер бы те четко сказал, что так нельзя и отказался бы такой код компилить. ЗЫ Прежде чем ругать компиляторы - не мешало бы язык выучить. |
Сообщ.
#7
,
|
|
|
Цитата DelphiLexx @ Но это все фигня, меня удивляет почему компилятор не ругается на очевидные ошибки? Он ругается Передача процедуры в другую процедуру - это просто указание её имени, как вам уже два раза здесь ткнули. Вы же берёте указатель в чистом виде. Разумеется, компилятор на такое ругаться не станет. P.S. На самом деле, если бы вы включили строгую проверку типов (Typed @ Operator), то компилятор не дал бы вам и этого сделать (типа, указатель - не то же самое, что процедурный тип). |
Сообщ.
#8
,
|
|
|
Цитата jack128 @ Прежде чем ругать компиляторы - не мешало бы язык выучить. jack128, ты самое главное не нервничай, будь спокойнее. и язык знаю не хуже тебя. успокойся Добавлено Цитата CodeMonkey @ Передача процедуры в другую процедуру - это просто указание её имени, как вам уже два раза здесь ткнули. Вы же берёте указатель в чистом виде. Разумеется, компилятор на такое ругаться не станет. P.S. На самом деле, если бы вы включили строгую проверку типов (Typed @ Operator), то компилятор не дал бы вам и этого сделать (типа, указатель - не то же самое, что процедурный тип). Согласен |
Сообщ.
#9
,
|
|
|
Цитата DelphiLexx @ и язык знаю не хуже тебя. успокойся |
Сообщ.
#10
,
|
|
|
Цитата Romkin @ что смешного |
Сообщ.
#11
,
|
|
|
смешно, когда человек, который утверждает, что знает язык, задаёт такие вопросы как #1
|
Сообщ.
#12
,
|
|
|
Цитата jack128 @ смешно, когда человек, который утверждает, что знает язык, задаёт такие вопросы как #1 Читай #5 без последнего предложения |
Сообщ.
#13
,
|
|
|
То что ты питаешся зделать, собсно что почерпнул из этой статьи
Цитата DelphiLexx @ называется (цытирую из твоей сатьи) Собственно из-за них то я и затеял тему смотрим здесь http://forum.vingrad.ru/topic-96077.html Цитата В контексте языков программирования под лямбда-функцией понимают анонимную функцию. Т.е. функцию, которой не сопоставлено никакое имя, просто значение функционального типа. Например: теперь переходим на описаний новшеств языке в 2009 делфи http://8vmr.livejournal.com/6114.html Цитата Я думаю дальше объяснять ничего не нужно 2. Анонимные методы (лямбды), с ними же и замыкания (closures): |
Сообщ.
#14
,
|
|
|
Цитата ViktorXP @ теперь переходим на описаний новшеств языке в 2009 делфи http://8vmr.livejournal.com/6114.html Цитата 2. Анонимные методы (лямбды), с ними же и замыкания (closures): Я думаю дальше объяснять ничего не нужно Эту статью я уже виделю Наконец-то, мы плавненько перешли к новшествам BDS2009 - ананимным методам и замыканиям. Кто-нибудь может превести пример с использованием этих технологий для решения поставленной задачи. |
Сообщ.
#15
,
|
|
|
Цитата DelphiLexx @ TPredicate = reference to function(x: Integer): Boolean; и все будет работать. |
Сообщ.
#16
,
|
|
|
Цитата jack128 @ и все будет работать. Не мог бы примерчиком показать, а то не совсем понятно |
Сообщ.
#17
,
|
|
|
Цитата DelphiLexx @ Не мог бы примерчиком показать, а то не совсем понятно type TIntList = array of integer; TPredicate = reference to function(x: Integer): boolean; implementation {$R *.dfm} function Find(list: TIntList; f: TPredicate): Integer; var b: boolean; i: integer; begin for i := 0 to high(list) do begin b := f(list[i]); if b then begin Result := list[i]; Exit; end; end; Result := 0; end; procedure Test; var t: TIntList; n: Integer; v: integer; GreaterN:TPredicate; procedure InitT; begin setLength(t, 2); t[0] := 3; t[1] := 6; end; begin n := 5; initT; GreaterN := function(x: Integer): Boolean begin result := ( x >= n); end; ShowMessage(IntToStr(Find(t, GreaterN))); end; Добавлено если у тебя 2009 то уже и про такие вещи можеш забыть Цитата DelphiLexx @ теперь тут можно спокойно написать Result := list[i]; Exit; Exit(list[i]); |
Сообщ.
#18
,
|
|
|
Цитата ViktorXP @ или можно сразу в find вбить функцию без использования переменной Спасибо, теперь понятно. Цитата ViktorXP @ теперь тут можно спокойно написать Exit(list[i]); ага это я знал. А когда обещают Borladn или CodeGear или Embarcadero (просто не знаю как нызываь их) оффициальный выход BDS2009? |