PDA

View Full Version : تابع به عنوان پارامتر



1485159
یک شنبه 09 خرداد 1389, 11:02 صبح
سلام
من میخوام یه تابعی بسازم که پارامتر ورودیش یه تابع باشه و بتونم از درون تابعم اون تابع رو که به عنوان ورودی گرفتم فراخوانی کنم. چطوری؟
ممنون میشم راهنمایی کنید.

hossein_h62
یک شنبه 09 خرداد 1389, 11:33 صبح
سلام
شما حالتی مثل کد زیر مدنظرتون هست ؟؟؟ بطور مثال دو تابع Length و Odd رو نوشتم:



procedure TForm1.Button1Click(Sender: TObject);
begin
if odd(length(Edit1.Text)) Then ShowMessageFmt('%d is odd',[length(Edit1.Text)])
else ShowMessageFmt('%d is not odd',[length(Edit1.Text)]);
end;


درست متوجه شدم منظورتون رو ؟؟

Mahmood_M
یک شنبه 09 خرداد 1389, 11:35 صبح
نمیشه نام یک تابع رو به مقدار پارامتر دریافت کرد و بعد اون رو فراخوانی کرد ، اگر چند تابع نوشتید و می خواید در یک تابع دیگه اونها رو فراخوانی کنید ، می تونید یک لیست از اون توابع ایجاد کنید و یکی از مقادیر اون لیست رو به تابع ارسال کنید و در تابع اصلی هم شرطی برای مقدار لیست قرار بدید ، به عنوان مثال فرض کنید دو تا تابع فرعی داریم به نامهای Func1 و Func2 و یک تابع اصلی هم به نام MainFunc داریم ، حالا می تونیم به صورت زیر توابع فرعی رو درون تابع اصلی فراخوانی کنیم :
اول لیست رو مثلا به صورت زیر تعریف می کنیم :
type
TFuncList = (F1, F2);
توابع فرعی رو هم تعریف می کنیم :
implementation

{$R *.dfm}

function Func1 : Boolean;
begin
Result := True;
end;

function Func2 : Boolean;
begin
Result := False;
end;

و در نهایت با استفاده از Case می تونیم مقدار ورودی رو چک کنیم :
function MainFunc(Func : TFuncList) : Boolean;
begin
case Func of
F1 : Result := Func1;
F2 : Result := Func2;
end;
end;
نحوه ی استفادش هم به صورت زیر هست ( مثال ) :
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
if MainFunc(F2) = True then
ShowMessage('...');
end;
اگر منظورتون چیز دیگه ای هست بگید ، موفق باشید ...

1485159
یک شنبه 09 خرداد 1389, 11:39 صبح
نمیشه نام یک تابع رو به مقدار پارامتر دریافت کرد و بعد اون رو فراخوانی کرد
اسمشو نمیگیریم که.... آدرسش رو میگیریم.

vcldeveloper
یک شنبه 09 خرداد 1389, 13:10 عصر
نمیشه نام یک تابع رو به مقدار پارامتر دریافت کرد و بعد اون رو فراخوانی کرد
با استفاده از RTTI میشه.


من میخوام یه تابعی بسازم که پارامتر ورودیش یه تابع باشه و بتونم از درون تابعم اون تابع رو که به عنوان ورودی گرفتم فراخوانی کنم. چطوری؟
یک راه استفاده از Function Pointers هست که آقای مهری براتون در پست شماره 3 تاپیک توضیح دادند، یک راه هم استفاده از Anonymous Methods هست.

Mahmood_M
یک شنبه 09 خرداد 1389, 13:19 عصر
به نظرم باید به نوعی یک تابع CallBack بسازید ...
یک مثال می زنم ، فرض کنید می خوایم مقدار طول یک رشته رو بدست بیاریم ...
ابتدا تابعی به صورت زیر به عنوان یک Type تعریف کنید :
type
TMyFunc = Function(S : String) : Integer;
به تعریف Type توجه کنید ، نام تابع نوشته نشد ، فقط مشخص شد که Type ما یک تابع هست و پارامترها و خروجی اون مشخص شد ...
یک تابع تعریف می کنیم که خصوصیات TMyFunc رو داشته باشه و در اون مقدار طول رشته رو حساب می کنیم ، مثال :
function Func(S1 : String) : Integer;
begin
Result := Length(S1);
end;
توجه کنید که تابعی که تعریف کردیم نوع و تعداد پارامترها و خروجیش مانند TMyFunc هست ...

حالا یک تابع اصلی تعریف می کنیم که وظیفه ی اجرای این تابع رو بر عهده داره :
function MainFunc(S2 : String; F : TMyFunc) : Integer;
begin
Result := F(S2);
end;
این تابع ، یک تابع از نوع TMyFunc به عنوان ورودی و یک مقدار String هم دریافت میکنه و تابع دریافتی رو با دادن مقدار String به عنوان ورودی به تابع اجرا می کنه ...

نحوه ی استفادش هم که مشخص هست :
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
Edit1.Text := IntToStr(MainFunc('Mahmood', Func));
end;
توجه کنید که Func رو به عنوان یک تابع تعریف کردیم ...

راههای دیگه ای هم هست ، ولی احتمالا مشکلتون به همین صورت حل میشه ، اگر مشکلتون حل نشد بگید تا راه دیگه ای هم مثال بزنم ، البته دردسر زیاد داره و کمی باید با احتیاط ازش استفاده کرد !!

به هر حال ، امیدوارم مشکلتون حل بشه ، موفق باشید ...

1485159
یک شنبه 09 خرداد 1389, 13:19 عصر
با استفاده از RTTI میشه.
میشه توضیح بدید؟

یک راه هم استفاده از Anonymous Methods هست.
چی؟؟؟


