چطور مثل یک حرفه‌ای برنامه بنویسیم – بخش دوم: حوزه‌ی متغیرها

آوریل 9, 2010

در پست قبلی در مورد نحوه‌ی افزایش خوانایی برنامه با انتخاب نام مناسب برای متغیرها و تابع‌ها توضیح دادم. این موضوع در کلاس‌ها آکادمیک تدریس می‌شود و اکثر افراد با این موضوع آشنا هستند (هرچند که هنگام برنامه نوشتن این موضوع را رعایت نمی‌کنند). در این پست قصد دارم بیش‌تر در مورد متغیرها توضیح دهم. مطالبی که در این پست توضیح خواهم داد را معمولا نمی‌توان در بین مباحثی که در کلاس‌های آکادمیک تدریس می‌شود پیدا کرد. پس این پست را با دقت بخوانید.

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

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

این چند حالت را در نظر بگیرید. (این‌ها لزوما تمام حالت‌های ممکن نیستند)

حالت اول:‌ متغیر با طول عمر طولانی و گام‌های کوچک

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


String str;
str = getUsername();
// do something with username
// ...
str = "SELECT * FROM TABLE FOO";
// do something with the query
// ...
str = getErrorMessage();
// show error message on the screen
// ...

در تمام این کد از متغیر str به صورت مداوم استفاده شده است، ولی استفاده از یک متغیر به این صورت یکی از خطرناک‌ترین و آماتوری‌ترین روش‌های ممکن برای کد نویسی است. اگر جایی دیدید که یک برنامه‌نویس از یک متغیر به این صورت استفاده کرده است، بنا به دلایل زیر، مطمئن باشید که حتی الفبای برنامه‌نویسی را هم نمی‌داند.

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

حالت دوم: متغیر با طول عمر طولانی و گام‌های متوسط

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

حالت سوم: متغیر با طول عمر زیاد و گام‌های بلند

در این حالت متغیر دارای عمری طولانی است. ولی بعد از أنکه متغیر تعریف شد، مدت‌ها بدون استفاده باقی مانده است. در این مثال متغیر بعد از آن‌که در دستور اول تعریف شد تا دستور ششم دیگر مورد استفاده قرار نمی‌گیرد. بعد دوباره از این متغیر استفاده‌ای نمی‌شود تا آخرین دستور. این حالت یکی از بدترین حالت‌هایی است که ممکن است به وجود آید. زیرا از یک طرف فاصله‌ی محل تعریف و محل استفاده‌ی متغیر طولانی است و کسی که در حال کار کردن روی کد است نمی‌تواند هنگام دیدن کدی که از متغیر استفاده می‌کند، جایی که متغیر تعریف شده است را هم ببیند. ثانیا از آن‌جا که این متغیر به ندرت مورد استفاده قرار گرفته است، دلیل تعریف و استفاده از این متغیر در خاطر برنامه‌نویس باقی نمی‌ماند. اگر این متغیر بیش‌تر مورد استفاده قرار می‌گرفت، این احتمال وجود داشت که برنامه‌نویس ساده‌تر به یاد بیاورد که این متغیر چه جیزی را در خود ذخیره می‌کند. به هر حال می‌توان با تعریف کردن متغیر در جایی نزدیک به اولین جایی که متغیر استفاده شده است تا حدی از این مشکل کاست.

حالت چهارم: متغیر با طول عمر طولانی، یک گام بلند و سپس گام‌های کوچک

در این مثال هم متغیر دارای عمر طولانی است، اما این متغیر فقط در بخش کوچکی از برنامه استفاده شده است (در چند دستور آخر). بین جایی که متغیر تعریف شده است و جایی که برای اولین بار از آن استفاده شده است یک فاصله‌ی بزرگ وجود دارد. با تعریف کردن متغیر در نزدیکی اولین جایی که از آن استفاده شده است می‌توان مشکل این حالت را برطرف کرد.

