Trong phần trước * Jerry, một tay cựu học việc yêu cầu Alphonse, một tay học việc, viết một chương trình tạo các số nguyên dùng "sieve of Etastosthenes". Jerry, nhận thấy Alphonse ứng dụng trọn bộ thuật toán vào một function "khổng tượng" nên đã yêu cầu Alphonse tách nó ra theo ba khái niệm: khởi động, ứng tạo và chuẩn xuất;... nhưng Alphonse không biết phải bắt đầu từ đâu...
Gã nhìn tôi một lúc, rõ ràng đang đợi tôi làm gì đó. Nhưng rốt cuộc gã thở dài, lắc đầu và tiếp tục. "Ðể mở rộng ba khái niệm rõ ràng hơn, tao muốn mày tách chúng ra thành ba methods riêng biệt. Ðồng thời vứt hết những cái phụ chú không cần thiết và đặt một cái tên khá hơn cho cái class. Mày làm xong những thứ đó rồi phải bảo đảm là mấy cái test vẫn còn chạy được."
Các bạn có thể thấy những điểm tôi đã làm trong mã dẫn 3. Tôi đã đánh dấu những thay đổi bằng chữ đậm, y hệt như Martin Fowler trình bày trong cuốn Refactoring của ông ta. Tôi đổi tên của cái class thành dạng danh từ, vứt hết những phụ chú về chuyện Eratosthenes và tạo ra ba methods từ ba khái niệm trong generatePrimes function.
Tách ra ba functions buộc tôi phải đưa ra một số biến hàm của function thành static fields của cái class. Jerry nói cách này làm rõ những biến hàm nào là local và những biến hàm nào có ảnh hưởng rộng lớn hơn.
Mã dẫn 3:
PrimeGenerator.java, version 2
/**
* This class generates prime numbers up to a user-specified
* maximum. The algorithm used is the Sieve of Eratosthenes.
* Given an array of integers starting at 2: Find the first
* uncrossed integer, and cross out all its multiples. Repeat
* until the first uncrossed integer exceeds the square root of
* the maximum value.
*/
import java.util.*;
public class PrimeGenerator {
private static int s;
private static boolean[] f;
private static int[] primes;
public static int[] generatePrimes(int maxValue) {
if (maxValue < 2)
return new int[0];
else {
initializeSieve(maxValue);
sieve();
loadPrimes();
return primes; // return the primes
}
}
private static void loadPrimes() {{{/b}
int i,j;
// how many primes are there?
int count = 0;
for (i = 0; i < s; i++) {
if (f[i])
count++; // bump count.
}
primes = new int[count];
// move the primes into the result
for (i = 0, j = 0; i < s; i++) {
if (f[i]) // if prime
primes[j++] = i;
}
}
private static void sieve() {{{/b}
int i,j;
for (i = 2; i < Math.sqrt(s) + 1; i++) {
// if i is uncrossed, cross out its multiples.
if (f[i]) {
for (j = 2 * i; j < s; j += i)
f[j] = false; // multiple is not prime
}
}
}
private static void initializeSieve(int maxValue) {{{/b}
// declarations
s = maxValue + 1; // size of array
f = new boolean[s];
// initialize array to true.
for (int i = 0; i < s; i++)
f[i] = true;
// get rid of known non-primes
f[0] = f[1] = false;
}
}
Jerry bảo tôi mã này hơi lộn xộn, nên gã giành lấy bàn đánh và chỉ tôi cách dọn dẹp. . Mã dẫn 4 minh hoạ những gì gã đã làm. Thoạt tiên gã vứt đi cái biến hàm s trong initializeSieve và thay thế nó bằng f.length. Sau đó gã đổi tên của ba functions (theo kiểu) gã cho là có ấn tượng hơn. Cuối cùng gã sắp xếp lại cái "bộ lòng" initializeArrayOfIntegers (từ initializeSieve) để cho dễ đọc hơn một chút. Các cái test vẫn chạy nhưng thường.
Mã dẫn 4:
PrimeGenerator.java, version 3 (partial)
public class PrimeGenerator {
private static boolean[] f;
private static int[] result;
public static int[] generatePrimes(int maxValue) {
if ((maxValue < 2)
return new int[0];
else {
initializeArrayOfIntegers(maxValue);
crossOutMultiples();
putUncrossedIntegersIntoResult();
return result;
}
}
private static void
initializeArrayOfIntegers(int maxValue) {
f = new boolean[an[maxValue + 1];
f[0] = f[1] = false; //neither primes nor multiples.
for (int i = 2; i < f.length; i++)
f[i] = true;
}
Tôi phải công nhận mã này rõ hơn một chút. Trước giờ tôi nghĩ tạo functions có tên sinh động là phí thời giờ , nhưng những chỉnh đổi của gã quả thật làm cho mã nguồn dễ đọc hơn.
Tiếp theo Jerry trỏ vào crossOutMultiples, nói là gã nghĩ cụm if(f[i] == true) có thể làm cho dễ đọc hơn nữa. Tôi nghĩ đến điểm này chừng một phút. Ý định của các cụm này dùng để kiểm tra xem i không bị loại trừ; thế là tôi đổi tên của f thành unCrossed.
Jerry nói mã này được hơn nhưng tôi vẫn chưa hài lòng với nó vì nó dẫn đến khả năng phủ định đôi (double negative) như unCrossed[i] = false. Bởi thế gã đổi tên của dãy số thành dãy isCrossed với chỉ số nhỏ hơn 2. Các cái test vẫn chạy được.
Jerry tách phần lặp bên trong (inner loop) của crossOutMultiples function và gọi nó là crossOutMultipleOf. Gã bảo rằng các cụm tương tự như if (isCrossed[i] == false) dễ nhầm lẫn nên gã tạo ra function có tên notCrossed và thay cụm if thành if (notCrossed(i)). Kết tiếp gã chạy thử mấy cái test lại.
Sau đó Jerry hỏi tôi ý nghĩa của phần số căn đó là gì. Tôi tốn ít thời giờ viết phụ chú giải thích tại sao cần phải lặp lại cho đến phần số căn của chiều dài dãy số. Tôi cố tranh đua với Jerry bằng cách tách phần tính toán thành một function, nơi tôi có thể đưa vào phần phụ giải. Trong khi viết phụ chú tôi nhận ra rằng căn số là phân tố cực đại của số nguyên trong một dãy số. Bởi thế để ứng phó, tôi chọn cách gọi đó (maxValue) cho các biến hàm. Cuối cùng tôi bảo đảm các tests vẫn chạy được. Kết quả của các thay đổi trong mã dẫn 5.
Mã dẫn 5:
PrimeGenerator.java version 4 (partial)
public class PrimeGenerator {
private static boolean[] isCrossed;
private static int[] result;
public static int[] generatePrimes(int maxValue) {
if (maxValue < 2)
return new int[0];
else {
initializeArrayOfIntegers(maxValue);
crossOutMultiples();
putUncrossedIntegersIntoResult();
return result; }
}
private static void
initializeArrayOfIntegers(int maxValue) {
isCrossed = new boolean[maxValue + 1];
for (int i = 2; i < isCrossed.length; i++)
isCrossed[i] = false;
}
private static void crossOutMultiples() {
int maxPrimeFactor = calcMaxPrimeFactor();
for (int i = 2; i <= maxPrimeFactor; i++)
if (notCrossed(i))
crossOutMultiplesOf(i);
}
private static int calcMaxPrimeFactor() {
// We cross out all multiples of primes. Thus, all crossed
// out multiples have p and q for factors. If p > sqrt of the
// size of the array, then q will never be greater than 1.
// Thus p is the largest prime factor in the array, and is
// also the iteration limit.
double maxPrimeFactor = Math.sqrt(isCrossed.length) + 1;
return (int) maxPrimeFactor;
}
private static void crossOutMultiplesOf(int i) {
for (int multiple = 2*i; multiple < isCrossed.length;
multiple += i)
isCrossed[multiple] = true;
}
private static boolean notCrossed(int i) {
return isCrossed[i] == false;
}
Tôi bắt đầu nắm bắt được vấn đề nên liền xét lại method xét lại method putUncrossedIntegersIntoResult. Tôi thấy rằng method này có hai phần. Phần thứ nhất đếm các số nguyên không bị loại trong dãy số, và tạo nên dãy kết quả (bằng chiều dài của dãy số). Phần thứ nhì dời các số nguyên không bị loại vào dãy kết quả này. Bởi thế, như bạn thấy trong mã dẫn 6, tôi tách phần thứ nhất ra để hình thành function cho chính nó và dọp dẹp lặt vặt đôi chút. Các tests vẫn chạy được. Jerry chỉ thoáng gật đầu. Gã có thật sự khoái những điều tôi đã thực hiện không?
Mã dẫn 6:
PrimeGenerator.java, version 5 (partial).
private static void putUncrossedIntegersIntoResult() {
result = new int[numberOfUncrossedIntegers()];
for (int j = 0, i = 2; i < isCrossed.length; i++)
if (notCrossed(i))
result[j++] = i;
}
private static int numberOfUncrossedIntegers() {
int count = 0;
for (int i = 2; i < isCrossed.length; i++)
if (notCrossed(i))
count++;
return count;
}
<đón xem phần kế tiếp>
* Trong nguyên bản là "In the last month's column..." nhưng ở đây tạm dịch thoáng ra là "trong phần trước" cho phù hợp với tinh thần các bài craftsman được post lên diễn đàn (không theo tháng mà theo... tùy hứng của người dịch ;)) ;))
Monday, November 12, 2007
Monday, October 29, 2007
The craftsman - Thợ học việc: Phần 1
Đây là nhật kí của một anh chàng lập trình viên học việc tại một công ty software áp dụng XP (Extreme Programming). Nếu bạn là một lập trình viên, tôi hy vọng nó có thể tạo cho bạn một góc nhìn mới, một cách suy nghĩ mới - hoặc ít ra bạn có thể khám phá một số điều thú vị từ đây.
Nhật ký thân mến,
13 tháng 2, 2002.
Hôm nay đúng là một ngày xui xẻo - Tôi làm hỏng cả chuyện. Tôi rất muốn gây ấn tượng với các ngài "cựu học việc" ở đây nhưng rút cuộc chỉ làm rối tung cả lên.
Ðó là ngày đầu tiên tôi được một chân học việc với ông C. Tôi quả là may mắn có được chân học việc này. Ông C là một tay trùm lớp lang trong vấn đề phát triển phần mềm. Ðấu để giành được chân việc này đúng là nẩy lửa. Các tay học việc của ông C thường trở nên các tay "cựu học việc" sáng giá. Ðiều này có nghĩa được làm việc với ông C có giá trị rõ ràng.
Tôi cứ ngỡ là hôm nay tôi sẽ được gặp ông ta nhưng thay vì đó tôi bị một gã "cựu học việc" níu tôi qua một bên. Gã bảo ông C luôn luôn dẫn các tay học việc đi xuyên qua phần định hướng trong những ngày đầu. Gã nói ông C nhất quyết cho rằng phần thực tập định hướng là thiết thực với các tay học việc và nó dẫn đến mức chất lượng mã nguồn mà ông ta ta dự tưởng.
Tôi náo nức kinh khủng. Ðây là một cơ hội cho họ thấy tôi là một tay lập trình "ngon" cỡ nào. Thế là tôi bảo Jerry tôi không chờ được nữa. Gã đáp lại sự náo nức của tôi bằng cách bảo tôi thử viết một chương trình đơn giản cho gã. Gã muốn tôi dùng "Sieve of Eratosthenes" để tính các số nguyên. Gã còn bảo tôi phải chuẩn bị xong chương trình bao gồm trọn bộ các "unit tests" sẵn sàng để "chấm" sau buổi ăn trưa.
Thật là khoái! Tôi có gần 4 tiếng đồng hồ để "xào nấu" một chương trình giống như Sieve. Tôi quyết tâm thực hiện công tác này một cách hết sức có ấn tượng. Mã dẫn 1 đưa ra những gì tôi đã viết. Tôi nắm chắc là chương trình của tôi được chú thích cẩn thận và trình bày gọn gàng.
Mã dẫn 1:
/**
* This class generates prime numbers up to a user-specified maximum.
* The algorithm used is the Sieve of Eratosthenes.
*
*
* The algorithm is quite simple: Given an array of integers starting at 2,
* cross out all multiples of 2. Find the next uncrossed integer, and cross
* out all of its multiples. Repeat until you have passed the square root of
* the maximum value.
*
* @authorAlphonse, @version 13 Feb 2002 atp
*/
import java.util.*;
public class GeneratePrimes {
/**
* @param maxValue is the generation limit.
*/
public static int[] generatePrimes(int maxValue) {
if (maxValue >= 2) { // the only valid case
// declarations
int s = maxValue + 1; // size of array
boolean[] f = new boolean[s];
int i;
// initialize array to true.
for (i = 0; i < s; i++)
f[i] = true;
// get rid of known non-primes.
f[0] = f[1] = false;
// sieve
int j;
for (i = 2; i < Math.sqrt(s) + 1; i++) {
if (f[i]) { // if i is uncrossed, cross its multiples.
for (j = 2 * i; j < s; j += i)
f[j] = false; // multiple is not prime
}
}
// how many primes are there?
int count = 0;
for (i = 0; i < s; i++) {
if (f[i])
count++; // bump count.
}
int[] primes = new int[count];
// move the primes into the result.
for (i = 0, j = 0; i < s; i++) {
if (f[i]) // if prime
primes[j++] = i;
}
return primes; // return the primes.
} else // maxValue < 2
return new int[0]; // return null array if bad input.
}
}
Sau đó tôi viết một cái "unit test" cho GeneratePrimes. Xem ở mã dẫn 2. Ðoạn mã này dùng JUnit framework như Jerry đã chỉ dẫn. Nó dùng tính chất hướng thống kê; kiểm tra xem cái "generator" có thể tạo ra các số nguyên tới 0, 2, 3 và 100. Trong trường hợp thứ nhất hẳn không có số nguyên nào cả. Trong trường hợp thứ nhì hẳn phải có một số nguyên và nó phải là số 2. Trường hợp thứ ba phải có hai số nguyên và chúng phải là số 2 và 3. Trường hợp cuối phải là 25 số nguyên và số cuối phải là 97. Nếu các bước kiểm tra đều đúng, tôi giả định là cái "generator" làm việc đúng. Tôi e rằng khó có thể tin cậy tuyệt đối cách ở trên, nhưng tôi không nghĩ ra được một trường hợp nào một "function" có thể bị hỏng mà các bước kiểm tra đều đúng.
Mã dẫn 2:
import junit.framework.*;
import java.util.*;
public class TestGeneratePrimes extends TestCase {
public static void main(String args[]) {
Junit.swingui.TestRunner.main(
new String[] {"TestGeneratePrimes"});
}
public TestGeneratePrimes(String name) {
super(name);
}
public void testPrimes() {
int[] nullArray = GeneratePrimes.generatePrimes(0);
assertEquals(nullArray.length, 0);
int[] minArray = GeneratePrimes.generatePrimes(2);
assertEquals(minArray.length, 1);
assertEquals(minArray[0], 2);
int[] threeArray = GeneratePrimes.generatePrimes(3);
assertEquals(threeArray.length, 2);
assertEquals(threeArray[0], 2);
assertEquals(threeArray[1], 3);
int[] centArray = GeneratePrimes.generatePrimes(100);
assertEquals(centArray.length, 25);
assertEquals(centArray[24], 97);
}
}
Tôi mất khoảng một giờ đồng hồ để làm những bước trên chạy được. Jerry không muốn gặp tôi cho đến sau buổi ăn trưa, bởi thế, tôi dành trọn bộ thời gian còn lại đọc cuốn Design Patterns mà Jerry đưa cho tôi.
Sau buổi ăn trưa, tôi ghé văn phòng của Jerry và cho gã biết tôi đã thực hiện xong chương trình. Gã nhìn tôi và với một nụ cười khó tả, hắn nói: "Ðược lắm, hãy xem thử nó thế nào."
Gã dẫn tôi và phòng thí nghiệm và cho tôi ngồi trước một máy. Gã ngồi bên cạnh tôi và yêu cầu tôi đưa chương trình của tôi vào máy này. Thế là tôi chuyển mã nguồn từ máy laptop của tôi lên.
Jerry xem xét hai mã nguồn chừng năm phút rồi gã lắc đầu và bảo: "Mày không thể đưa những cái này cho ông C xem được! Nếu tao để ổng xem mấy cái này, ổng sẽ đuổi cổ cả tao lẫn mày. Ông ấy không phải là người kiên nhẫn đâu."
Tôi đánh thót một phát nhưng cố giữ bình tĩnh và hỏi gã: "Chớ nó sai chỗ nào?"
Jerry thở dài và nói: "Tụi mình nên đi xuyên qua mã nguồn này với nhau." "Tao sẽ chỉ cho mày từng điểm một cách ông C muốn thực hiện nó như thế nào."
"Quá rõ ràng", gã tiếp tục, "cái main function muốn làm ra ba cái functions riêng biệt. Cái thứ nhất khởi tạo tất cả các biến hàm và thiết lập cái "sieve". Cái thứ nhì thực sự thi hành cái "sieve" và cái thứ ba tải kết quả của "sieve" vào một dãy số nguyên."
Tôi nhận ra được ý gã muốn nói gì. Có ba khái niệm chôn trong cái function đó. Tuy vậy, tôi không biết gã muốn tôi phải làm gì với nó.
Gã nhìn tôi một lúc, rõ ràng đang đợi tôi phản ứng sao đó. Nhưng rốt cuộc gã thở dài, lắc đầu và....
(Source: vninfomatic)
Nhật ký thân mến,
13 tháng 2, 2002.
Hôm nay đúng là một ngày xui xẻo - Tôi làm hỏng cả chuyện. Tôi rất muốn gây ấn tượng với các ngài "cựu học việc" ở đây nhưng rút cuộc chỉ làm rối tung cả lên.
Ðó là ngày đầu tiên tôi được một chân học việc với ông C. Tôi quả là may mắn có được chân học việc này. Ông C là một tay trùm lớp lang trong vấn đề phát triển phần mềm. Ðấu để giành được chân việc này đúng là nẩy lửa. Các tay học việc của ông C thường trở nên các tay "cựu học việc" sáng giá. Ðiều này có nghĩa được làm việc với ông C có giá trị rõ ràng.
Tôi cứ ngỡ là hôm nay tôi sẽ được gặp ông ta nhưng thay vì đó tôi bị một gã "cựu học việc" níu tôi qua một bên. Gã bảo ông C luôn luôn dẫn các tay học việc đi xuyên qua phần định hướng trong những ngày đầu. Gã nói ông C nhất quyết cho rằng phần thực tập định hướng là thiết thực với các tay học việc và nó dẫn đến mức chất lượng mã nguồn mà ông ta ta dự tưởng.
Tôi náo nức kinh khủng. Ðây là một cơ hội cho họ thấy tôi là một tay lập trình "ngon" cỡ nào. Thế là tôi bảo Jerry tôi không chờ được nữa. Gã đáp lại sự náo nức của tôi bằng cách bảo tôi thử viết một chương trình đơn giản cho gã. Gã muốn tôi dùng "Sieve of Eratosthenes" để tính các số nguyên. Gã còn bảo tôi phải chuẩn bị xong chương trình bao gồm trọn bộ các "unit tests" sẵn sàng để "chấm" sau buổi ăn trưa.
Thật là khoái! Tôi có gần 4 tiếng đồng hồ để "xào nấu" một chương trình giống như Sieve. Tôi quyết tâm thực hiện công tác này một cách hết sức có ấn tượng. Mã dẫn 1 đưa ra những gì tôi đã viết. Tôi nắm chắc là chương trình của tôi được chú thích cẩn thận và trình bày gọn gàng.
Mã dẫn 1:
/**
* This class generates prime numbers up to a user-specified maximum.
* The algorithm used is the Sieve of Eratosthenes.
*
* Eratosthenes of Cyrene, b. c. 276 BC, Cyrene, Libya; d. c. 194 BC, Alexandria. He was
* the first man to calculate the circumference of the Earth, and was also
* known for working on calendars with leap years and running the library at
* Alexandria.
*
* The algorithm is quite simple: Given an array of integers starting at 2,
* cross out all multiples of 2. Find the next uncrossed integer, and cross
* out all of its multiples. Repeat until you have passed the square root of
* the maximum value.
*
* @authorAlphonse, @version 13 Feb 2002 atp
*/
import java.util.*;
public class GeneratePrimes {
/**
* @param maxValue is the generation limit.
*/
public static int[] generatePrimes(int maxValue) {
if (maxValue >= 2) { // the only valid case
// declarations
int s = maxValue + 1; // size of array
boolean[] f = new boolean[s];
int i;
// initialize array to true.
for (i = 0; i < s; i++)
f[i] = true;
// get rid of known non-primes.
f[0] = f[1] = false;
// sieve
int j;
for (i = 2; i < Math.sqrt(s) + 1; i++) {
if (f[i]) { // if i is uncrossed, cross its multiples.
for (j = 2 * i; j < s; j += i)
f[j] = false; // multiple is not prime
}
}
// how many primes are there?
int count = 0;
for (i = 0; i < s; i++) {
if (f[i])
count++; // bump count.
}
int[] primes = new int[count];
// move the primes into the result.
for (i = 0, j = 0; i < s; i++) {
if (f[i]) // if prime
primes[j++] = i;
}
return primes; // return the primes.
} else // maxValue < 2
return new int[0]; // return null array if bad input.
}
}
Sau đó tôi viết một cái "unit test" cho GeneratePrimes. Xem ở mã dẫn 2. Ðoạn mã này dùng JUnit framework như Jerry đã chỉ dẫn. Nó dùng tính chất hướng thống kê; kiểm tra xem cái "generator" có thể tạo ra các số nguyên tới 0, 2, 3 và 100. Trong trường hợp thứ nhất hẳn không có số nguyên nào cả. Trong trường hợp thứ nhì hẳn phải có một số nguyên và nó phải là số 2. Trường hợp thứ ba phải có hai số nguyên và chúng phải là số 2 và 3. Trường hợp cuối phải là 25 số nguyên và số cuối phải là 97. Nếu các bước kiểm tra đều đúng, tôi giả định là cái "generator" làm việc đúng. Tôi e rằng khó có thể tin cậy tuyệt đối cách ở trên, nhưng tôi không nghĩ ra được một trường hợp nào một "function" có thể bị hỏng mà các bước kiểm tra đều đúng.
Mã dẫn 2:
import junit.framework.*;
import java.util.*;
public class TestGeneratePrimes extends TestCase {
public static void main(String args[]) {
Junit.swingui.TestRunner.main(
new String[] {"TestGeneratePrimes"});
}
public TestGeneratePrimes(String name) {
super(name);
}
public void testPrimes() {
int[] nullArray = GeneratePrimes.generatePrimes(0);
assertEquals(nullArray.length, 0);
int[] minArray = GeneratePrimes.generatePrimes(2);
assertEquals(minArray.length, 1);
assertEquals(minArray[0], 2);
int[] threeArray = GeneratePrimes.generatePrimes(3);
assertEquals(threeArray.length, 2);
assertEquals(threeArray[0], 2);
assertEquals(threeArray[1], 3);
int[] centArray = GeneratePrimes.generatePrimes(100);
assertEquals(centArray.length, 25);
assertEquals(centArray[24], 97);
}
}
Tôi mất khoảng một giờ đồng hồ để làm những bước trên chạy được. Jerry không muốn gặp tôi cho đến sau buổi ăn trưa, bởi thế, tôi dành trọn bộ thời gian còn lại đọc cuốn Design Patterns mà Jerry đưa cho tôi.
Sau buổi ăn trưa, tôi ghé văn phòng của Jerry và cho gã biết tôi đã thực hiện xong chương trình. Gã nhìn tôi và với một nụ cười khó tả, hắn nói: "Ðược lắm, hãy xem thử nó thế nào."
Gã dẫn tôi và phòng thí nghiệm và cho tôi ngồi trước một máy. Gã ngồi bên cạnh tôi và yêu cầu tôi đưa chương trình của tôi vào máy này. Thế là tôi chuyển mã nguồn từ máy laptop của tôi lên.
Jerry xem xét hai mã nguồn chừng năm phút rồi gã lắc đầu và bảo: "Mày không thể đưa những cái này cho ông C xem được! Nếu tao để ổng xem mấy cái này, ổng sẽ đuổi cổ cả tao lẫn mày. Ông ấy không phải là người kiên nhẫn đâu."
Tôi đánh thót một phát nhưng cố giữ bình tĩnh và hỏi gã: "Chớ nó sai chỗ nào?"
Jerry thở dài và nói: "Tụi mình nên đi xuyên qua mã nguồn này với nhau." "Tao sẽ chỉ cho mày từng điểm một cách ông C muốn thực hiện nó như thế nào."
"Quá rõ ràng", gã tiếp tục, "cái main function muốn làm ra ba cái functions riêng biệt. Cái thứ nhất khởi tạo tất cả các biến hàm và thiết lập cái "sieve". Cái thứ nhì thực sự thi hành cái "sieve" và cái thứ ba tải kết quả của "sieve" vào một dãy số nguyên."
Tôi nhận ra được ý gã muốn nói gì. Có ba khái niệm chôn trong cái function đó. Tuy vậy, tôi không biết gã muốn tôi phải làm gì với nó.
Gã nhìn tôi một lúc, rõ ràng đang đợi tôi phản ứng sao đó. Nhưng rốt cuộc gã thở dài, lắc đầu và....
(Source: vninfomatic)
Sunday, October 28, 2007
Tình yêu học trò
Năm đó Tính học lớp 9, Tính có thương một cô nàng học lớp 7, Tính chấp nhận ở lại lớp 2 năm để có thể cùng nàng chung bước đến trường.... Nhưng có ngờ đâu...sau 2 năm ở lại lớp....nàng vẫn là học sinh lớp 7.....Tính bực mình hỏi nàng tại sao, nàng trả lời : "Em xin lỗi, e đã thương anh Hùng học lớp 5!"
Thông báo
Thông báo của hội SV phía cuối trang có đoạn:
Các bạn SV nữ đang cần tuyển gấp 50-70 bạn SV nam để làm tình...
(sang trang)
... nguyện viên cho chương trình Mùa hè xanh
Các bạn SV nữ đang cần tuyển gấp 50-70 bạn SV nam để làm tình...
(sang trang)
... nguyện viên cho chương trình Mùa hè xanh
Nhân, Lễ, Nghĩa, Trí, Tín, Dũng
Khi quay cóp phải có đủ 6 điều sau: Nhân, Lễ, Nghĩa, Trí, Tín
Nhân khi bạn bè quay cóp bị bắt,phải tỏ ra thông cảm và làm ra vẻ mặt an ủi. Đó gọi là Nhân. Lễ:Đừng bao giờ cao điểm hơn người đă chỉ bài cho mình...để lần sau được chỉ típ đó gọi là Lễ.
Nghĩa khi mình quay cóp bị bắt....thà chít cũng không khai bạn bè mình ra.Đó gọi là Nghĩa.
Trí: Trong lúc thi phải quan sát triệt để chỗ đứng của giám thị đồng thời lắng nghe 4 phương tám hướng các đáp án của bạn bè....đấy gọi là Trí .
Tín: Đừng bao giờ nghi ngờ đáp án,bạn bè trong khi quay cóp, phải luôn tin tưởng đáp án trong tài liệu cũng như bạn bè là chính xác!
Nhân khi bạn bè quay cóp bị bắt,phải tỏ ra thông cảm và làm ra vẻ mặt an ủi. Đó gọi là Nhân. Lễ:Đừng bao giờ cao điểm hơn người đă chỉ bài cho mình...để lần sau được chỉ típ đó gọi là Lễ.
Nghĩa khi mình quay cóp bị bắt....thà chít cũng không khai bạn bè mình ra.Đó gọi là Nghĩa.
Trí: Trong lúc thi phải quan sát triệt để chỗ đứng của giám thị đồng thời lắng nghe 4 phương tám hướng các đáp án của bạn bè....đấy gọi là Trí .
Tín: Đừng bao giờ nghi ngờ đáp án,bạn bè trong khi quay cóp, phải luôn tin tưởng đáp án trong tài liệu cũng như bạn bè là chính xác!
Subscribe to:
Comments (Atom)