PDA

View Full Version : آموزش: چگونه دستورات شرطی به کار نبریم!



a_mosavian
دوشنبه 08 فروردین 1390, 14:03 عصر
چند روز پیش آموزشی (http://barnamenevis.org/showthread.php?280128-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D9%87%D8%A7%DB%8C-%DA%AF%D9%88%D9%86%D8%A7%DA%AF%D9%88%D9%86-%D8%A7%D8%B2-%D9%85%D8%AA%D8%AF-AddObject-%D8%AF%D8%B1-TStringList) زده بودم. یکی از دوستان پرسیده بود که چرا از دستور case یا if بهره نگرفته ام. در واقع به این دستورها دستور های شرطی می گویند. دستور های شرطی گرچه در جای خود مهم هستند و کاربرد دارند، اما بهره گیری بی رویه از آنها منجر به کند شدن برنامه، ناخوانایی کد و توسعه ناپذیری برنامه خواهد شد. در عمل استفاده نابجا از دستور شرطی برای برنامه یک جور خودکشی است!
حال چگونه دستورهای شرطی را به کار نیریم؟ به کد زیر بنگرید:

if X = 5 then
Button1.Enabled := True
else
Button1.Enabled := False;
این کد چک می کند اگر X برابر 5 بود یک دکمه را فعال کند. حال من این کد را در یک خط می نویسم

Button1.Enabled := X = 5;
در واقع من اسم سبک نخست را کدنویسی به سبک بیسیک می نامم (از آنجایی که در بیسیک عملگر تساوی و assignment جدا از هم نیست، کد نخست را گریزناپذیر می کند). این مثال بسیار ساده ای بود. بی گمان برنامه نویسی های شما به این سادگی ها نیست.
حال سراغ مثال پبشرفته تری می رویم. بینگارید می خواهید تابعی بنویسید که شماره روز در هفته را گرفته و به شما نام روز را در هفته برگرداند. یک راه ساده انگارانه بهره گیری از دستور case است:

case DayOfTheWeek(Date) of
1: Result := 'Monday';
2: Result := 'Tuesday';
3: Result := 'Wednesday';
4: Result := 'Thursday';
5: Result := 'Friday';
6: Result := 'Saturday';
7: Result := 'Sunday';
end;
حال کمی پیشرفته تر می اندیشیم! می خواهیم دستوری بنویسیم که دستور شرطی نداشته باشد

const
DayName: array[1..7] of String = ('Monday','Tuesday','Wednesday','Thursday','Friday ','Saturday','Sunday');
begin
Result:=DayName[DayOfTheWeek(Date)];
کد دوم هم خواناتر است، هم کوتاه تر است، هم سرعت آن بالاتر است و در نهایت هیچ دستور شرطی در آن دیده نمی شود، براین پایه در کد کامپایل شده آن هم خبری از cmp و پرش های آن نیست.
مثال هایی که من زدم ساده بود، اما با کمی هوش و درایت می توان کدهای بسیار بزرگتری را ساده کنید و بر سرعت اجرای آن بیفزایید. برنامه نویس باید بداند که چگونه کد های خواناتر و کوتاه تری بنویسد و کارایی برنامه را قربانی ناآگاهی خود نکند.

mbshareat
دوشنبه 08 فروردین 1390, 22:56 عصر
با سلام
در کوتاه شدن کد حرفی نیست؛ ولی مزیت کد طولانی این است که کنار هر شرطی می شه توضیح لازم رو برای وقتی که نحوه کارکرد برنامه رو فراموش می کنیم قید کنیم. :گیج:
بعضی وقتها هم انتساب نتیجه بررسی تساوی و مسائل از این قبیل برنامه رو به کلی برای آینده غیر قابل درک می کنه!:بامزه:
(البته من این حرفها رو با توجه به استعداد پایین خودم گفتم شما به دل نگیرین!)

a_mosavian
دوشنبه 08 فروردین 1390, 23:34 عصر
با سلام
در کوتاه شدن کد حرفی نیست؛ ولی مزیت کد طولانی این است که کنار هر شرطی می شه توضیح لازم رو برای وقتی که نحوه کارکرد برنامه رو فراموش می کنیم قید کنیم. :گیج:
بعضی وقتها هم انتساب نتیجه بررسی تساوی و مسائل از این قبیل برنامه رو به کلی برای آینده غیر قابل درک می کنه!:بامزه:
(البته من این حرفها رو با توجه به استعداد پایین خودم گفتم شما به دل نگیرین!)
تنها کوتاه شدن نیست. سرعت و کارایی برنامه نیز کاهش می دهد. درباره توضیح، ربطی به کد ندارد! هر جا خواستید هر چند خط می توانید توضیح بدهید. افزون، کد نویسی بدون دستورهای شرطی کمتر گیج کننده است و راحتتر یادآوری می شود و توضیح کمتری می خواهد. من به شخصه حاضر نیستم خود برنامه را از میان ببرم که بتوانم جلو هر خط توضیح بنویسم! وانگهی قصد داشتم مقاله ای برای اصول برنامه نوشتن بنویسم. در آنها خواهم گفت چگونه کد خوانا بنویسید.

vcldeveloper
سه شنبه 09 فروردین 1390, 00:19 صبح
تنها کوتاه شدن نیست. سرعت و کارایی برنامه نیز کاهش می دهد.در بسیاری از موارد، تفاوت بسیار ناچیز هست. در واقع کامپایلرهای امروزی اونقدر عقب افتاده نیستند، و سعی می کنند بهینه ترین کدها را از سورس کد تولید کنند. در مثال اول شما، از نظر کامپایلر در هر دو حالت از شرط استفاده شده، فقط در حالت دوم شما شرط را در سورس کد خودتان نمی بینید. البته در کد اول چون باید متناسب با نتیجه شرط یکی از دو بلوک کد اجرا بشه، فقط یک دستور ماشین اضافی اجرا میشه، اما کد ماشین تولید شده تقریبا دو برابر میشه، چون دو بار Assignment در کد داشتید، ولی از نظر کارایی و سرعت تفاوت دو حالت فوق العاده ناچیز خواهد بود.

در مثال دوم، کارایی کد دوم بیشتر هست، البته به میزان کمی، ولی حافظه بیشتری استفاده شده. معمولا بین نوشتن کد با سرعت بالا و صرفه جویی در مصرف حافظه نسبت عکس وجود داره، و متناسب با شرایط ماشینی که براش کد می نویسید، باید بهینه سازی مناسب برای تولید کد پر سرعت با مصرف حافظه مناسب انجام بشه.

در واقع بهینه سازی کد باید با دقت انجام بشه، این پست به معنای تشویق دیگران به استفاده از ساختارهای نامناسب نیست؛ بلکه باید هر ساختاری را در جای خودش به درستی استفاده کرد، و البته استفاده از ساختارهای شرطی، خودکشی نیست ;-) بلکه استفاده ناصحیح از هر چیزی، موجب دردسر خواهد شد.

a_mosavian
سه شنبه 09 فروردین 1390, 03:03 صبح
در بسیاری از موارد، تفاوت بسیار ناچیز هست. در واقع کامپایلرهای امروزی اونقدر عقب افتاده نیستند، و سعی می کنند بهینه ترین کدها را از سورس کد تولید کنند. در مثال اول شما، از نظر کامپایلر در هر دو حالت از شرط استفاده شده، فقط در حالت دوم شما شرط را در سورس کد خودتان نمی بینید. البته در کد اول چون باید متناسب با نتیجه شرط یکی از دو بلوک کد اجرا بشه، فقط یک دستور ماشین اضافی اجرا میشه، اما کد ماشین تولید شده تقریبا دو برابر میشه، چون دو بار Assignment در کد داشتید، ولی از نظر کارایی و سرعت تفاوت دو حالت فوق العاده ناچیز خواهد بود.

در مثال دوم، کارایی کد دوم بیشتر هست، البته به میزان کمی، ولی حافظه بیشتری استفاده شده. معمولا بین نوشتن کد با سرعت بالا و صرفه جویی در مصرف حافظه نسبت عکس وجود داره، و متناسب با شرایط ماشینی که براش کد می نویسید، باید بهینه سازی مناسب برای تولید کد پر سرعت با مصرف حافظه مناسب انجام بشه.