حالت پنجم:‌ متغیر با طول عمر کوتاه و گام‌های کوچک

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

حالا که توضیح طول عمر متغیرها و اندازه‌ی گام‌های آن‌ها تمام شد بهتر است به کدی که بحث روی آن بود برگردیم.

در کد برنامه‌ای که در پست قبلی مورد بحث بود و در انتهای این پست هم آمده است، یک متغیر به نام temp تعریف شده است (از بین تمام متغیرهای این برنامه به عمد این متغیر را انتخاب کرده‌ام چون temp یکی از بدترین اسم‌هایی است که می‌توان برای یک متغیر انتخاب کرد). این متغیر در ابتدای برنامه تعریف شده است و فقط و فقط در تابع compute که در انتهای برنامه قرار دارد استفاده شده است. به این ترتیب طول عمر این متغیر بسیار زیاد است. من در یک مانیتور ۱۹ اینچ واید نتوانستم همزمان وقتی که در حال خواندن کد تابع compute بودم، جایی که این متغیر تعریف شده بود را هم ببینم. برای دیدن جایی که این متغیر تعریف شده است و توضیحاتی که در مورد این متغیر نوشته شده است مجبور شدم که چند بار کد را اسکرول کنم. این کار فقط باعث شد که رشته‌ی افکار من که روی تابع compute متمرکز شده بود گسسته شود و معطوف جست‌وجو در کد و پیدا کردن متغیر compute شود. لازم است دوباره این مطلب را یادآوری کنم که نوشتن و فهمیدن یک برنامه‌ی این‌چنینی به اندازه‌ی کافی کار پیچیده‌ای است و برهم زدن تمرکز برنامه‌نویسی که در حال کار کردن روی کد است، به هر دلیل، فقط این پیچیدگی را بیش‌تر می‌کند. شخصی که این کد را نوشته باید دلیل بسیار خوبی برای عمومی تعریف نکردن این متغیر داشته باشد زیرا از نظر من هیچ دلیلی وجود ندارد که این متغیر را به صورت محلی در تابع compute تعریف نکرد.

متاسفانه این مطلب در مورد سایر متغیرهای عمومی که در این برنامه تعریف شده‌اند نیز صدق می‌کند. تمام این متغیر‌ها فقط و فقط در بخش کوچک از کد (هر کدام فقط در یک تابع) استفاده شده‌اند.

در صورتی که متغیر sub در برنامه‌ی زیر در کنار جایی که از آن استفاده شده است (داخل تابع compute) تعریف شده بود، برنامه‌نویس اصلی این کد احتمالا خیلی ساده‌تر می‌توانست ببیند که sub یک ساختمان داده است که روی آن فقط سه عمل contains‌ و add و clear انجام می‌شود و شاید برای آن‌که کارآیی کد خود را بهتر کند به جای آنکه sub را از نوع ArrayList تعریف می‌کرد، آن‌را از نوع HashSet تعریف می‌کرد تا دو عمل contains‌ و add با سرعت بسیار بیشتری (۱)o روی آن اجرا شوند.


پاورقی

برای تولید شکل‌ها از نرم‌افزار asymptote همراه با نرم‌افزار ImageMagick استفاده شده است.


کد اصلی

