المقال مترجم من الروسية
http://www.citforum.ru/programming/delphi/miracles/
الكاتب : Максим Кузьминский
ماكسيم كوزمينسكي
هل تؤمن بالمعجزات ؟ انا ابضا اؤمن بالمعجزات ..بالتاكيد كل منا سيسلم ان شيئا من هذا القبيل يحدث مع اكواد برماجنا بل واحيانا يرفض البرنامج عملcompile له !؟ او ان البرنامج يظهر نتائج غيرمتوقعة..وهنا تراودك افكار عن مشاركة قوى خفيه في صنع هذه المعجزات
في هذا المقال سوف نتطرق الى 7 (من العديد ) من تلك المعجزات وسوف نحاول ان نزيل الغطاء السحري ونتاكد ان هذا ما هو الا خدعة...( Illusion) وهم ..
(Round Miracle) معجزة التقريب
انشي مشروعا جديدا سمه مثلا AllMiracles ضع مكونا من النوع button على الفورم.. الان بالضغط مرتين على الزر اكتب ماياتي
procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage( IntToStr( Round(3.5) - Round(2.5) ) ); end;
توقف قليلا وقل لي ما هي النتيجة المتوقعة ؟؟اتمنى ان لا تكون اجابتك بواحد والا لم تكن معجزة ..واذا كان حدسك ممتازا فستكون اجابتك 0 وهذا ابعد عن الحقيقة مقارنة عن ذي قبل واذا كنت مقامرا جيدا وتقرا الهلب جيدا فكنت ستجيب بالرقم 2!!
لاتصدق.. تضغط F9
هل تعرف لماذا؟؟؟
لو كنت قرات الهلب جيدا فانظر مالمكتوب عن الداله (التابع) Round :
Round returns an Int64 value that is the value of X rounded to the nearest whole number. If X is exactly halfway between two whole numbers, the result is always the even number.
هل عرفت السبب !!! :P
الان اتمنى ان تملك تصورا عن ما اتحدث عنه اليوم ... في هذا المقال لايوجد ما صعب تعمدنا تبسيط الكود حتى تتضح المشكلة
(Absolute Miracle)
الان اضف زرا اخر الى الفورم .. الان بالضغط مرتين على الزر اكتب ماياتي
procedure TForm1.Button2Click(Sender: TObject); var i1: int64; begin i1:= abs(low(integer)); ShowMessage(IntToStr(i1)); end;
قبل ان تقوم بالضغط على F9 سنقوم بتحليل الموقف
من المعروف ان Low مع ال integer تعطي القيمة -2147483648 (انظر الهلب) كما ان الهلب لا تعطي معلومة جديدة عن الabs
Abs returns the absolute value of the argument X. X is an integer-type or real-type expression
المتغير i1 عبارة عن int64 ..طبعا هذا صحيح لان 2147483648 تخرج عن حدود الinteger وهذه القيمة هي القيمة التي ننتظرها اليس كذلك . اضغط F9 وتاكد !!! ماذا ..غير معقول ان تعيد الداله abs عددا سالبا ..فكيف حدث ذلك...
تعالوا نتامل الموضوع مره اخري سوف نرى العباره abs(low(integer)) .ماذا نستطيع ان نقول عن هذه العبارة رغم استدعاء دوال في هذه العبارة الا انها في الحقيقة قيمة ثابته
ماذا يقول الهلب الثوابت Constant expressions :
...Constant expressions cannot include variables, pointers, or function calls, except calls to the following predefined functions: Abs...Low...
سوف نعرف ثابتا لهذه الطريقة :
const ci = abs(low(integer)); ...
هل تمكنت من تشغيل برنامج يحتوي على هذه العبارة ؟؟ على ماذا يدل هذا؟ على انها قيمة ثابته ؟ من كان يصدق هذا!!
ولكن لم لا تعطينا العبارة abs(low(integer)) عددا سالبا وكيف السبيل لفعل العكس
انظر المثال التالي
procedure TForm1.Button3Click(Sender: TObject); const ci = abs(low(integer)); var i1: int64; begin i1:= abs(int64(low(integer))); ShowMessage(IntToStr(i1)); end;
جرب هل رايت ؟ حصلنا على العكس وعرفنا السر ! :P
معناها ان العدد abs(low(integer)) خارج نطاق integer ويدخل نطاق int64
يالمناسبه ايضا العبارة abs(int64(low(integer))); تعتبر قيمة ثابته .
(One more low integer miracle).
جرب هذا الكود
procedure TfrmAllMiracles.btnLowIntMrclClick( Sender: TObject);
var
lowInt: integer;
begin
lowInt := -2147483648;
ShowMessageFmt('%d',[lowInt]);
end;مالمشكلة اجراء عادي جدا والقيمة قانونيه جدا؟؟ ومع ذلك لا نستطيع اجراء compile بحجة ان
Overflow in conversion or arithmetic operation
اضغط على F1 ماذا تلاحظ
The compiler has detected an overflow in an arithmetic expression: the result of the expression is too large to be represented in 32 bits
كيف overflow اذا كانت القيمة قانونية ؟ اذا لابد ان المترجم يقوم بانشاء قيمة موجبة ةعد ذلك يضربها في -1 :P
لكن كيف الخروج من هذه المحنه انظر الكود:
procedure TForm1.Button4Click(Sender: TObject);
var
lowInt: integer;
begin
lowInt := -int64(2147483648);
ShowMessageFmt('%d',[lowInt]);
end;ربما كان المثال ساذجا لكنه يعطي تصورا حقيقيا لما يفعلة مترجم الدلفي لانشاء الثوابت ومنح القيم !
التالي
(String Trick)
سيدي العزيز انظر الى هذا المثال طبعا اضف زر الى الفورم بالضغط مرتين على هذا الزر
واكتب ماياتي
procedure TfrmAllMiracles.btnCopyMrclClick (Sender: TObject); const cs: array[0..1] of char='01'; begin ShowMessage(copy(cs,0,1)+copy(cs,1,1)); end;
ما هي الاجابه بالنظر الى الكود السابق؟
هه خطا طبعا . الاجابه هي "00" هل تعرف لماذا عد الى الهلب وانظر الى الى تعريف ال copy
function Copy(S; Index, Count: Integer): string; function Copy(S; Index, Count: Integer): array;
اعتقد ان الامر مفهوم الان...
في المره الاولي تم استدعاء copy لمعالجة (arry)مصفوفه ديناميكة تبدا بالعنصر ذو العنوان صفر!
في المراة الثانية تم استدعاء الداله وكانها string يبدا من العنصر ذو العنوان صفر!
(Is-Miracle)
اكتب في الجزء protected من الفورم الحقل FControl من النوع TControl
والان ضع في على الفورم زرا جديدا وفي الحدث onClick اكتب الكود
procedure TfrmAllMiracles.btnIsMrclClick(Sender: TObject);
begin
if (FControl is TControl) then
begin
if not Assigned(FControl) then
FControl := TControl.Create(Self);
end
else
ShowMessage('Not a Control');
end;قم بعمل لاحظ انك مهما قمت بعمل compile فانك لن ترى غير الرساله 'Not a Control' والباني للصنف TControl
لن يستدعى ابدا!!
ماذا يقول الهلب لنا في هذا الامر
The expression object is class returns True if object is an instance of the class denoted by class or one of its descendants, and False otherwise. (If object is nil, the result is False.)
هل فهمت سر الموضوع ؟ الير يكمن في كون is كانت تتعامل مع متغير في الحقيقة لم يخصص لها اي ذاكرة يعني في الحقيقة ما هي الا مؤشر لا يشير الى شيء (null )
انوه الى كون اختيار الصنف Tcontrol كان اختياريا كان من الممكن ام يحصل نفس الشي مع اي صنف اخر سوف نرى شيئا مشابها في المثال القادم
(Is-Miracle II)
كون مكتبه dll وسمها يالاسم AllMirrLib
library AllMirrLib; uses Controls; function IsControlLib(const anObj: TObject): boolean; begin Result := anObj is TControl; end; exports IsControlLib; end;
قم بعمل نفس الخطوات في المثال الماضي مع الحقل FControl ثم اضف مكون Button وفي الحدث onClick واكتب الاتي
procedure TForm1.Button2Click(Sender: TObject);
begin
FControl := TControl.Create(nil);
try
if not IsControlLib(FControl) then
ShowMessage('Not a Control');
finally
FreeAndNil(FControl);
end;
end;اضغط F9 ؟
ظهور الرساله مرة اخري ؟؟ معني هذا ان FControl =Null رغم اننا استدعينا الباني قبلا فما المشكلة اذن
سوف نعدل الكود في مكتية الdll الى الكود التالي
function IsControlLib(const anObj: TObject): boolean; var p3,p4: pointer; begin p3 := pointer(anObj.ClassType); p4 := pointer(TControl); Result := (p3=p4); end;
لا تنسى ان تعمل Build لمكتبة الdll ثم عد الى البرنامج الاصلي
بينما في برنا مجنا الاصلي نعدل كالاتي في الحدث onClick من الزر اكتب
procedure TForm1.Button1Click(Sender: TObject);
var
p1, p2: pointer;
begin
FControl := TControl.Create(nil);
try
p1 := pointer(FControl.ClassType);
p2 := pointer(TControl);
if (p1=p2)then showmessage('yes')
else showmessage('No');
if IsControlLib(FControl) then
ShowMessage('yes dll')
else ShowMessage('No dll')
finally
FreeAndNil(FControl);
end;
end;قم بالتشغيل والان اضغط الزر ماذا تلاحظ p1, p2 متساويان في البرنامج لذلك ترى الرساله yes
بينما ظهور ال NO dll في الرساله الثانية يدل على عدم تساوي هاتين القيمتين في Dll !!!
رغم انهما يشيران الى نفس الشي اين ترى اشارة الى هذا الشي في الهلب ؟؟
Use ClassNameIs when writing conditional code based on an object's type or to query objects across modules, or DLLs.
هذا في الحديث عن المنهج (method ) ClassNameIs
واضح انه في الdll نوعان من الTcontrol !!
***************
(Variant trick)
ماذا يقول الهلب عن Variants in expressions
...In a binary operation, if only one operand is a variant, the other is converted to a variant..
لايبدوا هذا مثير انك تستطيع ان تجمع كل شي مع ال variant ناتج الجاع يكون variant دائما
اتظر الى هذا الكود
procedure TForm1.Button3Click(Sender: TObject);
var
v: variant;
b: boolean;
i: integer;
s: string;
d: TDatetime;
x: Double;
begin
v:=0;
b := true;
i := 2;
s := '3';
d := StrToDateTime('01.01.01');
x := 5;
v := v+i+s+d+x+b;
ShowMessage(VarToStr(v));
end;كيف ممكن ان تجمع كل هذه الاشياء ؟
الاغرب ان هذه الكود لا يكتفي بالعمل وبل يعطي ناتجا ايضا :P
