2. Ngụn ngữ lập trỡnh Visual C#
2.8 Khỏi niệm liờn quan
2.8.1 Khỏi niệm về Predicate
Trong ngụn ngữ lập trỡnh C# cú một số cấu trỳc đặc biệt như cấu trỳc if…then…else, cấu trỳc for(), foreach(), while(), do…while()…Những cấu trỳc đú người ta gọi là cỏc cấu trỳc điều khiển, đú là cỏc cấu trỳc lặp (Loop), rẽ nhỏnh, ngoài ra một số cấu trỳc khai bỏo phương thức (Method), lớp (Class), khụng gian tờn (Namespace), thuộc tớnh (Property) cũng cú thể xem là cỏc cấu trỳc điều khiển.
Cấu trỳc điều khiển là một khối lệnh trong đú cú một cõu lệnh đảm nhiệm việc điều khiển thực thi của cỏc cõu lệnh khỏc trong khối, người ta gọi cõu lệnh đú là Predicate. Predicate điều khiển việc thực thi của cỏc cõu lệnh khỏc trong khối cú nghĩa là cỏc cõu lệnh trong khối chỉ được thực thi khi predicate được thực thi.
Vớ dụ:
Cṍu trỳc điều khiển Predicate tương ứng
1. Int tong = 0; 2. Int nhan = 1; 3. for(int i = 0; i<100;i++) 4. { 5. tong = tong+i; 6. nhan = nhan*i; 3. for(int i = 0; i<100;i++) Cỏc phụ thuộc điều khiển: 34; 35; 36; 37
7. } 8. int hieu = 0; 9. if(nhan>tong*tong) 10. hieu = nhan-tong*tong; 11.else if(nhan==tong*tong) 12. hieu = 0; 13.else if(nhan<tong*tong) 14. hieu = tong*tong-nhan; 9. if(nhan>tong*tong) 11.else if(nhan==tong*tong) 13.else if(nhan<tong*tong) Cỏc phụ thuộc điều khiển: 910; 1112; 1314
Cỏc cõu lệnh là Predicate thỡ khụng kết thỳc bằng kớ tự “ ; ” và thường là cõu lệnh đầu tiờn trong khối lệnh điều khiển, đõy là dấu hiệu để nhận biết Predicate với cỏc cõu lệnh bỡnh thường khỏc.
Predicate cú một thuộc tớnh đú là thuộc tớnh ‘khối cõu lệnh được điều khiển bởi Predicate đú’, thuộc tớnh này là một cấu trỳc bao gồm hai thành phần là chỉ số cõu lệnh đầu tiờn của khối sau Predicate và chỉ số cõu lệnh cuối cựng của khối.
Theo như định nghĩa của phụ thuộc điều khiển, thỡ cỏc cõu lệnh trong khối điều khiển sẽ phụ thuộc điều khiển vào Predicate. Như vậy, chỳng ta cú thể xỏc định được cỏc phụ thuộc điều khiển của chương trỡnh qua việc xỏc định cỏc Predicate của chương trỡnh.
2.8.2 Bậc của cõu lệnh (Level)
Khối lệnh là một tập liờn tiếp cỏc cõu lệnh của chương trỡnh, cỏc cõu lệnh trong khối cú quan hệ với nhau đú là chỳng đều thuộc một cấu trỳc điều khiển hoặc một cấu trỳc đặc biệt khỏc. Khối lệnh được đặc trưng bởi vị trớ của cõu lệnh đầu tiờn và vị trớ của cõu lệnh cuối cựng của khối.
Cỏc khối lệnh cú thể chứa “gọn” lẫn nhau, núi là chứa “gọn” tức là chỳng khụng được lồng nhau. Gọi B1 và B2 là hai khối trong chương trỡnh P nào đú, B1 được gọi là trực tiếp chứa B2 nếu như B1 chứa B2 và khụng tồn
Cõu lệnh s được gọi là thuộc trực tiếp trong khối B nếu như khụng tồn tại một khối B0 nào đú được chứa bởi B mà s nằm trong B0.
Xét chương trỡnh P (P cũng cú thể là một phương thức) khi đú P là một khối lệnh, trong P cú thể chứa cỏc khối lệnh con khỏc đú cú thể là cỏc cấu trỳc điều khiển hoặc một số cấu trỳc đặc biệt khỏc. Một cõu lệnh s nào đú của P cú thể nằm sõu trong cỏc khối con của P, điều đú cho thấy chỉ số của s chưa thể mụ tả hết mối quan hệ của s với cỏc cõu lệnh khỏc trong P. Chỳng tụi đó đưa ra một khỏi niệm đú là bậc của cõu lệnh qua khỏi niệm bậc của khối lệnh như sau:
o Bậc của một khối lệnh là một số nguyờn khụng õm. o Bậc của P bằng 0.
o Nếu B2 là khối được chứa trực tiếp trong B1 thỡ bậc của B2 bằng bậc của B1 cộng thờm một đơn vị.
o Bậc của một cõu lệnh bằng bậc của khối nú thuộc trực tiếp. Chi tiết và vớ dụ về bậc của cõu lệnh chỳng ta cú thể xem ở bảng sau:
stt Cõu lệnh Bậc Nhỏnh
1 public Nghiem PTBH(float a, float b, float c)
2 { 0
3 float delta = b * b - 4 * a * c; 0
4 Nghiem ngh = new Nghiem(); 0
5 if (a == 0) 0 (5,0,5) 6 { 1 7 if (b == 0) 1 (7,0,7) 8 { 2 9 if (c == 0) 2 (9,0,9) 10 { 3 11 n1 = 1; 3 12 n2 = 2; 3 13 } 3 14 else if (c != 0) 2 (9,1,14) 15 { 3 16 n1 = 0; 3 17 n2 = 0; 3 18 } 3 19 } 2
21 { 3 22 n1 = -c / b; 3 23 n2 = -c / b; 3 24 } 3 25 } 2 26 else if(delta > 0) 0 (5,1,26) 27 { 1 28 n1 = (-b + Math.Sqrt((double)delta)) / (2 * a); 1 29 n2 = (-b - Math.Sqrt((double)delta)) / (2 * a); 1 30 } 1 31 else if (delta == 0) 0 (5,2,31) 32 { 1 33 n1 = -b / (2 * a); 1 34 n2 = -b / (2 * a); 1 35 } 1 36 ngh.Ng1 = n1; 0 37 ngh.Ng2 = n2; 0 38 return ngh; 0 39 } 0
2.8.3 Nhỏnh của cõu lệnh (Branch)
Trong việc xử lý cỏc cõu lệnh việc phỏt hiện ra cỏc cõu lệnh khụng cựng thuộc trong cựng một nhỏnh của một cõu lệnh rẽ nhỏnh hoặc ngược lại là rất quan trọng trong việc thiết lập mối quan hệ giữa cỏc cõu lệnh. Trong phần này chỳng tụi xin trỡnh bày một khỏi niệm quan trọng liờn quan nữa đú là khỏi niệm chỉ số nhỏnh của cõu lệnh.
Một cấu trỳc rẽ nhỏnh sẽ cú ớt nhất là một nhỏnh và số nhỏnh là khụng hạn chế, trong cấu trỳc đú cỏc nhỏnh được đỏnh chỉ số tăng dần một đơn vị theo thứ tự từ 0 đến nhỏnh cuối cựng, chỉ số này được gọi là chỉ số cục bộ(kớ hiệu là Lindex).
Nhỏnh của một cấu trỳc rẽ nhỏnh là một khối lệnh, núi chớnh xỏc hơn nhỏnh của một cấu trỳc rẽ nhỏnh cũng là một cấu trỳc điều khiển. Vỡ thế nhỏnh cũng cú Predicate của nú, chỉ số Predicate thuộc nhỏnh kớ hiệu là
Gọi X là một cấu trỳc rẽ nhỏnh của chương trỡnh P nào đú; X1, X2…Xn là cỏc nhỏnh của X, khi đú trong chương trỡnh P quy ước đỏnh chỉ số của cỏc nhỏnh như sau:
o Chỉ số của nhỏnh theo chương trỡnh P gọi là chỉ số chung (Kớ hiệu Gindex).
o Chỉ số chung là bộ ba số nguyờn dương được ghép lại bởi chỉ số Predicate thuộc nhỏnh (Bindex) chỉ số cục bộ (Lindex) và chỉ số của Predicate thuộc X(kớ hiệu là Index) theo cụng thức:
GIndex = ([Index], [LIndex], [BIndex]).
Vớ dụ:
Nếu Bindex = 5; Index = 1; LIndex = 2 Gindex = (1, 2, 5); Nếu Bindex = 7; Index = 2; Lindex = 14Gindex = (2, 14, 7); Nhỏnh của cõu lệnh được định nghĩa qua chỉ số nhỏnh của cỏc nhỏnh thuộc cấu trỳc rẽ nhỏnh như sau:
o Thuộc tớnh nhỏnh chỉ tớnh cho những cõu lệnh nằm trong cỏc nhỏnh của cấu trỳc rẽ nhỏnh.
o Chỉ số nhỏnh của cõu lệnh bằng chỉ số nhỏnh trong cựng chứa cõu lệnh đú.
Vớ dụ: Xét chương trỡnh sau:
Cõu lệnh Nhỏnh của cõu lệnh
1.if(x >0) (1,0,1) 2.{ (1,0,1) 3. If(a <0) (3,0,3) 4. { (3,0,3) 5. x = -a*a; (3,0,3) 6. y = -a*b; (3,0,3) 7. } (3,0,3) 8. else if(a==0) (3,1,8) 9. { (3,1,8) 10. x = 0; (3,1,8) 11. y = 0; (3,1,8) 12. } (3,1,8) 13.} (1,0,1)
2.9 Cỏc vṍn đề khỏc
o Mảng: trong C# cú hai loại mảng, mảng tĩnh và mảng động.
o Tham số và truyền tham số: Cú hai cỏch truyền tham số cho cỏc phương thức đú là truyền giỏ trị và truyền địa chỉ. Truyền nếu truyền bằng giỏ trị thỡ giỏ trị của biến khi ra khỏi hàm sẽ khụng bị thay đổi trong khi truyền bằng địa chỉ thỡ giỏ trị của biến dựng để truyền cú thể bị thay đổi.
o Trong C# cú cỏc biến đối tượng. So với cỏc biến bỡnh thường chỉ thay đổi giỏ trị khi cú cõu lệnh gỏn, nhưng đối với cỏc biến đối tượng chỳng cú thể bị thay đổi giỏ trị khi chỳng tự gọi đến một phương thức của bản thõn đối tượng đú.
3. Kỹ thuật chuẩn húa chương trỡnh
Với hệ thống hụ̃ trợ sửa lụ̃i chương trỡnh, yờu cầu đầu tiờn đú là chương trỡnh đầu vào phải là một chương trỡnh đỳng hoàn toàn về mặt cỳ phỏp. Điều này cũng dờ hiểu vỡ việc sửa lụ̃i cỳ phỏp đó được hụ̃ trợ rất nhiều trong cỏc “mụi trường phỏt triển tớch hợp” IDE (Intergrated Development
Environment) tương ứng với cỏc ngụn ngữ lập trỡnh. Hệ thống hụ̃ trợ sửa
lụ̃i chương trỡnh mà chỳng tụi đang xõy dựng với mục tiờu là hụ̃ trợ cho lập trỡnh viờn để họ cú thể dờ dàng tỡm ra cỏc lụ̃i về mặt ngữ nghĩa của chương trỡnh chỉ ỏp dụng cho những chương trỡnh khụng còn lụ̃i về mặt cỳ phỏp.
phải chuõ̉n húa chương trỡnh? Và chương trỡnh như thế nào thỡ được gọi là một chương trỡnh chuõ̉n để hệ thống cú thể xử lý bước tiếp theo?
Trước khi trỡnh bày chuõ̉n húa chương trỡnh chỳng tụi xin đưa ra một số qui ước cho chương trỡnh như sau:
Keyword: Tập cỏc từ khúa trong ngụn ngữ lập trỡnh, vớ dụ như cỏc từ
khúa “int”, “float”, “double”, “static”… chẳng hạn. Xem chi tiết cỏc từ khúa tại bảng cỏc từ khúa của C#.
Operator: Tập cỏc phép toỏn số học, phép toỏn logic của ngụn ngữ lập
trỡnh; chi tiết cỏc phép toỏn được liệt kờ dưới bảng sau:
Loại phộp toỏn Chi tiết loại phộp toỏn
Tớnh toỏn + - * / % ++ -- += -= =+ =- ( )
So sỏnh < <= > >= == !=
Logic ! && ||
Dịch bit ^
Variable: Biến chương trỡnh là biến do lập trỡnh viờn khai bỏo trong
quỏ trỡnh lập trỡnh.
Assignment:Phép gỏn, trong C# phép gỏn được biểu thị bởi dấu “=”.
Expression: thành phần chứa cỏc thành phần Keyword, Variable và
một số kớ tự đặc biệt, thành phần này chỉ thuộc một trong hai vế của phép gỏn; vớ dụ như cõu lệnh “int a = b + c” sẽ bao gồm hai biểu thức “int a” và “b + c”. Cỏc Expression cú thể lồng lẫn nhau, vỡ thế chỳng ta cú thể cú nhiều Expression trong một cõu lệnh.
Khỏi niệm chương trỡnh chuõ̉n mà chỳng tụi trỡnh bày ở đõy chỉ cú ý nghĩa trong phạm vi đề tài này, vỡ thế khỏi niệm chương trỡnh chuõ̉n ở đõy
khụng nờn dựng cho cỏc nghiờn cứu liờn quan đến chương trỡnh mỏy tớnh khỏc.
Một chương trỡnh C# chuõ̉n là một chương trỡnh thỏa món cỏc điểm sau:
Là một chương trỡnh đỳng hoàn toàn về mặt cỳ phỏp. Chỉ cú duy nhất một cõu lệnh trờn một dòng chương trỡnh.
Cỏc kớ tự ‘{‘ và ‘}’ cũng được xem là một cõu lệnh khi chỳng nằm biệt lập và vỡ thế chỳng cũng phải nằm trờn một dòng chương trỡnh.
Một cõu lệnh là một tổ hợp của cỏc Keyword, Variable, Operator, Assignment hoặc giữa cỏc Expression với Assignment (Vớ dụ cõu lệnh sau: “float a = (float)(x+y)/z”). Giữa cỏc Keyword, Variable, Assignment chỉ được phõn tỏch bởi một kớ tự khoảng trắng.
Giữa Operator và cỏc thành phần khỏc phải liền nhau, tức là khụng cú khoảng trắng làm phõn cỏch. Vớ dụ như nếu ta viết a + b là đỳng cỳ phỏp nhưng khụng đỳng chuõ̉n, đỳng chuõ̉n phải là a+b.
Chuõ̉n húa chương trỡnh qua một số bước cơ bản như sau:
Bước 1: Đưa chương trỡnh về dạng một dòng chương trỡnh chứa một cõu lệnh.
Bước 2: Loại bỏ khoảng trắng trước và sau kớ tự phép toỏn của mụ̃i cõu lệnh.
Vớ dụ: “int c = (a + b ) / d;” chuyển thành int “c = (a+b)/d;”
4. Xõy dựng Đồ thị phụ thuộc chương trỡnh
Trước khi chỳng ta cú thể tạo nờn một rỳt gọn của chương trỡnh, chỳng ta phải xõy dựng nờn một biểu diờn đồ thị của chương trỡnh gần với mụ hỡnh ngữ nghĩa của chương trỡnh. Nếu cú thể, chỳng ta rất muốn cú được cỏc thụng tin chớnh xỏc về quỏ trỡnh thực thi của chương trỡnh theo thời gian thực thi của nú. Núi chung đú là điều khụng thể bởi vỡ cỏc giỏ trị của tham số đầu vào là khụng xỏc định cũng như khụng thể xỏc định được nhỏnh nào của cõu lệnh rẽ nhỏnh sẽ được thực thi và một vòng lặp sẽ thực thi bao nhiờu lần. Nhưng khụng phải thế là chỳng ta khụng thể tớnh toỏn được cỏc thụng tin cho việc tớnh Rỳt gọn cũng như việc gỡ rối chương trỡnh, chỳng ta hoàn toàn cú thể xỏc định được tập cỏc biến được định nghĩa, tập cỏc biến được sử dụng tại mụ̃i cõu lệnh, tập hàm được gọi trong chương trỡnh, tập cỏc tham số được truyền cho một hàm, hay chỳng ta cũng cú thể xỏc định được số nhỏnh của cõu lệnh rẽ nhỏnh, vựng diều khiển của cỏc cõu lệnh điều khiển…Tất cả cỏc thụng tin chỳng ta cú thể cú đủ để cho một thuật toỏn Rỳt gọn chương trỡnh cú thể cài đặt.
Như đó trỡnh bày ở phần lý thuyết cơ sở, Đồ thị phụ thuộc chương trỡnh núi chung và Đồ thị phụ thuộc phương thức núi riờng là một đồ thị cú hướng bao gồm tập cỏc đỉnh và cỏc cung, trong đú một đỉnh của đồ thị biểu diờn một cõu lệnh của chương trỡnh (hoặc phương thức) còn cỏc cung của đồ thị biểu diờn mối quan hệ phụ thuộc điều khiển hoặc phụ thuộc dữ liệu giữa cỏc cõu lệnh.
Khi xõy dựng Đồ thị phụ thuộc thỡ phần việc khú khăn nhất đú là xỏc định cỏc cung của đồ thị một cỏch tối ưu, tức là cỏc cung được xỏc định phải phản ỏnh đỳng mối quan hệ phụ thuộc giữa cỏc cõu lệnh, để được như vậy
thỡ ngay từ đầu việc tớnh toỏn cỏc tập biến được định nghĩa (Def) và tập cỏc biến được sử dụng (Ref) cũng phải được tớnh toỏn một cỏch chớnh xỏc.
4.1 Kỹ thuật xỏc định tập Def và Ref tại một cõu lệnh
Cỏc cõu lệnh cú cỏc biến được định nghĩa đú là: cõu lệnh khai bỏo biến, cõu lệnh khởi tạo biến, cõu lệnh vừa khai bỏo vừa khởi tạo biến, cõu lệnh khai bỏo phương thức, cõu lệnh gọi phương thức.
Trong phần này chỳng tụi đưa ra một phương thức dựng để phỏt hiện và lấy ra cỏc biến cú mặt trong một Expression đó được cài đặt trong hệ thống (khỏi niệm Expression đó được để cập ở phần trờn), phương thức này lấy đầu vào là một Expression và trả về đầu ra tập cỏc biến cú mặt trong Expression đú, chỳng tụi gọi phương thức này là GetVar().
4.1.1. Cõu lệnh khai bỏo biến.
Cỳ phỏp: [modifier] DataType variablelist;
Trong đú: modifier là một trong cỏc từ khúa:public, protected, private, static.
DataType: kiểu dữ liệu của biến.
variablelist: danh sỏch cỏc biến được khai bỏo cỏch nhau bởi dấu “,”. Theo cỳ phỏp trờn thỡ phần cỏc biến được định nghĩa của cõu lệnh là variablelist, tập Def được tớnh một cỏch đơn giản bằng cỏch lấy variablelist làm tham số truyền vào phương thức GetVar(): Def = GetVar(variablelist).
Cõu lệnh khai bỏo khụng tồn tại cỏc biến được định nghĩa nờn tập Ref khụng được khởi tạo.
4.1.2. Cõu lệnh khởi tạo
Def = variable;
Ref = GetVar(Expression);
4.1.3. Cõu lệnh khai bỏo phương thức
Cỳ phỏp: [KeywordList] [DataType] MethodName(listofparameter); Trong cõu lệnh khai bỏo phương thức cỏc biến nằm trong vựng listofparameter dưới dạng cỏc tham số, cỏc biến này là cỏc biến được định nghĩa, vỡ thế:
Def = Getvar(listofparameter);
4.1.4. Cõu lệnh gọi phương thức
Cỳ phỏp: MethodName(passedparameter);
Trong đú passedparameter là danh sỏch cỏc giỏ trị, biến được dựng làm tham số để truyền vào phương thức. Trong trường hợp này cỏc tập Def và Ref được tớnh như sau:
Def = GetVar(passedparameter); Ref = GetVar(passrdparameter);
4.2. Xỏc định tập cung của Đồ thị phụ thuộc
Xỏc định cỏc cung của đồ thị thực chất là xỏc định cỏc phụ thuộc điều khiển và phụ thuộc dữ liệu, theo phần lý thuyết đó trỡnh bày thỡ cỏc phụ thuộc điều khiển và phụ thuộc dữ liệu được tớnh như sau.
4.2.1. Xỏc định phụ thuộc điều khiển (Control Dependence)
Phụ thuộc điều khiển xuất hiện trong cỏc cấu trỳc điều khiển, như vậy để xỏc định cỏc phụ thuộc điều khiển thỡ đầu tiờn chỳng ta phải xỏc định được sự cú mặt của cỏc cấu trỳc điều khiển. Xỏc định phụ thuộc điều khiển qua cỏc bước sau:
o Xỏc định cỏc cấu trỳc điều khiển.
o Cỏc cõu lệnh trực tiếp thuộc cấu trỳc trừ Predicate đều phụ thuộc vào Predicate.
4.2.2. Xỏc định phụ thuộc dữ liệu (Data Dependence)
Phụ thuộc dữ liệu xuất phỏt từ cõu lệnh cú tập Def khỏc rụ̃ng đến cõu lệnh cú tập Ref khỏc rụ̃ng, cỏc bước sau dựng để xỏc định phụ thuộc dữ liệu. Thuật toỏn sau dựng để kiểm tra xem cõu lệnh s2 cú phụ thuộc dữ liệu vào cõu lệnh s1 hay khụng.
Đầu vào: cõu lệnh s1, cõu lệnh s2 thuộc chương trỡnh P.
Đầu ra: trả về giỏ trị true nếu s2 phụ thuộc dữ liệu vào s1, trả về giỏ trị false nếu s2 khụng phụ thuộc dữ liệu vào s1.
Quy ước: s1.Def: tập Def của s1; s1.Ref: tập Ref của s1. s2.Def: tập Def của s2; s2.Ref: tập Ref của s2.