/*
/* this class implements the encoding requested in problem #1706
*/
/**
* @author Esmaeil Ashrafi <s.ashrafi@gmail.com>
*/
import java.util.ArrayList;
public class StierlitzCipher {
private static String input; // the string suppose to be ciphered
private static int k; // the length of sub strings
// temporary variable for query substrings
private static StringBuffer temp = new StringBuffer();
// an array to store substrings of each query string
private static ArrayList<String> sub = new ArrayList<String>();
/**
* @param args
* the command line arguments
*/
public static void main(final String[] args) {
/*
* if ran in command line console,first argument willl be the string to
* cipher and second considered as the key
*/
if (args.length == 2) {
input = new String(args[1]);
k = Integer.parseInt(args[0]);
} // otherwise use of default values of problem
else {
input = new String("abaccc");
k = 3;
// prompt the user to using default values
System.out.println("doing encrypt by default values:"
+ "\nString : \"abaccc\"\nKey : 3");
}
query(input, k);
System.out.println(); // just for trimming the output !
}// end of main
/**
* gets the entire string and splits it to substrings in length of "len" and
* sends each substring to method compute for calculating the number of
* different non-empty substrings could obtained from them.there will be
* substrings(and consequent call to compute method)call as much as the length
* of parameter str
*
* @param str
* - the string to be cipher
* @param len
* - the length of substrings(the key)
*/
private static void query(final String str, final int len) {
int m;
final int strLen = str.length();
for (m = 0; m <= strLen - len; m++) {
compute(str.substring(m, m + len));
}
for (m = m; m < strLen; m++) {
compute(str.substring(m).concat(str.substring(0, len - (strLen - m))));
}
}// end of query
/**
* queries queryString to calculate number of its different and non-empty
* substrings and prints the count to the standard output
*
* @param queryStr
* - the string to be queried
*/
private static void compute(final String queryStr) {
final int qLen = queryStr.length();
for (int i = 0; i < qLen; i++) {
for (int j = i + 1; j <= qLen; j++) {
if (!sub.contains(temp.replace(0, temp.length(),
queryStr.substring(i, j)).toString())) {
sub.add(temp.toString());
}
}
}
System.out.print(" " + sub.size());
sub.clear();
}// end of compute
}// end of class

چطور مثل یک حرفه‌ای برنامه بنویسیم – بخش اول: انتخاب نام برای متغیرها

آوریل 7, 2010

چندی پیش شخص از من خواست تا به کدی که نوشته بود نگاهی بیندازم. این کد را در انتهای این پست آورده‌ام. این کد یک مساله‌ی دشوار را حل می‌کند. مطمئن هستم که بیشتر از ۹۰ درصد از برنامه‌نویس‌ها و دانشجویان رشته‌ی کامپیوتر قادر به حل کردن این مساله نیستند (اگر فکر می‌کنید که جز این ۹۰ درصد نیستید امتحان کنید). من قصد ندارم که در این مورد که این کد چقدر خوب یا بد این مساله را حل می‌کند نظری بدهم. فقط می‌خواهم چند سطر در مورد این موضوع که چطور می‌توان این کد را بهتر کرد توضیح بدهم.

نوشتن برنامه برای حل کردن مسایل دنیای واقعی با استفاده از کامپیوتر کار بسیار پیچیده‌ای است و برنامه‌نویس‌ها باید هر کاری می‌توانند انجام دهند تا این پیچیدگی کم شود. یکی از این پیچیدگی‌ها این است که در هر برنامه تعداد زیادی متغیر تعریف می‌شود. حتی اگر برنامه کوچک باشد هیچ‌کس به خاطر نمی‌آورد که متغیر x که در فلان سطر از فلان فایل استفاده شده است کجا تعریف شده است، چرا تعریف شده است، چه مقداری در آن ذخیره می‌شود، و …

برای این‌که پیچیدگی برنامه‌ها کم‌تر شود باید کاری کرد که افرادی که کد را می‌خوانند (چه به قصد فهمیدن، چه به قصد رفع اشکال، چه به قصد بهبود دادن) به سادگی بتوانند بفهند که هر متغیر به چه منظور تعریف شده و چه مقداری در أن ذخیره می‌شود.

در یکی از سطرهای این برنامه این دستور را می‌توانید پیدا کنید:

k = 3;