در واقع بهینه سازی کد باید با دقت انجام بشه، این پست به معنای تشویق دیگران به استفاده از ساختارهای نامناسب نیست؛ بلکه باید هر ساختاری را در جای خودش به درستی استفاده کرد، و البته استفاده از ساختارهای شرطی، خودکشی نیست ;-) بلکه استفاده ناصحیح از هر چیزی، موجب دردسر خواهد شد.
اینگونه نیست! من مثال نخست را در یک حلقه یکصد میلیون بار اجرا کردم. در اجرای نخست با کامپایلر دلفی 2010 کد نابهینه حدود 110 میلی ثانیه و کد بهینه 60 میلی ثانیه طول کشید و بارهای دگر نیز نتیجه مشابه بدست آمد!
در مثال دوم، حجم کد اسمبلی برای هر شرط دستور case، شد 15 بایت و 4 خط کد اسمبلی و خود دستور هم 81 بایت و 27 خط کد اسمبلی! (51 خط کلا) کد بهینه شده تنها 9 خط کد اسمبلی شد! (توضیح اینکه در هر دو، داده ها به صورت پوینتر بودند و هر داده 4 بایت می برد) البته در این مثال با بهره گیری از دستورهای date و dayofweek تفاوت زمانی معنا دار دیده نشد، حتی هنگامی که بجای این توابع، (I mod 7) + 1 را جایگزین کردم هم تفاوت معنادار بدست نیامد! خودم هم مانند شما انتظار نداشتم :قهقهه: ولی از نگرش حجمی بسیار بهینه تر بود!
البته سرعت شگفت آور پردازنده های کنونی، خرفتی برنامه نویس ها را کاملا می پوشاند! من هم گفتم کاربرد خود را دارد و استفاده نابجا از آن درست نیست :دی

vcldeveloper
سه شنبه 09 فروردین 1390, 06:08 صبح
اینگونه نیست! من مثال نخست را در یک حلقه یکصد میلیون بار اجرا کردم. در اجرای نخست با کامپایلر دلفی 2010 کد نابهینه حدود 110 میلی ثانیه و کد بهینه 60 میلی ثانیه طول کشید و بارهای دگر نیز نتیجه مشابه بدست آمد!کل تفاوت دو کد ماشینی که به عنوان مثال اول قرار دادید با هم در یک دستور JNZ هست.
برای همچین کدی، در یک tight loop با یک صد میلیون تکرار (که در عمل چیز نادری هست)، تفاوت سرعت دو کد مورد نظر شما کمتر از 3 درصد هست، نه 45 درصدی که شما به دست آوردید!


در مثال دوم، حجم کد اسمبلی برای هر شرط دستور case، شد 15 بایت و 4 خط کد اسمبلی و خود دستور هم 81 بایت و 27 خط کد اسمبلی! (51 خط کلا) کد بهینه شده تنها 9 خط کد اسمبلی شد! (توضیح اینکه در هر دو، داده ها به صورت پوینتر بودند و هر داده 4 بایت می برد) البته در این مثال با بهره گیری از دستورهای date و dayofweek تفاوت زمانی معنا دار دیده نشدتوجه نکردید که برای شما توضیح داده شد، در مثال دوم، بهینه سازی در سرعت از طریق افزایش حافظه مصرفی به دست اومده، و بین بهینه سازی سرعت و بهینه سازی مصرف حافظه معمولا نسبت عکس وجود داره. در اون مثال شما از یک آرایه ایستا استفاده کردید که فضای اضافی روی Data Segment اشغال میکنه.

a_mosavian
سه شنبه 09 فروردین 1390, 14:59 عصر
کل تفاوت دو کد ماشینی که به عنوان مثال اول قرار دادید با هم در یک دستور JNZ هست.
برای همچین کدی، در یک tight loop با یک صد میلیون تکرار (که در عمل چیز نادری هست)، تفاوت سرعت دو کد مورد نظر شما کمتر از 3 درصد هست، نه 45 درصدی که شما به دست آوردید!

توجه نکردید که برای شما توضیح داده شد، در مثال دوم، بهینه سازی در سرعت از طریق افزایش حافظه مصرفی به دست اومده، و بین بهینه سازی سرعت و بهینه سازی مصرف حافظه معمولا نسبت عکس وجود داره. در اون مثال شما از یک آرایه ایستا استفاده کردید که فضای اضافی روی Data Segment اشغال میکنه.

درباره مثال اول توی حلقه گذاشتم که تفاوت سرعت آنها قابل ثبت باشد. تکرار هم کردم کدش را هم در زیر می آورم و همان نتیجه ها بود! درباره کدهای اسمبلی مثال نخست، بدون در نظر گرفتن کد مربوط به حلقه ها، کد اولیه 12 خط کد اسمبلی و 26 بایت و کد بهینه شده 9 خط کد اسمبلی و 21 بایت می باشد.:چشمک:

حافظه اشغال شده توسط Data Segment در هر دو یکی است. افزایش مصرف حافظه ای وجود ندارد. اینجا به علت تغییر نگرش به کل مسئله بهینه سازی هزینه ای نداشته.:لبخند:

آپدیت: با افزودن Label3.Caption:=BoolToStr(X); به خط پس از هر حلقه مثال نخست، سرعت اجرای کد اولیه به 320 میلی ثانیه افزایش و سرعت اجرای کد بهینه حدود 170 میلی ثانیه شد. یعنی همان نسبتی که پیش از این به دست آمد.

procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
X: Boolean;
C1, C2, C3: Cardinal;
begin
C1:=GetTickCount;
for I := 0 to 100000000 do
X := (I mod 2) = 0;
Label3.Caption:=BoolToStr(X);
C2:=GetTickCount - C1;
for I := 0 to 100000000 do
if (I mod 2) = 0 then
X := True
else
X := False;
Label3.Caption:=BoolToStr(X);
C3:=GetTickCount - C1 - C2;
Label1.Caption:=IntToStr(C2);
Label2.Caption:=IntToStr(C3);
end;

Mask
سه شنبه 09 فروردین 1390, 15:53 عصر
با تشکر از دوست عزیز.
کار جالبی بود.
میخاستم بدونم معادل دستور زیر بدون if چی میشه؟

if Label1.Caption='a' then
Label2.Caption:='1'
else
Label2.Caption:='2';

a_mosavian
سه شنبه 09 فروردین 1390, 16:16 عصر
با تشکر از دوست عزیز.
کار جالبی بود.
میخاستم بدونم معادل دستور زیر بدون if چی میشه؟

if Label1.Caption='a' then
Label2.Caption:='1'
else
Label2.Caption:='2';

const
DataForCon: array[Boolean] of String = ('2', '1');
begin
Result:=DataForCon[Label1.Caption='a'];

حتی اگر بروید تابع BoolToStr را در VCL بنگرید می بینید که از شیوه ی بهینه شده ای که بنده گفتم بهره گرفته! از آنجا که کتابخانه VCL تلاش شده که بسیار بهینه نوشته شود، نشان می دهد این کد بهینه ترین کد است. از نظر حافظه هم بر خلاف نظر اقای کشاورز حافظه کمتری را اشغال می کند:لبخند:. خط آخرش را بنگرید:
function BoolToStr(B: Boolean; UseBoolStrs: Boolean = False): string;
const
cSimpleBoolStrs: array [boolean] of String = ('0', '-1');
begin
if UseBoolStrs then
begin
VerifyBoolStrArray;
if B then
Result := TrueBoolStrs[0]
else
Result := FalseBoolStrs[0];
end
else
Result := cSimpleBoolStrs[B];
end;