راههای دیگه ای هم هست ، ولی احتمالا مشکلتون به همین صورت حل میشه ، اگر مشکلتون حل نشد بگید تا راه دیگه ای هم مثال بزنم ، البته دردسر زیاد داره و کمی باید با احتیاط ازش استفاده کرد !!

به هر حال ، امیدوارم مشکلتون حل بشه ، موفق باشید ...
ممنون مشکلم حل شد ولی اگه امکان داره راه دیگش رو هم بگید.

vcldeveloper
یک شنبه 09 خرداد 1389, 14:11 عصر
چی؟؟؟Anonymous Methods؛ به عنوان یک نمونه ساده:
procedure Test(AText: string; ATextProducer: TFunc<string>);
begin
ShowMessage(AText + #13#10 + ATextProducer);

end;
procedure TForm9.Button1Click(Sender: TObject);
begin
Test('This is a test', function: string
begin
Result := 'This one is generated from our method.';
end);
end;

procedure TForm9.Button2Click(Sender: TObject);
begin
Test('This is a test', function: string
begin
Result := 'This is another sample.' + #13#10 +
'And here is an extra line';
end);
end;


میشه توضیح بدید؟RTTI مخفف RunTime Type Information هست. اینها اطلاعاتی درباره Typeها و داده های استفاده شده در برنامه هستند، و میشه با استفاده از این داده ها، اطلاعات مربوط به Typeهای مختلف را استخراج کرد، و یا از آنها نمونه های جدیدی ساخت. به این نوع از اطلاعات Meta-data گفته میشه، یعنی داده هایی که داده ها را توضیح میدند. مثلا کد زیر، نام یک کلاس در برنامه را دریافت میکنه، و لیست متدهای آن کلاس را در یک Memo برمیگردونه:

procedure GetMethodsList(const ClassName: string; MethodsList: TStrings);
var
ctx : TRttiContext;
rt : TRttiType;
m : TRttiMethod;
begin
if not Assigned(MethodsList) then
raise Exception.Create('MethodList parameter is not created!');

ctx := TRttiContext.Create;
try
rt := ctx.FindType(ClassName);
if not Assigned(rt) then
raise Exception.CreateFmt('Class type "%s" not found.',[ClassName]);
MethodsList.Clear;
if rt.IsInstance then
for m in rt.GetMethods do
MethodsList.Add(m.ToString);
finally
ctx.Free;
end;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
GetMethodsList(Edit1.Text, Memo1.Lines);
end;

دقت کنید که نام کلاس ها باید به همراه نام یونیت وارد بشند، مثلا Forms.TForm، یا Unit1.TForm1.

متدهایی که به این شکل لیست شدن را میشه با استفاده از متد Invoke از کلاس TRttiMethod فراخوانی کرد.

1485159
یک شنبه 09 خرداد 1389, 14:18 عصر
RTTI مخفف RunTime Type Information هست. اینها اطلاعاتی درباره Typeها و داده های استفاده شده در برنامه هستند، و میشه با استفاده از این داده ها، اطلاعات مربوط به Typeهای مختلف را استخراج کرد، و یا از آنها نمونه های جدیدی ساخت. به این نوع از اطلاعات Meta-data گفته میشه، یعنی داده هایی که داده ها را توضیح میدند. مثلا کد زیر، نام یک کلاس در برنامه را دریافت میکنه، و لیست متدهای آن کلاس را در یک Memo برمیگردونه:
تا جایی که من متوجه شدم این روش برای کلاس ها هست نه توابع؟

مصطفی ساتکی
یک شنبه 09 خرداد 1389, 16:59 عصر
البته فکر کنم اون روشی که آقای کشاورز گفتن قابلیت فراخوانی Class Method ها رو داشته باشه که در قسمت public تعریف شدن.
برای Invoke کردن هم بایستی به صورت زیر استفاده بشه.

var cls :TClass;
if m.ToString = 'class function UnitName: string' then
begin
Cls := rt.AsInstance.MetaclassType;
v := m.Invoke(Cls,[]);
Form2.Caption := v.AsString;
end


برای دسترسی فقط به متدها هم می تونین از این روش استفاده کنید

type tprocex= procedure of object;
procedure TestEX(AInstance : TObject; AMethodName : String);
var Method : TMethod;
Proc : TProcEX;
begin
Method.Data := Pointer( AInstance);
Method.Code := AInstance.MethodAddress(AMethodName);

proc := TProcEX(Method);
proc;

end;
procedure TForm2.Button2Click(Sender: TObject);
begin
TestEX(Form2,Edit1.Text);

end;

vcldeveloper
یک شنبه 09 خرداد 1389, 18:42 عصر
البته فکر کنم اون روشی که آقای کشاورز گفتن قابلیت فراخوانی Class Method ها رو داشته باشه که در قسمت public تعریف شدن.
قبلا کامپایلر فقط برای متدهای public کلاس ها، و خصوصیات published آنها RTTI تولید می کرد؛ ولی در دلفی 2010، RTTI گسترش پیدا کرده، و می تونید حتی اطلاعات فیلدها و متدهای private رو هم از کلاس ها استخراج کنید. البته این امکان وجود داره که برای هر کلاس، نوع و حجم RTTIایی که تولید میشه را کنترل کرد، مثلا میشه با استفاده از Compiler Directive مربوطه، تعیین کرد که یک کلاس فقط متدهای publicاش RTTI داشته باشند، و فیلدها و سایر متدهای آن براشون RTTI تولید نشه.


تا جایی که من متوجه شدم این روش برای کلاس ها هست نه توابع؟
RTTI برای typeها تولید میشه، نه برای توابع. اگر اون type به صورت class یا record تعریف شده باشه، اطلاعات مربوط به متدهای آن هم در دسترس خواهد بود.