برای اینکه بفهمید این دستور چه کاری انجام می‌دهد باید بدانید که متغیر k برای چه منظوری تعریف شده است. برای اینکه بفهمید k برای چه منظوری تعریف شده است باید سطری که متغیر k در آن تعریف شده را پیدا کنید و اگر خوش‌شانس باشید در کنار آن توضیح داده شده است که k برای چه تعریف شده است. اگر خوش‌شانس نباشید باید فردی که این کد را نوشته پیدا کنید و از او بپرسید که چرا k را تعریف کرده است. باز هم اگر آدم خوش‌شانسی باشید خودتان کد را نوشته‌اید و با کمی فکر کردن یادتان می‌آید که چرا k را تعریف کرده‌اید. در غیر این صورت باید از پشت دستگاه بلند شوید و دنبال فردی که کد را نوشته بگردید . اگر در این حالت هم خوش‌شانس باشید فرد در دست‌رس است و می‌توانید از او سوال بپرسید. نمی‌خواهم بیش‌تر از این بحث را با توضیح دادن سناریوهای مختلف طولانی کنم. ولی در یک جمله در تمام این سناریوها لازم است که شما چند لحظه تمرکز خود را از روی مساله‌ی اصلی بردارید و به یک موضوع فرعی (متغیر k) فکر کنید. لازم نسیت یادآوری کنم که مساله‌ی اصلی به اندازه‌ی کافی پیچیده است و به تمرکز زیادی نیاز دارد (هیچ کس دوست ندارد هنگاه راه رفتن یک تخم مرغ را به وسیله‌ی یک قاشق در دهانش حمل کند).

این دو دستور را با هم مقایسه کنید:

k = 3;

و

subStringLength = 3;

یا حتی اگر به جز زبان مادری خود زبان دیگری نمی‌دانید

touleZirReshte = 3;

وقتی یک نفر دستور اول را می‌خواند هیچ برداشتی به او دست نمی‌دهد. ولی وقتی دستور دوم (یا سوم) را می‌خواند خیلی راحت حدس می‌زند که این دستور طول زیررشته را برابر با ۳ قرار می‌دهد. دقت کنید که در همین پاراگراف هم برای اینکه ببینید دستور اول و دوم و سوم چه بودند باید تمرکز خود را از روی پاراگراف برمی‌داشتید و چند بار به پاراگراف‌های بالاتر مراجعه می‌کردید.

متاسفانه در این کدِ این برنامه این نوع متغیرها کم نیستند. i , j , m , temp, sub. دلیل این امر شاید به فرهنگ نادرستی بر‌می‌گردد که توسط کتاب‌های آموزش برنامه‌نویسی ترویج داده می‌شود. بسیاری از کتاب‌های آموزش برنامه‌نویسی نام‌های این‌چنینی برای متغیرها انتخاب می‌کنند. بخشی از أن هم به فرهنگی بر می‌گردد که در بین ریاضی‌دان‌ها استفاده می‌شود. ریاضی‌دان‌ها معمولا از نمادهای تک حرفی برای بیان روابط و معادله‌ها استفاده می‌کنند. اما کد یک برنامه‌ی کامپیوتری با اثبات یک قضیه ریاضی خیلی تفاوت دارد و نباید از فرهنگ رایج ریاضی‌دان‌ها برای نوشتن برنامه‌های کامپیوتری استفاده کرد (مگر أن‌که به ساده‌تر شدن مساله کمک کند).

همین موضوع در مورد نام تابع‌هایی که در برنامه تعریف شده‌اند نیز صدق می‌کند. تقریبا تمام تابع‌های تعریف شده در این برنامه نام‌های عجیبی دارند که به هیچ وجه کاری که انجام می‌دهند را القا نمی‌کنند. برای مثال دستور زیر را درنظر بگیرید:

query(input, k);

به نظر شما این دستور چه کاری انجام می‌دهد؟ آیا k بار برای پیدا کردن input جست و جو انجام می‌دهد؟ آیا در کاراکتر kام input جست و جو انجام می‌دهد؟ آیا …

برای پیدا کردن جواب باید حتما به جایی که متغیرهای k و input و تابع query تعریف شده است مراجعه کنید و توضیحات مربوط به أن‌ها را بخوانید (اگر خوش‌شانس باشید). حالا دستور زیر را در نظر بگیرید.