M_Maskout
سه شنبه 09 فروردین 1390, 18:00 عصر
در واقع من اسم سبک نخست را کدنویسی به سبک بیسیک می نامم (از آنجایی که در بیسیک عملگر تساوی و assignment جدا از هم نیست، کد نخست را گریزناپذیر می کند
دستور
Button1.Enabled = X = 5
در Basic و فرزاندان آن کاملاً معتبره.
ضمناً کدهای کوتاهتر، الزاماً خواناتر نیستن. مخصوصاً وقتی که بخوایم اونا رو trace کنیم. و مخصوصاً بیشتر وقتی بخوایم بعدها اونا رو trace کنیم.
در مورد مثال دومی: در نظر بگیرین یه برنامه داریم که فاصله قسمت const از begin مثلاً 100 یا 150 خطه، اون وقت واسه دیدن مقادیر آرایه باید چی کار کرد (هی برو بالا، هی بیا پایین). همونطور که بعضی از دوستان اشاره کردن، اینجوری کد نوشتن الزاماً بهتر نیست و فقط جذابه (بقیه فکر می‌کنن، کد نویس خیلی حرفه‌ای بوده!!!)

a_mosavian
سه شنبه 09 فروردین 1390, 18:29 عصر
دستور
Button1.Enabled = X = 5
در Basic و فرزاندان آن کاملاً معتبره.
ضمناً کدهای کوتاهتر، الزاماً خواناتر نیستن. مخصوصاً وقتی که بخوایم اونا رو trace کنیم. و مخصوصاً بیشتر وقتی بخوایم بعدها اونا رو trace کنیم.
در مورد مثال دومی: در نظر بگیرین یه برنامه داریم که فاصله قسمت const از begin مثلاً 100 یا 150 خطه، اون وقت واسه دیدن مقادیر آرایه باید چی کار کرد (هی برو بالا، هی بیا پایین). همونطور که بعضی از دوستان اشاره کردن، اینجوری کد نوشتن الزاماً بهتر نیست و فقط جذابه (بقیه فکر می‌کنن، کد نویس خیلی حرفه‌ای بوده!!!)

من روی بیسیک چندان وارد نیستم شاید اشتباه می کنم.
کد معادل همین برنامه ای که در نظر گرفته اید با case بالغ بر چندین هزار خط خواهد شد! من نمی دونم چنین کدی رو چجوری مطالعه خواهید کرد. وانگهی کد بهینه شده معمولا یکی دو خط است و تنها تبدیل نوع هست و دستوری نیست! شما همون بالا رو بخونی کافیست لازم نیست پایین بیاید :قهقهه:
از آنجا که مقدار کد تنها یک خط می باشد trace کردن آن نیز بسیار ساده تر از چند هزار خظ شما خواهد شد!
الزاما اگر انجام پذیر باشد بهتر است! جذاب تر هم هست! حرفه ای تر هم هست! اما حرفه ای ها چیزهای خوب را بکار می برند :لبخند:

AliReza Vafakhah
سه شنبه 09 فروردین 1390, 18:30 عصر
ضمناً کدهای کوتاهتر، الزاماً خواناتر نیستن. مخصوصاً وقتی که بخوایم اونا رو trace کنیم. و مخصوصاً بیشتر وقتی بخوایم بعدها اونا رو trace کنیم.

این شیوه کدنویسی و توجه زیاد به بهینه بودن کد از لحاظ سرعت و حافظه ، به نظر من یک برنامه نویسی حرفه ای است.
خوانا بودن کد به نظر من به کوتاهی کد بستگی داره و صد البته Rem ها (توضیح ها)

M_Maskout
سه شنبه 09 فروردین 1390, 19:42 عصر
کد معادل همین برنامه ای که در نظر گرفته اید با case بالغ بر چندین هزار خط خواهد شد!
من فقط مثالی که شما آوردین رو در نظر گرفتم با همون هفت تا المنت یعنی اینکه بعد از سطری که آرایه رو تعریف کردین 100 خط (فقط 100 خط یعنی مثلاً یه برنامه آموزشی نه یه پروژه واقعی) برنامه بنویسین بعد بخواین با روشی که نشون داده شده از آرایه استفاده کنین؛ در نظر بگرین در زمان trace شما مجبورین مدام مقدار DayOfTheWeek(Date) رو کنترل کنین (تازه در این مورد خاص اصلاً این مقدار قابل کنترل نیست چون مقدار خروجی توابع رو نمی‌شه در زمان trace دید) ولی با همون ساختار case با اولین trace بعد از خط case شما متوجه مقدار خروجی تابع مورد مثال می‌شین.
ضمناً من نگفتم این روش خوب نیست؛ گفتم الزاماً بهتر از روش‌های دیگه نیست؛ این یعنی روش، روش درست و خوبیه. و ...

a_mosavian
چهارشنبه 10 فروردین 1390, 00:52 صبح
من فقط مثالی که شما آوردین رو در نظر گرفتم با همون هفت تا المنت یعنی اینکه بعد از سطری که آرایه رو تعریف کردین 100 خط (فقط 100 خط یعنی مثلاً یه برنامه آموزشی نه یه پروژه واقعی) برنامه بنویسین بعد بخواین با روشی که نشون داده شده از آرایه استفاده کنین؛ در نظر بگرین در زمان trace شما مجبورین مدام مقدار DayOfTheWeek(Date) رو کنترل کنین (تازه در این مورد خاص اصلاً این مقدار قابل کنترل نیست چون مقدار خروجی توابع رو نمی‌شه در زمان trace دید) ولی با همون ساختار case با اولین trace بعد از خط case شما متوجه مقدار خروجی تابع مورد مثال می‌شین.
ضمناً من نگفتم این روش خوب نیست؛ گفتم الزاماً بهتر از روش‌های دیگه نیست؛ این یعنی روش، روش درست و خوبیه. و ...
شما اون تابع رو باید در خود تابع trace کنید! وانگهی شما برنامه می نویسید که یه کاری براتون انجام بده یا می نویسید که traceش کنید؟! و هنوز هم نگفته اید اون چند هزار خط رو چند سال طول می کشه تا trace کنید؟ :دی

vcldeveloper
چهارشنبه 10 فروردین 1390, 04:11 صبح
درباره مثال اول توی حلقه گذاشتم که تفاوت سرعت آنها قابل ثبت باشد. تکرار هم کردم کدش را هم در زیر می آورم و همان نتیجه ها بود!
یکی از چیزهایی که باید برای تست کردن و مقایسه کدها یاد بگیرید، این هست که سعی کنید شرایط تست را کنترل کنید، تا حتی المکان فاکتورهای کمتری، به جز فاکتور مورد بررسی، بر روی تست تاثیر بزارند. کدی که شما نوشتید، ارزش چندانی برای تست نداره، چون حتی با جا به جا کردن ترتیب اجرای حلقه ها، نتایج به دست آمده از تست متفاوت خواهد بود! در واقع شما اومدید در یک تابع، یک متغیر روی stack تعریف کردید، همون متغیر رو در دو حلقه پشت سر هم استفاده کردید، در حالی که قبلا هم گفته شد که کامپایلر بهینه سازی های مختلفی روی کد انجام میده، و با همچین بهینه سازی هایی، شرایط اجرای حلقه اول و حلقه دوم یکسان نخواهد بود. بماند که در مثال اولیه، تغییر خصوصیت یک شی که منجر به فراخوانی یک تابع میشه هم با تخصیص حافظه به یک متغیر محلی روی stack فرق میکنه، چون بهینه سازی های کامپایلر برای اونها فرق میکنه.

اگر بخواید واقعا این کدها را تست کنید، با توجه به حالات مختلف نتیجه شرط ها، می تونید کدی مثل این بنویسید:

unit Unit1;

interface

uses
Windows, SysUtils, Classes, Controls, Forms, StdCtrls;

type
TfrmTest = class(TForm)
Button1: TButton;
btnDoTests: TButton;
mmoLogs: TMemo;
procedure btnDoTestsClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
const MaxLoopCount = 100000000;
const ExpectedValue = 5;
private
FRandomValue : Integer;
procedure Log(const TestName: string; Value: Integer; Res: Cardinal);
function TestCase1(Value, Expected: Integer; AControl: TControl) : Cardinal;
function TestCase2(Value, Expected: Integer; AControl: TControl) : Cardinal;
public
procedure DoTest1_Negative;
procedure DoTest1_Positive;
procedure DoTest1_Random;
procedure DoTest2_Negative;
procedure DoTest2_Positive;
procedure DoTest2_Random;
end;

var
frmTest: TfrmTest;

implementation

{$R *.dfm}

procedure TfrmTest.btnDoTestsClick(Sender: TObject);
begin
DoTest1_Negative;
DoTest2_Negative;
DoTest1_Positive;
DoTest2_Positive;
DoTest1_Random;
DoTest2_Random;
end;

procedure TfrmTest.DoTest1_Negative;
var
Value : Integer;
R : Cardinal;
begin
Value := 4;
R := TestCase1(Value,ExpectedValue,Button1);
Log('Test1 Negative',Value,R);
end;

procedure TfrmTest.DoTest1_Positive;
var
Value : Integer;
R : Cardinal;
begin
Value := 5;
R := TestCase1(Value,ExpectedValue,Button1);
Log('Test1 Positive',Value,R);
end;

procedure TfrmTest.DoTest1_Random;
var
Value : Integer;
R : Cardinal;
begin
Value := FRandomValue;
R := TestCase1(Value,ExpectedValue,Button1);
Log('Test1 Random ',Value,R);
end;
procedure TfrmTest.DoTest2_Negative;
var
Value : Integer;
R : Cardinal;
begin
Value := 4;
R := TestCase2(Value,ExpectedValue,Button1);
Log('Test2 Negative',Value,R);
end;

procedure TfrmTest.DoTest2_Positive;
var
Value : Integer;
R : Cardinal;
begin
Value := 5;
R := TestCase2(Value,ExpectedValue,Button1);
Log('Test2 Positive',Value,R);
end;

procedure TfrmTest.DoTest2_Random;
var
Value : Integer;
R : Cardinal;
begin
Value := FRandomValue;
R := TestCase2(Value,ExpectedValue,Button1);
Log('Test2 Random ',Value,R);
end;

procedure TfrmTest.FormCreate(Sender: TObject);
begin
FRandomValue := Random(ExpectedValue);
end;

procedure TfrmTest.Log(const TestName: string; Value: Integer; Res: Cardinal);
begin
mmoLogs.Lines.Add(Format('%s (Value = %d) : Result = %d ms',[TestName,Value,Res]));
end;

function TfrmTest.TestCase1(Value, Expected: Integer; AControl: TControl) : Cardinal;
var
i: Integer;
begin
AControl.Enabled := True;
Result := GetTickCount;
for i := 0 to MaxLoopCount do
begin
if Value = Expected then
AControl.Enabled := True
else
AControl.Enabled := False;
end;
Result := GetTickCount - Result;
end;

function TfrmTest.TestCase2(Value, Expected: Integer; AControl: TControl) : Cardinal;
var
i: Integer;
begin
AControl.Enabled := True;
Result := GetTickCount;
for i := 0 to MaxLoopCount do
AControl.Enabled := Value = Expected;
Result := GetTickCount - Result;
end;

end.


با همچین تستی، در شرایطی که هر مجموعه تست سه بار اجرا بشه، با کامپایلر دلفی 2010، در صورتی که Compiler Optimization فعال باشه، حالت دوم حدودا 25% و اگر Compiler Optimization غیرفعال باشه، حالت دوم حدودا 8% از حالت اول سریع تر هست. علت اصلی اش هم ماهیت اصلی عملیات هست که یک Assignment ساده هست، و یک if-else برای همچین کار ساده ایی اضافی هست. البته با داده های Random، در حالتی که Compiler Optimization فعال باشه، در موارد خاصی حتی حالت دوم 1 تا 2 درصد از حالت اول کندتر میشه.



درباره کدهای اسمبلی مثال نخست، بدون در نظر گرفتن کد مربوط به حلقه ها، کد اولیه 12 خط کد اسمبلی و 26 بایت و کد بهینه شده 9 خط کد اسمبلی و 21 بایت می باشد.:چشمک:

حافظه اشغال شده توسط Data Segment در هر دو یکی است. افزایش مصرف حافظه ای وجود ندارد. اینجا به علت تغییر نگرش به کل مسئله بهینه سازی هزینه ای نداشته.:لبخند:
ظاهرا مطالب را به درستی مطالعه نمی کنید؛ اولا جایی گفته نشد که در مثال اول شما، حافظه بیشتری استفاده میشه، بلکه حافظه بیشتر برای مثال دوم بود، که شما یا دقت نکردید، یا چیز دیگه، و برای من مثال از مصرف حافظه مثال اول ارائه کردید، که کلا بی معنی هست.
ثانیا، مدعی شدید که کد اسمبلی بیشتری تولید میشه، اما احتمالا متوجه نیستید که چه کدی تولید شده، اون مجموعه کد به خاطر وجود دو بلوک از کد در دستور if-else ایجاد شده، اما در هر بار اجرا فقط یکی از اون بلوک ها اجرا خواهد شد، پس از نظر اجرا، تفاوت تعداد کدهای اجرا شده، یک دستور هست.

ثالثا، مدعی شدید که در VCL از کد مدنظر شما استفاده شده؛ باز هم دقت نکردید که اساسا بهینه سازی یعنی چی! برای شما توضیح داده شد که در بهینه سازی کد، معمولا بین افزایش سرعت و مصرف بهینه حافظه نسبت عکس وجود داره، و برنامه نویس بر اساس اولویت های خودش تصمیم میگیره که چطور تعادل مورد نظرش را در کد حفظ کنه. در یک سخت افزار با حافظه بسیار محدود، برنامه نویس وزنه استفاده بهینه از حافظه رو سنگین تر میکنه، و در یک سخت افزار دیگه، وزنه افزایش سرعت به قیمت افزایش مصرف حافظه رو. کد VCL برای سیستم عامل ویندوز نوشته شده، و حداقل نیازهای یک سیستم PC برای اجرای ویندوز، و محدودیت های تخصیص حافظه در ویندوز مشخص هست. برنامه نویس با توجه به واقعیات بهینه سازی میکنه، نه تخیلات. در اون کد VCL، برنامه نویس تشخیص داده با توجه به نوع و تعداد حالات ممکنه (که بسیار محدود هست)، سربار اضافی حافظه بسیار ناچیز خواهد بود، اما در ازاء آن، کارایی کد در حد مناسبی از نظر سرعت افزایش پیدا خواهد کرد، پس به این نتیجه رسیده که این بهینه سازی ارزشمند هست. بهینه سازی با تحلیل درست کد و شرایط اجرای کد حاصل میشه، و در شرایط مختلف باید بهینه سازی های مختلفی صورت بگیره. وقتی این اصول اولیه رو نمیدونید، به بهانه تولید کد بهینه شده، معمولا هم باعث تولید کد ناخوانا میشید، و هم در بعضی از مواقع، همون بهینه سازی بی مورد میتونه خودش موجب بروز مشکلات دیگه در کد بشه.

در ضمن، این رو هم به عنوان یک نکته اضافی به خاطر داشته باشید که کد VCL لزوما بهینه ترین کد نیست، بلکه در بسیاری از مواقع میشه کدهای بهینه تر از کد VCL پیدا کرد. ساده ترین مصداقش همون مدیر حافظه VCL بود که نسبت به FastMM در حد لاک پشت بود، و نهایتا بعد از سال ها، و فشار کاربران، بورلند قبول کرد اون کد بی خاصیت رو کنار بزاره، و از FastMM به عنوان مدیر حافظه استفاده کنه. از این نمونه های بهینه نبودن کدهای VCL در کد VCL زیاد پیدا میشه، که یک بخش اون به خاطر همون تفاوت در اولویت بندی های برنامه نویسان مختلف هست، که در بالا بهش اشاره کردم.

M_Maskout
پنج شنبه 11 فروردین 1390, 07:48 صبح
شما اون تابع رو باید در خود تابع trace کنید! وانگهی شما برنامه می نویسید که یه کاری براتون انجام بده یا می نویسید که traceش کنید؟!
بنده که برنامه می‌نویسم که traceش کنم؛ شما رو نمی‌دونم. اصولاً هم برنامه رو نمی‌نویسن تا کاری برای برنامه نویس انجام بده، می‌نویسن تا کاری برای کاربرا (مشتریا) انجام بده. متأسفانه خیلی از دوستان برنامه نویس وقتی تو برنامه‌هاشون خطایی می‌بینن، اون رو فقط یک بار هم trace نمی‌کنن، و نتیجه‌اش هم همین وضعیت کیفی نرم افزارهای موجود بازاره!


و هنوز هم نگفته اید اون چند هزار خط رو چند سال طول می کشه تا trace کنید؟ :دی
دوست عزیز بنده تو پست #13 گفتم 100 خط (صد)؛ عدد 100 از یک 1 و دوتا 0 در سمت راست اون ساخته می‌شه و از نظر مقداری کمتر از 1000 هست؛ هزار از یک 1 و سه‌تا 0 در سمت راست اون درست می‌شه. اعداد بعدی، 101، 102، 103، 104 و ... هست. همونطور که مشاهده می‌شه تا هزار هنوز کلی فاصله هست.

a_mosavian
پنج شنبه 11 فروردین 1390, 18:35 عصر
بنده که برنامه می‌نویسم که traceش کنم؛ شما رو نمی‌دونم. اصولاً هم برنامه رو نمی‌نویسن تا کاری برای برنامه نویس انجام بده، می‌نویسن تا کاری برای کاربرا (مشتریا) انجام بده. متأسفانه خیلی از دوستان برنامه نویس وقتی تو برنامه‌هاشون خطایی می‌بینن، اون رو فقط یک بار هم trace نمی‌کنن، و نتیجه‌اش هم همین وضعیت کیفی نرم افزارهای موجود بازاره!
منم نگفتم trace نکنید! trace در سطح روتین انجام میشه! اگر درباره کارکرد روتین Dateofweek شک دارید باید اون رو trace کنید. trace در سطح روتین هم راحتتر هست هم در آینده که از اون روتین دوباره بهره گرفتید خیالتون راحت هست. هنگامی که از کارکرد یک روتین مطمئن شدید دیگه trace نمی کنید. شما گویا اصلا نخوندید من چه گفته ام. من هم فرض رو بر این گذاشتم که کد هاتون را در متدها یا متدهای کلاس می نویسید و هر متد هم تنها و تنها یک کار را انجام می دهد.


دوست عزیز بنده تو پست #13 گفتم 100 خط (صد)؛ عدد 100 از یک 1 و دوتا 0 در سمت راست اون ساخته می‌شه و از نظر مقداری کمتر از 1000 هست؛ هزار از یک 1 و سه‌تا 0 در سمت راست اون درست می‌شه. اعداد بعدی، 101، 102، 103، 104 و ... هست. همونطور که مشاهده می‌شه تا هزار هنوز کلی فاصله هست.
با هم نخوانده اید پاسخ مرا. کد معادل 150 خط کد بهینه در سبک نوشتاری شما به هزار خط خواهد رسید. (چرا؟)

a_mosavian
پنج شنبه 11 فروردین 1390, 19:38 عصر
ظاهرا مطالب را به درستی مطالعه نمی کنید؛ اولا جایی گفته نشد که در مثال اول شما، حافظه بیشتری استفاده میشه، بلکه حافظه بیشتر برای مثال دوم بود، که شما یا دقت نکردید، یا چیز دیگه، و برای من مثال از مصرف حافظه مثال اول ارائه کردید، که کلا بی معنی هست.
ثانیا، مدعی شدید که کد اسمبلی بیشتری تولید میشه، اما احتمالا متوجه نیستید که چه کدی تولید شده، اون مجموعه کد به خاطر وجود دو بلوک از کد در دستور if-else ایجاد شده، اما در هر بار اجرا فقط یکی از اون بلوک ها اجرا خواهد شد، پس از نظر اجرا، تفاوت تعداد کدهای اجرا شده، یک دستور هست.
ثالثا، مدعی شدید که در VCL از کد مدنظر شما استفاده شده؛ باز هم دقت نکردید که اساسا بهینه سازی یعنی چی! برای شما توضیح داده شد که در بهینه سازی کد، معمولا بین افزایش سرعت و مصرف بهینه حافظه نسبت عکس وجود داره، و برنامه نویس بر اساس اولویت های خودش تصمیم میگیره که چطور تعادل مورد نظرش را در کد حفظ کنه. در یک سخت افزار با حافظه بسیار محدود، برنامه نویس وزنه استفاده بهینه از حافظه رو سنگین تر میکنه، و در یک سخت افزار دیگه، وزنه افزایش سرعت به قیمت افزایش مصرف حافظه رو. کد VCL برای سیستم عامل ویندوز نوشته شده، و حداقل نیازهای یک سیستم PC برای اجرای ویندوز، و محدودیت های تخصیص حافظه در ویندوز مشخص هست. برنامه نویس با توجه به واقعیات بهینه سازی میکنه، نه تخیلات. در اون کد VCL، برنامه نویس تشخیص داده با توجه به نوع و تعداد حالات ممکنه (که بسیار محدود هست)، سربار اضافی حافظه بسیار ناچیز خواهد بود، اما در ازاء آن، کارایی کد در حد مناسبی از نظر سرعت افزایش پیدا خواهد کرد، پس به این نتیجه رسیده که این بهینه سازی ارزشمند هست. بهینه سازی با تحلیل درست کد و شرایط اجرای کد حاصل میشه، و در شرایط مختلف باید بهینه سازی های مختلفی صورت بگیره. وقتی این اصول اولیه رو نمیدونید، به بهانه تولید کد بهینه شده، معمولا هم باعث تولید کد ناخوانا میشید، و هم در بعضی از مواقع، همون بهینه سازی بی مورد میتونه خودش موجب بروز مشکلات دیگه در کد بشه.

در ضمن، این رو هم به عنوان یک نکته اضافی به خاطر داشته باشید که کد VCL لزوما بهینه ترین کد نیست، بلکه در بسیاری از مواقع میشه کدهای بهینه تر از کد VCL پیدا کرد. ساده ترین مصداقش همون مدیر حافظه VCL بود که نسبت به FastMM در حد لاک پشت بود، و نهایتا بعد از سال ها، و فشار کاربران، بورلند قبول کرد اون کد بی خاصیت رو کنار بزاره، و از FastMM به عنوان مدیر حافظه استفاده کنه. از این نمونه های بهینه نبودن کدهای VCL در کد VCL زیاد پیدا میشه، که یک بخش اون به خاطر همون تفاوت در اولویت بندی های برنامه نویسان مختلف هست، که در بالا بهش اشاره کردم.
بقیه فرمایش شما متین است. همین 2 مورد را پاسخ می دهم.
من فرمایش شما را خوانده ام که برای مثال دوم فرموده اید. پاسخ همان را دادم. به گمانم در جریان بودید دیگر از اشاره دوباره به اینکه درباره چه دارم صحبت می کنم خودداری کردم. اگر باعث شده ام گمان اشتباه برید پوزش می خواهم.
مقایسه این مورد ساده که هر دو مورد کد شناخته شده است با مبحث FastMM که خود یک ماژول است کار صحیحی نیست. ممکن کدی بهینه تر از کدهای ما نیز باشد. صحبت آنها نیست. صحبت سر همین دو تاست که قطعا هر دو از نظر نویسندگان VCL شناخته شده است.
معمولا علت اینکه بورلند در عوض کردن کدهای کند با کدهای سریعتر محافظه کاری بخرج می دهد، ناسازگاری کدهای نوین با سیستم های قدیمی است. مثالی دیگر هم در تایید این فرمایش شما، استفاده دلفی از سیستم قدیمی و ناکارآمدتر GDI و GDI+ در برابر Direct2D ست. اما چون استفاده از D2D مشکل ناسازگاری با ویندوز xp و کامپیوترهای قدیمی دارد، Codegear از این سیستم فوق العاده سریعتر که بار پردازنده مرکزی را هم کم می کند چشم پوشیده!
حتی جالبتر اینکه دلفی در محاسبات اعشاری، هنوز دستورات FPU را به SSE ترجیح می دهد! و صد البته دستورات SSE که امروزه در بیشتر کامپیوترها وجود دارد، چندین برابر سریعتر دستورات ماقبل تاریخ 8087 است.

Felony
پنج شنبه 11 فروردین 1390, 21:09 عصر
استفاده دلفی از سیستم قدیمی و ناکارآمدتر GDI و GDI+ در برابر Direct2D ست. اما چون استفاده از D2D مشکل ناسازگاری با ویندوز xp و کامپیوترهای قدیمی دارد، Codegear از این سیستم فوق العاده سریعتر که بار پردازنده مرکزی را هم کم می کند چشم پوشیده!
کجا چشم پویشده ؟ منظورتون استفاده داخلی خود دلفی در VCL هست یا ... ؟ کتابخانه Direct2D همراه با دلفی 2010 برای دلفی عرضه شده .

vcldeveloper
جمعه 12 فروردین 1390, 00:58 صبح
مثالی دیگر هم در تایید این فرمایش شما، استفاده دلفی از سیستم قدیمی و ناکارآمدتر GDI و GDI+ در برابر Direct2D ست. اما چون استفاده از D2D مشکل ناسازگاری با ویندوز xp و کامپیوترهای قدیمی دارد، Codegear از این سیستم فوق العاده سریعتر که بار پردازنده مرکزی را هم کم می کند چشم پوشیده!
Direct2D تازه با ویندوز 7 عرضه شده؛ خودِ مایکروسافت هم روی ساخت کنترل های رابط کاربر با استفاده از آن سرمایه گزاری چندانی نکرده، چه برسه به VCL که از سال 1995 تا الان داره استفاده میشه. همچنان کنترل های استاندارد ویندوز که توسط SDK ویندوز ارائه میشند، از همون GDI برای رسم استفاده می کنند.


حتی جالبتر اینکه دلفی در محاسبات اعشاری، هنوز دستورات FPU را به SSE ترجیح می دهد! و صد البته دستورات SSE که امروزه در بیشتر کامپیوترها وجود دارد، چندین برابر سریعتر دستورات ماقبل تاریخ 8087 است.
این یکی به خاطر محافظه کاری نیست، بلکه به خاطر بی عرضگی هست! در واقع به خاطر سیاست های غلط پی در پی بورلند در گذشته، و جدا شدن برنامه نویسان اصلی کامپایلر دلفی در دوره های زمانی مختلف از این شرکت، و سرعت فعلی قابل قبول کامپایلر دلفی، شرکت بورلند و بعد از اون Embarcadero علاقه چندانی به سرمایه گزاری جدی روی بهینه سازی کد تولید شده کامپایلر از خودشان نشان ندادند. تازه الان مدتی هست که حرف بهینه سازی اساسی در کامپایلر رو میزنند، و معلوم نیست که این تغییرات اساسی که ازش حرف میزنند، کی قرار هست عرضه بشه، و نتیجه اش چه خواهد شد.


مقایسه این مورد ساده که هر دو مورد کد شناخته شده است با مبحث FastMM که خود یک ماژول است کار صحیحی نیست. ممکن کدی بهینه تر از کدهای ما نیز باشد. صحبت آنها نیست. صحبت سر همین دو تاست که قطعا هر دو از نظر نویسندگان VCL شناخته شده است.
FastMM یک مثال بارز بود، در حد پایین تر مثال ها متعدد هستند، مثل کارایی پایین متد TDataset.FieldByName یا کارایی پایین ClientDataset در انجام برخی عملیات های خاص، و غیره، که هر از چند گاهی تکنیک هایی برای دور زدن این کدهای ناکارآمد توسط سایر برنامه نویسان به عنوان روش های جایگزین ارائه میشه.

SAASTN
شنبه 13 فروردین 1390, 11:18 صبح
عذر می خوام که مطلب رو ادامه می دم، به نظر می رسه که بحث از نظر دوستان تمام شده است ولی هنوز چند تا نکته گنگ برا من باقی مونده. خوب من خودم اینطوری کد می نویسم، چون باهاش راحتترم و خیلی به بهینه تر بودنش از نظر پیچیدگی های زمانی و حافظه ای کاری نداشتم. که صحبتهای دوستان خیلی برام مفید بود. اما یه سری ایرادات گرفته شده که به نظرم قابل قبول نیست:

ضمناً کدهای کوتاهتر، الزاماً خواناتر نیستن. مخصوصاً وقتی که بخوایم اونا رو trace کنیم. و مخصوصاً بیشتر وقتی بخوایم بعدها اونا رو trace کنیم.

در نظر بگرین در زمان trace شما مجبورین مدام مقدار DayOfTheWeek(Date) رو کنترل کنین (تازه در این مورد خاص اصلاً این مقدار قابل کنترل نیست چون مقدار خروجی توابع رو نمی‌شه در زمان trace دید)
من از مجموع این دو گفته به این نتیجه می رسم که دوستمون با ابزارهای Debug دلفی آشنایی کافی ندارند، والا من تفاوتی در Trace کردن این دو سبک کد نمی بینم.

در مورد مثال دومی: در نظر بگیرین یه برنامه داریم که فاصله قسمت const از begin مثلاً 100 یا 150 خطه، اون وقت واسه دیدن مقادیر آرایه باید چی کار کرد (هی برو بالا، هی بیا پایین)
باز هم من شما رو به عدم شناخت امکانات IDE متهم می کنم. شما وقتی با یه کد چند هزار خطی روبرو هستی (البته من فرق صد و چند هزار رو به خوبی می دونما:لبخند:) لاجرم باید بتونی توش حرکت کنی، حالا چه برای دیدن مقادیر اون آرایه یا فقط برای دیدن مقادیر یه Enumerative ساده. اگه یکبار دیگه Shortcut های دلفی رو مطالعه کنید متوجه میشید که اون فاصله چند صد خطی رو چطور با فشار چند دکمه بارها و بارها طی کنید.
به عقیده بنده اینها مشکلات یه سبک کد نویسی نیست.

وقتی این اصول اولیه رو نمیدونید، به بهانه تولید کد بهینه شده، معمولا هم باعث تولید کد ناخوانا میشید، و هم در بعضی از مواقع، همون بهینه سازی بی مورد میتونه خودش موجب بروز مشکلات دیگه در کد بشه.
اینجا هم آقای کشاورز به این نوع کد نویسی صفت "معمولا نا خوانا" دادند که به عقیده من درسته: من در محیط های واقعی این موضوع رو درک کردم. فقط باید یک نکته رو مد نظر قرار بدیم و اونم اینه که یه سری از عوامل ذاتا باعث ناخوانایی می شن مثل استفاده از اسامی بی معنی و عدم رعایت فاصله گذاری و توضیحات و ... اما یکسری از ناخوانایی ها نسبی هستند و بستگی به میزان آشنایی ما به اون روش کد نویسی داره. به عنوان مثال عرض می کنم که سبک نگارش فارسی جناب موسویان برای من نا خوانا به حساب میاد چون کل سیستم ذهنی و پزدازشی من خیلی عادت به رمزگشایی همچین سبکی نداره. اما در مقابل سبک کد نویسی که ایشون ارائه کردن از نظر من کاملا خواناست چون خودم چندین ساله اینجوری کد می نویسم و کاملا برام بدیهیه، و راستش وقتی دیدم تحت عنوان آموزش مطرح شده و اینقدر هم مورد استقبال قرار گرفته یکم تعجب کردم.
حالا مسئله اینجاست که بله: همکاران ما با این نوع کد نویسی آشنایی چندانی ندارند و شاید کدی که ما با این سبک تولید می کنیم براشون قدری ناخوانا باشه، اما سوال اینجاست که حالا ایراد از سبک هست یا از همکاران ما؟

vcldeveloper
یک شنبه 14 فروردین 1390, 00:05 صبح
اینجا هم آقای کشاورز به این نوع کد نویسی صفت "معمولا نا خوانا" دادند که به عقیده من درسته: من در محیط های واقعی این موضوع رو درک کردم. فقط باید یک نکته رو مد نظر قرار بدیم و اونم اینه که یه سری از عوامل ذاتا باعث ناخوانایی می شن مثل استفاده از اسامی بی معنی و عدم رعایت فاصله گذاری و توضیحات و ... اما یکسری از ناخوانایی ها نسبی هستند و بستگی به میزان آشنایی ما به اون روش کد نویسی داره. من بحثم مستقیما به آموزش ایشون برنمیگرده. در هر حال، مثال هایی که مطرح کردند، نوعی بهینه سازی محسوب میشه. بحث من حول این محور بود که اصولا بهینه سازی باید با آگاهی صورت بگیره، و برنامه نویس بدونه که چی رو برای چه کاری داره بهینه میکنه. بعضی بهینه سازی ها کلا جنبه منفی ندارند، و میشه از برنامه نویس خواست که اون ها را به عنوان راهکارهای مناسب همیشه به کار بگیره، حتی اگر نمیدونه دقیقا منظور از اونها چی هست. مثلا ما میگیم اگر string یا Interface یا Dynamic Array به عنوان پارامتری به تابعی ارسال میشه، ولی در اون تابع هیچ تغییری در محتوای آن داده نمیشه، برای افزایش کارایی، و کاهش کد ماشین تولید شده، این داده را بصورت پارامتر const تعریف کنید. حالا اگر برنامه نویس یک string ایی که در داخل تابع تغییر داده میشه رو const تعریف کنه، کلا کدش کامپایل نمیشه، و مجبور میشه که const رو برداره. اگر هم string مربوطه تغییر داده نمیشه، تعریف به صورت const هیچ جنبه منفی براش نداره. پس حتی اگر ندونه که اینجا const دقیقا چیکار میکنه، مشکلی پیش نمیاد، و این عملکرد همیشه مثبت هست. حالا یک بار شما میاید میگید به جای فلان ساختار case یا if - else، از یک آرایه استفاده کنید. خب این در شرایط مختلف تبعات مختلفی داره. همه برنامه نویسان نباید از اون استفاده کنند، بلکه باید با توجه به کدشان، و اولویت هایشان تصمیم بگیرند که آیا این بهینه سازی برایشان مناسب هست، یا نه؟ اینجا دیگه حفظ کردن یک قانون کارایی نداره، چون ممکنه برنامه نویس در شرایطی از همچین پیشنهادی استفاده کنه این کار براش بیشتر جنبه منفی داشته باشه، تا جنبه مثبت.

پست های من هم با این نیت ارسال شدند که فردا یکی نره هر چی ساختار if-else و case توی کدش بود، تغییر بده، بگه حتما اینطوری برنامه ام بهتر میشه؛ بلکه متوجه باشه که بهینه سازی امر ظریفی هست، و باید در خیلی از موارد بفهمه که چرا داره یک کد خاص رو تغییر میده، و با تاثیرات این تغییر آشنا باشه، و با توجه به میزان وقتی که میزاره، و میزان بهبودی که از بهینه سازی حاصل میشه، و نوع کدی که نوشته میشه؛ تصمیم بگیره که آیا در شرایط فعلی اش این بهینه سازی برایش ارزشمند هست یا نه؟

M_Maskout
یک شنبه 14 فروردین 1390, 09:34 صبح
عذر می خوام که مطلب رو ادامه می دم، به نظر می رسه که بحث از نظر دوستان تمام شده است
سلام
اتفاقاً خیلی خوب شد که شما مطلب رو ادامه دادی، چون اصولاً بحث جالبیه و جای کار داره، شاید هم بهتر بود خود جناب موسویان مطلب رو ادامه می‌دادن (البته اگه سوء برداشت نشه، با یه کم متن روان‌تر).
مثال‌هایی که جناب موسویان اوردن، جالب و درسته. من هم مثل شما چندین سال هست که دارم با همین روش کد می‌نویسم (از زمان پاسکال!). اتفاقاً به خاطر تجربه‌ی شخصی هم بود که گفتم "الزاماً خواناتر نیست ...".
البته بنده یه مدعی در دلفی (یا پاسکال) نیستم، ولی راجع به دستورات شرطی به سبک گفته شده، در نظر بگیرید زمانی رو که دارین برنامه رو خط به خط اجرا می‌کنین و دلیلش هم اینه که، اون جوری که تصورتون هست، کار انجام نمی‌شه. و اتفاقاً به خاطر دستور شرطی‌ای هست که به صورت خطی نوشته شده. این همونجایی هست که شما مجبورین مثلاً مقدار متغیر X رو ببینید و متوجه بشین که، مقدار قابل پیش بینی توی اون هست یا نه. اصولاً شاید این کار تو برنامه 100 (یا هزار) خطی با یکی دو تا ماژول مشکلی ایجاد نکنه. ولی تو پروژه‌های بزرگ که به ذاته گیج کننده‌س، لااقل به نظر من که یه جورایی مشکل ساز هست. البته ذکر این نکته حتماً لازمه که مثال اورده شده، قطعاً چنین مشکلی ایجاد نمی‌کنه چون نتیجه‌ش کاملاً بصریه.
در مورد مثال دوم هم، در نظر بیارین که بخواین المنتی که حاصل چند محاسبه (در شرایط بد، تو در تو هم باشه) به اون اشاره می‌کنه رو داشته باشین. مثلا:

Result := A[Func1(a + b) - Func2(c) + Byte(d=5)];

و اتفاقاً خروجی Func1 (که مثلاً تابعی باشه که به سورس اون دسترسی نداریم)، مقدار مورد انتظار رو نداره، آیا trace این خط راحت انجام می‌شه؟
به هر حال، همونطور که در ابتدای پست گفتم، بحث تاپیک، بحث جالبیه و من که از اون و پستایی که توش زده شده استفاده کردم. چه اینکه آقای استاد کشاورز هم تو پست #22 و البته پست‌های قبلی‌شون تأکید کردن که با این روش، بهینه سازی انجام می‌شه.
البته آقای مارکو کانتو هم تو فصل دوم کتاب Mastring Delphi نکته‌ای نوشتن که به نظر من، مضمون تاپیک رو تأکید می‌کنه:


The IfThen function is similar to the ?: operator of the C/C++ language. I find it handy because you can replace a complete if/then/else statement with a much shorter expression, writing less code and often declaring fewer temporary variables.

"تابع IfThen شبیه عملگر :? در زبان ++C/C هست. من آنرا سودمند می‌دانم زیرا شما می‌توانید یک عبارت کامل if/then/else را با عبارت بسیار کوتاهتری جایگزین کنید، نوشتن کد کمتر و تعریف متغیر‌های کمتر"

SAASTN
یک شنبه 14 فروردین 1390, 16:30 عصر
من بحثم مستقیما به آموزش ایشون برنمیگرده. در هر حال، مثال هایی که مطرح کردند، نوعی بهینه سازی محسوب میشه. بحث من حول این محور بود که اصولا بهینه سازی باید با آگاهی صورت بگیره، و برنامه نویس بدونه که چی رو برای چه کاری داره بهینه میکنه.
من کل فرمایشات شما رو تایید می کنم، مسئله من اصلا روی این موارد نبود و صحبتهای من فقط در مورد نا خوانایی کد بخاطر استفاده از چنین روش هایی بود. من می گم همه برنامه نویسا باید به تمام امکانات سینتکسی که باهاش کار می کنن آشنا باشن. و الا اینکه ما باید از هر ابزاری آگاهانه استفاده کنیم کاملا بدیهیه.

البته بنده یه مدعی در دلفی (یا پاسکال) نیستم، ولی راجع به دستورات شرطی به سبک گفته شده، در نظر بگیرید زمانی رو که دارین برنامه رو خط به خط اجرا می‌کنین و دلیلش هم اینه که، اون جوری که تصورتون هست، کار انجام نمی‌شه. و اتفاقاً به خاطر دستور شرطی‌ای هست که به صورت خطی نوشته شده. این همونجایی هست که شما مجبورین مثلاً مقدار متغیر X رو ببینید و متوجه بشین که، مقدار قابل پیش بینی توی اون هست یا نه. اصولاً شاید این کار تو برنامه 100 (یا هزار) خطی با یکی دو تا ماژول مشکلی ایجاد نکنه. ولی تو پروژه‌های بزرگ که به ذاته گیج کننده‌س، لااقل به نظر من که یه جورایی مشکل ساز هست. البته ذکر این نکته حتماً لازمه که مثال اورده شده، قطعاً چنین مشکلی ایجاد نمی‌کنه چون نتیجه‌ش کاملاً بصریه.
من متوجه فرمایشات شما هستم، اما باز هم تصور می کنم که امکاناتی که دلفی برای دیباگ در اختیار گذاشته امکان هر جور خطاگیری رو به ما می ده. اما همونطور که تولید یه الگوریتم هزار خطی به مراتب پیچیده تر از تولید یه الگوریتم بیست خطیه خوب دیباگ کردنش هم سخت تر هست، و اساسا دیباگینگ هم بخشی از برنامه نویسیه و فنون مربوط به خوش رو داره. بهتره روی مثالی که آوردید صحبت کنیم:
ببینید اگه خروجی Func1 با مقدار ورودی صحیح، مورد انتظار ما نباشه که دیگه تقصیر ما یا اون سبک کد نویسی نیست. اما گه منظورتون اینه که خروجی تابعی که درش هستیم مورد انتظار نیست و ما می خوایم بدونیم که آیا عبارت:

Func1(a + b) - Func2(c) + Byte(d=5)
درست محاسبه میشه یا نه و به تبع اون می خوایم مقادیر زیر عبارتها مثل Func1(a+b) رو بدست بیاریم، اونوقت می گم بله Trace به راحتی انجام میشه، باید همینجا عبارت مدنظر رو انتخاب کنیم و با یه Ctrl+F7 و با استفاده از پنجره Evaluate مقدار مدنظر رو بررسی کنیم. Byte(d=5) که از قبلی هم راحتتره، انتخابش می کنیم و موس رو روش نگه میداریم یا یه Ctrl+F5 می زنیم تا یه Watch اضافه بشه. تازه اگه می خواستیم همین کد رو با case بنویسیم مگه تفاوتی در Trace می کرد؟ در اون صورت هم می خواستیم بنویسیم:
case Func1(a + b) - Func2(c) + Byte(d=5) of
0: Result := ...;
1: Result := ...;
...
end;

من که در این سطوح بی شک آرایه رو ترجیح می دم.


ضمنا تو تابعتون هم یه متغیر یا پارامتر به اسم a دارید هم یه ثابت به اسم A:بامزه:

M_Maskout
یک شنبه 14 فروردین 1390, 17:30 عصر
اگه خروجی Func1 با مقدار ورودی صحیح، مورد انتظار ما نباشه که دیگه تقصیر ما یا اون سبک کد نویسی نیست.
معلومه که تقصیر ما نیست. اما ممکنه به دلیل ایراد در روندی که پیاده کردیم این خروجی درست شده باشه، در زمان استفاده از ساختار case..of با زدن اولین F8، مکان نما به سطر مقدار محاسبه شده (در صورت وجود در موارد پیش بینی شده) پرش می‌کنه و اینجوری خیلی سریع مشخص می‌شه که مقدار محاسبه شده چقدر هست و ادامه کار ساده‌تر خواهد بود و ... . در هر حال بنده بازهم بر قید جمله‌ای که نوشتم تأکید می‌کنم "الزاماً ... این روش بهتر نیست" این یعنی اینکه به نظر من این سبک کد نویسی بهتره اما نه بطور تام.

...اما اگه منظورتون اینه که خروجی تابعی که درش هستیم مورد انتظار نیست و ما می خوایم بدونیم که آیا عبارت:

Func1(a + b) - Func2(c) + Byte(d=5)
درست محاسبه میشه یا نه و به تبع اون می خوایم مقادیر زیر عبارتها مثل Func1(a+b) رو بدست بیاریم، اونوقت می گم بله Trace به راحتی انجام میشه، باید همینجا عبارت مدنظر رو انتخاب کنیم و با یه Ctrl+F7 و با استفاده از پنجره Evaluate مقدار مدنظر رو بررسی کنیم.
سلام
شما مطمئنید که می‌شه تو پنجره Evaluate مقدار خروجی یه تابع (مثلاً (Func1(a+ b) رو می‌شه دید؟ اگه این امکان وجود داره، تو کدوم ورژن دلفی هست، آیا فقط زدن Ctrl-F7 کافیه یا باید بعدش کار دیگه‌ای کرد. البته همین سؤال رو در مورد پنجره Watch هم دارم. لطفاً بیشتر راهنمایی کنید.


ضمنا تو تابعتون هم یه متغیر یا پارامتر به اسم a دارید هم یه ثابت به اسم A:بامزه:
بله حق با شماست. اینم از اون اشتباهات معمول غیر حرفه‌ای هاست.

SAASTN
دوشنبه 15 فروردین 1390, 00:47 صبح
معلومه که تقصیر ما نیست. اما ممکنه به دلیل ایراد در روندی که پیاده کردیم این خروجی درست شده باشه، در زمان استفاده از ساختار case..of با زدن اولین F8، مکان نما به سطر مقدار محاسبه شده (در صورت وجود در موارد پیش بینی شده) پرش می‌کنه و اینجوری خیلی سریع مشخص می‌شه که مقدار محاسبه شده چقدر هست و ادامه کار ساده‌تر خواهد بود و ... .
ببینید الان شما در مرحله دیباگ هستید، یعنی متوجه شدید که خروجی تابعتون با مقداری که انتظار داشتید فرق می کنه. حالا یه عبارت داره محاسبه میشه که خودش ترکیبی از چند عبارت دیگست و محاسبه اشتباه ممنکه مربوط به هرکدوم از اون زیر عبارتها باشه، وقتی شما F8 رو میزنید کل محاسبات انجام میشه و Case بر اساس نتیجه نهایی شمارو به یکی از حالات هدایت می کنه، که به احتمال زیاد حالت اشتباهی هست و از قبل هم حدس زده بودیم که احتمالا تصمیم گیری اشتباهی صورت گرفته. پس با زدن F8 چیز زیادی دستگیرمون نمیشه، اینجا بهتره که قبل از محاسبه (چون ممکنه خود محاسبات تاثیری روی پارامترها داشته باشن و بعد از یکبار محاسبه شرایط محاسبه بعدی تغییر بکنه) با استفاده از ابزارهایی که خدمتتون عرض کردم هر زیر عبارت بطور جداگانه بررسی بشه تا محل خطا دقیقا مشخص بشه. اگر هم مقدار کل عبارت رو بخواید میتونید Evaluate رو روی کل عبارت انجام بدید. اما اگه فقط خود نتیجه نهایی مورد نظر شما بوده و فقط با یه F8 کارتون راه میافتاده خوب بله یکم کار سخت تر شده که من تصور می کنم قابل چشم پوشیه.

در هر حال بنده بازهم بر قید جمله‌ای که نوشتم تأکید می‌کنم "الزاماً ... این روش بهتر نیست" این یعنی اینکه به نظر من این سبک کد نویسی بهتره اما نه بطور تام.
کاملا موافقم و جای بحثی نیست.

شما مطمئنید که می‌شه تو پنجره Evaluate مقدار خروجی یه تابع (مثلاً (Func1(a+ b) رو می‌شه دید؟ اگه این امکان وجود داره، تو کدوم ورژن دلفی هست، آیا فقط زدن Ctrl-F7 کافیه یا باید بعدش کار دیگه‌ای کرد.
بله مطمئن هستم، و نیاز به انجام کار دیگه ای هم نیست. شما عبارت مدنظرتون رو انتخاب کنید و همون ترکیب رو فشار بدید، یا RightClick و از داخل Debug زیر منوی Evaluate\Modify رو کلیک کنید. حالا ورژنشو دیگه نمی دونم، بچگیامون زمان 4 و 5 از این قرتی بازیا بلد نبودیم ولی تو 7 و 8 به اینور که بود.

البته همین سؤال رو در مورد پنجره Watch هم دارم. لطفاً بیشتر راهنمایی کنید.
ببینید وقتی عبارتی که می خواید تحت نظر بگیرید درش تابعی وجود نداره و فقط از متغیر ها و ثابت ها و عملگرها استفاده شده و حداکثر Typecast روی اونها اتفاق افتاده می تونید از Watch استفاده کنید که بعد از هر بار جلو رفتن در کد بصورت اتوماتیک مقادیر جدید رو محاسبه میکنه و نمایش میده، اما اگه از توابع هم استفاده می کنید باید از همون پنجره Evalute\Modify استفاده کنید، که البته خودش اتوماتیک محاسبات رو بعد از جلو رفتن انجام نمیده و مثلا هربار که F8 یا F9 رو می زنید و دوباره مقدار رو می خواید باید دوباره دکمه Evaluate رو بزنید. این پنجره رفتار جالبی داره، وقتی روی یه خط ایستادید و مثلا مقدار متغیر a در اون خط 3 هست، اگه داخل این پنجره تایپ کنید:

a := 5;
قبل از این که هر عملی داخل کد اصلی روی متغیر a انجام بشه مقدار اون به 5 تغییر می کنه، در واقع این پنجره هر کدی رو که داخلش بنویسید اجرا می کنه و اگه اون کد خروجی ای داشته باشه نمایش میده.

کلا تمام گزینه های مربوط به همون منوی Debug که خدمتتون عرض کردم رو یه بررسی بکنید و دربارشون توی راهنمای خود دلفی مطالعه بکنید.
اگرم باز جایی برای بحث در این مورد باشه می تونیم توی یه تاپیک دیگه بهش بپردازیم چون فکر می کنم یکم داریم از موضوع این تاپیک فاصله می گیریم.

a_mosavian
دوشنبه 15 فروردین 1390, 01:06 صبح
مثل کارایی پایین متد TDataset.FieldByName یا کارایی پایین ClientDataset در انجام برخی عملیات های خاص، و غیره، که هر از چند گاهی تکنیک هایی برای دور زدن این کدهای ناکارآمد توسط سایر برنامه نویسان به عنوان روش های جایگزین ارائه میشه.

من خودم متد FieldByName و متد Lookup رو با بهره گیری از سیستم جدید class helper جایگزین کردم. متد بهینه FieldByName که اخیرا بحثش تو بلاگها اومده را که کپی کردم! برای Lookup هم متدی نوشتم که از SQL استفاده کنه تا پاسخ رو تولید کنه. اینجوری نیاز به کشیدن بار اضافی از حافظه نیست. حق با شماست. این دو تا متد هم مال ما قبل تاریخند! البته این متد Lookup جدید رو برای کوئری بر پایه چند فیلد پیاده نکردم چون نیاز نداشتم.

a_mosavian
دوشنبه 15 فروردین 1390, 01:26 صبح
پست های من هم با این نیت ارسال شدند که فردا یکی نره هر چی ساختار if-else و case توی کدش بود، تغییر بده، بگه حتما اینطوری برنامه ام بهتر میشه؛ بلکه متوجه باشه که بهینه سازی امر ظریفی هست، و باید در خیلی از موارد بفهمه که چرا داره یک کد خاص رو تغییر میده، و با تاثیرات این تغییر آشنا باشه، و با توجه به میزان وقتی که میزاره، و میزان بهبودی که از بهینه سازی حاصل میشه، و نوع کدی که نوشته میشه؛ تصمیم بگیره که آیا در شرایط فعلی اش این بهینه سازی برایش ارزشمند هست یا نه؟
شکی نیست که راحتی برنامه نویس شرط هست. اما اگر بخوایم همون جایی که هستیم در جا بزنیم پیشرفتی پدید نمیاد. قطعا نگهداری و بهینه کردن کد های قدیمی بزرگترین دردسر هر برنامه نویسی هست.


باز هم من شما رو به عدم شناخت امکانات IDE متهم می کنم. شما وقتی با یه کد چند هزار خطی روبرو هستی (البته من فرق صد و چند هزار رو به خوبی می دونما:لبخند:) لاجرم باید بتونی توش حرکت کنی، حالا چه برای دیدن مقادیر اون آرایه یا فقط برای دیدن مقادیر یه Enumerative ساده. اگه یکبار دیگه Shortcut های دلفی رو مطالعه کنید متوجه میشید که اون فاصله چند صد خطی رو چطور با فشار چند دکمه بارها و بارها طی کنید.
گویا من بد توضیح داده بودم! توضیح شما کاملا واضح بود. امیدوارم دوستان کار محیط های IDE را با توجه به اینکه امروزه اکثرشون امکانات شبیه به هم و یکسانی ارائه می کنند بخوبی فرا گیرند.


اما در مقابل سبک کد نویسی که ایشون ارائه کردن از نظر من کاملا خواناست چون خودم چندین ساله اینجوری کد می نویسم و کاملا برام بدیهیه، و راستش وقتی دیدم تحت عنوان آموزش مطرح شده و اینقدر هم مورد استقبال قرار گرفته یکم تعجب کردم.
منم به اندازه شما تعجب کردم! ولی خوشحالم بحث های بسیار جالبی مطرح شد. گمان کنم زدن یک تاپیک درباره این که چگونه باید کد بنویسیم هم نیاز باشه. فرصت کنم می زنم!

a_mosavian
دوشنبه 15 فروردین 1390, 01:35 صبح
کجا چشم پویشده ؟ منظورتون استفاده داخلی خود دلفی در VCL هست یا ... ؟ کتابخانه Direct2D همراه با دلفی 2010 برای دلفی عرضه شده .
بلی دقیقا. کتابخانه اصلا با VCL مجتمع نیست و اگر در جایی امکان بهره گیری از Direct2D فراهم باشد از آن استفاده نمی کند. گرچه خود همانی که در دلفی 2010 پیاده شده پیشرفت بزرگیست اما برنامه نویس را ناچار می کند حجم عظیمی کد برای استفاده از آن در ترسیم اشیا VCL پیاده سازی کند. صرف حضور کتابخانه کافی نیست.
مایکروسافت هم اکنون در تکنولوژی WPF نهایت استفاده را از آن می برد. اگر در دسترس باشد از آن بهره می گیرد. واقعا تکنولوژی جالبی است.

vcldeveloper
دوشنبه 15 فروردین 1390, 19:13 عصر
مایکروسافت هم اکنون در تکنولوژی WPF نهایت استفاده را از آن می برد.
WPF از Direct2D استفاده نمیکنه، بلکه Direct2D و WPF هر دو یک Thin Wrapper برای Direct 3D هستند، و از هم مستقل هستند. Direct2D برای کدهای Native ساخته شده و سطح پایین تر از WPF هست.