3.3.2. Phân tích chương trình thành chuỗi các Tokens
Các chương trình cần so sánh được phân tích thành các thẻ Tokens, việc này được hiểu là chuyển các dòng lệnh sang dạng các mã thông báo, các mã thông báo là căn cứ để so sánh ở bước sau. Mã thông báo đặc trưng cho bản chất của câu lệnh chứ không phụ thuộc vào hình thức thể hiện của tên biến, tên hàm. Ngoài ra JPlag cũng quy định các lời gọi phương thức, các ngoại lệ, dòng chú thích hay các dòng trắng sẽ không được gắn mã thông báo, do đây là điểm dễ thay đổi mà không ảnh hưởng đến chương trình. Danh sách các mã thông báo đầy đủ trong phụ lục mã Tokens của công cụ JPlag tại phần cuối của luận văn.
Để hiểu về cách phân tích chượng trình thành các mã thông báo ta xem xét ví dụ Mã nguồn 3.2. Dòng 1, 4, 18, 19, 20 là các dòng chú thích, không được gắn mã thông báo. Dòng 17 là dòng trống, dòng 22 là ngoại lệ cũng bị bỏ qua không gắn mã. Các dòng còn lại được chuyển thành các mã thông báo (sau đây gọi là các Tokens) phản ánh đúng ý nghĩa của câu lệnh. Các dòng lệnh bắt đầu và kết thúc class, hàm, vòng lặp, khởi tạo một đối tượng được chuyển thành các Tokens đặc trưng với BEGINCLASS, BEGINMENTHOD, BEGINWHILE, BEGINIF và ENDCLASS,ENDMENTHOD, ENDWHILE, ENDIF. Dòng 3 khởi tạo lớp được chuyển thành Token BEGINCLASS. Dòng 5 khởi tạo một phượng thức và khai báo 2 biến đầu vào nên được chuyển thành 2 Tokens VARDEF và Token BEGINMENTHOD. Dòng lệnh 6, 7 là bắt đầu câu lệnh while và if được chuyển thành Tokens BEGINWHILE và BEGINIF. Các câu lệnh 8 và 10 là câu lệnh gán giá trị cho biến, vì vậy dù là 2 câu lệnh khác nhau nhưng đều được chuyển thành Token ASSIGN. Tượng tự với những câu lệnh khác.
20
Code Tokens
1 //import your library
2 import java.util.Scanner; IMPORT
3 public class Solution { BEGINCLASS
4 //Type your main code here
5 public static int gcd(int a, int b){ VARDEF, VARDEF,
BEGINMENTHOD
6 if (a==0) BEGINIF
7 return b; RETURN, ENDIF
8 while (a != b) { BEGINWHILE 9 if (a > b) { BEGINIF 10 a -= b; ASSIGN 11 } else { ELSE 12 b -= a; ASSIGN 13 } ENDIF 14 } ENDWWHILE 15 return a; RETURN 16 } ENDMENTHOD 17 18 /** 19 * Main. 20 */
21 public static void main(String[] args) VARDEF, BEGINMENTHOD
22 throws java.io.IOException {
23 int gcd; VARDEF
24 Scanner sc = newScanner(System.in); VARDEF,NEWCLASS,ASSIGN
25 int a = sc.nextInt(); VARDEF, ASSIGN
26 int b = sc.nextInt(); VARDEF, ASSIGN
27 gcd = gcd(a,b); ASSIGN
28 System.out.println(gcd); APPLY
29 } ENDMENTHOD
30 } ENDCLASS
Mã nguồn 3.2. Tách mã nguồn thành các Tokens
Chuyển chương trình thành các Tokens có ý nghĩa lớn trong khâu so sánh tại bước tiếp theo của bài toán này. Việc so sánh kiểu từ với từ giữa các chương trình để tìm chương trình tương đương là không khả thi. Bởi vì học sinh ở giai đoạn mới lập trình thường được yêu cầu sử dụng các chú thích để giải thích đoạn lệnh nên chương trình của mỗi học sinh có nhiều chú thích khác nhau. Ngoài ra do học sinh chưa hoàn thiện thói quen định dạng code nên các lệnh có sử dụng mở đóng ngoặc có thể bị dịch xuống nhiều dòng, thừa dòng trắng giữa các câu lệnh. Việc chuyển mã chương trình
21
thành các mã thông báo giúp giải quyết các vấn đề trên, cho phép tìm ra các chương trình đúng có độ tương tự tốt nhất với chương trình đang cần sửa chữa.
3.3.3. So sánh các chuỗi Tokens
Đầu vào của mô-đun gợi ý chương trình là chương trình lỗi của học sinh và rất nhiều chương trình mẫu khác được lưu trong bộ sưu tập. Vì vậy, việc so sánh được hiểu là so sánh chương trình lỗi với tất cả các chương trình mẫu trong bộ sưu tập. Để thực hiện việc này, chương trình lỗi sẽ so sánh lần lượt với từng chương trình mẫu (Hình 3.6). Với mỗi bộ chương trình lỗi – chương trình mẫu sẽ tính ra tỉ lệ tương đồng, kết quả này được hiển thị cho người dùng là căn cứ lựa chọn chương trình mẫu nào làm gợi ý.
Bước phân tích chương trình thành chuỗi các Tokens như mô tả ở Mục 3.3.2 cho kết quả mỗi chương trình được phân tách thành nhiều Tokens khác nhau, ta gọi là chuỗi các Tokens. So sánh hai chuỗi Tokens trong JPlag sử dụng thuật toán Greedy-String- Tiling (GST) [30] được phát triển bởi Michael Wise, sau đó các tác giả đã cải tiến bằng cách kết hợp với thuật toán Karp-Rabin để nâng cao hiệu quả.
So sánh hai chuỗi Tokens A và B, mục đích là tìm ra chuỗi con chung lớn nhất của hai mã nguồn từ đó tính ra tỉ lệ trùng lắp so với tổng thể chương trình. Chuỗi con này phải đảm bảo nguyên tắc:
i. Một Token trong A chỉ được so khớp với một Token trong B
ii. Các chuỗi con được tìm thấy phải độc lập với vị trí của chúng trong chuỗi. Hay việc thay đổi vị trí các thành phần trong mã nguồn chương trình không làm ảnh hưởng tới việc so sánh.
Chương trình lỗi Chương trình mẫu 1 . . . . Chương trình mẫu n Chương trình mẫu 2 So sánh So sánh So sánh