splitIntoSubstrings(inputString, substringLength);

به سادگی می‌توان فهمید که این دستور رشته‌ی inputString را به زیر رشته‌هایی با طول substringLength تقسیم می‌کند.

همین موضوع در مورد سایر تابع‌ها به خصوص تابع compute صدق می‌کند. برای مثال دستور زیر را در نظر بگیرید.

compute(str.substring(m).concat(str.substring(0, len - (strLen - m))));

در مورد این‌که این دستور بیش‌تر به یک فاجعه شباهت دارد تا یک دستور که توسط یک برنامه‌نویس خوب نوشته می‌شود خیلی حرف دارم و احتمالا یک پست مجزا در مورد آن خواهم نوشت. ولی در حال حاضر فقط این موضوع را یادآوری می‌کنم که یکی از بدترین اسم‌هایی که برای یک تابع می‌توان انتخاب کرد همین compute است. برخی دیگر از این اسامی عبارتند از process, analyze, calculate, do, perform, init, initilize, start. این نام‌ها به این دلیل نامناسب هستند که هیچ اطلاعاتی در مورد اینکه این تابع‌ها چه کاری انجام می‌دهند به فردی که کد را می‌خواند منتقل نمی‌کنند. همه می‌دانند که هر تابعی بلاخره یک چیزی را حساب (compute) می‌کند، مهم این است که بدانند این تابع چه چیزی را حساب می‌کند.


کد اصلی

/* this class implements the encoding requested in problem #1706
*/
/**
* @author Esmaeil Ashrafi <s.ashrafi@gmail.com>
*/
import java.util.ArrayList;
public class StierlitzCipher {
private static String input; // the string suppose to be ciphered
private static int k; // the length of sub strings
// temporary variable for query substrings
private static StringBuffer temp = new StringBuffer();
// an array to store substrings of each query string
private static ArrayList<String> sub = new ArrayList<String>();
/**
* @param args
* the command line arguments
*/
public static void main(final String[] args) {
/*
* if ran in command line console,first argument willl be the string to
* cipher and second considered as the key
*/
if (args.length == 2) {
input = new String(args[1]);
k = Integer.parseInt(args[0]);
} // otherwise use of default values of problem
else {
input = new String("abaccc");
k = 3;
// prompt the user to using default values
System.out.println("doing encrypt by default values:"
+ "\nString : \"abaccc\"\nKey : 3");
}
query(input, k);
System.out.println(); // just for trimming the output !
}// end of main
/**
* gets the entire string and splits it to substrings in length of "len" and
* sends each substring to method compute for calculating the number of
* different non-empty substrings could obtained from them.there will be
* substrings(and consequent call to compute method)call as much as the length
* of parameter str
*
* @param str
* - the string to be cipher
* @param len
* - the length of substrings(the key)
*/
private static void query(final String str, final int len) {
int m;
final int strLen = str.length();
for (m = 0; m <= strLen - len; m++) {
compute(str.substring(m, m + len));
}
for (m = m; m < strLen; m++) {
compute(str.substring(m).concat(str.substring(0, len - (strLen - m))));
}
}// end of query
/**
* queries queryString to calculate number of its different and non-empty
* substrings and prints the count to the standard output
*
* @param queryStr
* - the string to be queried
*/
private static void compute(final String queryStr) {
final int qLen = queryStr.length();
for (int i = 0; i < qLen; i++) {
for (int j = i + 1; j <= qLen; j++) {
if (!sub.contains(temp.replace(0, temp.length(),
queryStr.substring(i, j)).toString())) {
sub.add(temp.toString());
}
}
}
System.out.print(" " + sub.size());
sub.clear();
}// end of compute
}// end of class

مقدمه

آوریل 6, 2010

قهوه‌ی جاوا

تقدیم به هر آن‌که بیش از یک زبان می‌داند

چرا این وبلاگ به وجود آمد

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

سلب مسئولیت

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

مسایل حقوقی

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