Giải quyết bài toán

Một phần của tài liệu Nghiên cứu ứng dụng ngôn ngữ f trong phát triển phần mềm (Trang 50 - 57)

CHƯƠNG 3: BÀI TOÁN ỨNG DỤNG

3.2. Ứng dụng F* trong bài toán tính tổng tài nguyên sử dụng chương trình

3.2.2. Giải quyết bài toán

Xây dựng thuật toán tính kiểu cho bài toán trên.

Trước tiên để có thể xây dựng thuật toán tính kiểu, chúng ta cần phải xây dựng các hàm parser từ các đoạn giả mã thành các chuỗi số và các ngoặc tương ứng với từng luồng nhất định. Ta có onacid tương ứng với +1, commit tương ứng với -1, khởi tạo một luồng spawn tương ứng với “(“, kết thúc luồng tương ứng với “)”,bắt đầu công việc tương ứng với “e”. Sau đó chúng ta đưa chuỗi số đó vào cây cú pháp trừu tượng và sử dụng các thuật toán dưới đây để rút gọn, tính toán và đưa ra kết quả cuối cùng. Trong đó ta có các định nghĩa kiểu như sau:

type Tag =

| Plus = 0

| Minus = 1

| Max = 2

| Join = 3

Để trừu tượng hóa các hành vi giao tác như mở đóng giao tác, giá trị giới hạn max tài nguyên và các thành phần joint commit, ta sử dụng lần lượt các định nghĩa kiểu sau:

plus, minus, max, jointương ứng với cỏc dấu sau ‘+, , #,ơ. Kốm theo cỏc dấu là các số tự nhiên thể hiện cho giá trị của mỗi hành vi như sau:type TagNum = Tag * int.

Tiếp theo ta định nghĩa chuỗi các hành vi type TagSeq = TagNum listtương ứng với chuỗi hành vi trong thread.Cuối cùng ta tổng hợp lại các thành phần đó và đưa vào cây cú pháp trừu tượng được định nghĩa như sau:

type Tree =

| Branch of Tree list

| Leaf of TagNum

3.2.2.1. Chính tắc một chuỗi số có dấu bất kì a. Mô tả thuật toán:

Đầu vào: Một chuỗi có dấu chưa được chính tắc. Ta có quy tắc rút gọn như sau:

- seq(S) = S khi S là một chuỗi chính tắc - seq(S#m#nS') = seq(S#max(m,n)S') - seq(S+m+nS') = seq(S+(m+n)S')

- seq(S−mnS') = seq(S(mn)S')

- seq(S+m#lnS') = seq(S+ (m - 1)# (l + 1)− (n - 1)S') Đầu ra: Chuỗi đã được chính tắc.

Bước 1: Lấy lần lượt các phần tử từ trái qua phải và kiểm tra như sau:

Chuỗi chớnh tắc là chuỗi khụng chứa cỏc dấu liền nhau như −−, +ơ, ##, ++, +−, +#−, +#ơ.Như vậy nếu chuỗi khụng gồm cỏc dấu liền nhau như trờn thỡ chuỗi đú được gọi là chuỗi chính tắc.

Bước 2: Gọi x là mẫu được tìm thấy đầu tiên từ trái qua phải của chuỗi lst, x có thể rút gọn bằng y theo quy tắc trên thì sẽ tiếp tục các việc sau:

- Loại x khỏi lst

- Thêm y vào đầu danh sách lst - Quay lại Bước 1 với chuỗi lst b. Cài đặt và kiểm chứng:

Phương thức seq được xây dựng để giải quyết chuẩn tắc một chuỗi có dấu bất kỳ có dạng như sau:

1val seq: TagSeq -> TagSeq 2 letrec seq lst =

3 match lst with 4 | [] -> []

5 | (_, 0)::xs-> seq xs

6 | (Tag.Plus, n1)::(Tag.Plus, n2)::xs -> seq ((Tag.Plus,n1+n2)::xs) 7 | (Tag.Max, n1)::(Tag.Max, n2)::xs -> seq ((Tag.Max,max n1 n2)::seq

xs)

8 | (Tag.Minus, n1)::(Tag.Minus, n2)::xs -> seq ((Tag.Minus,n1+n2)::xs) 9 | (Tag.Plus, n1)::(Tag.Minus, n2)::xs ->

10 if n1 >= n2 then

11 seq ((Tag.Plus,n1-n2)::(Tag.Max,n2)::xs) 12 else

13 seq ((Tag.Max,n1)::(Tag.Minus,n2-n1)::xs) 14 | (Tag.Plus, n1)::(Tag.Max, n)::(Tag.Minus, n2)::xs ->

15 let m = min n1 n2 in 16 if n1 > n2 then

17 seq ((Tag.Plus,n1-m)::(Tag.Max, n+m)::xs) 18 elif n1 = n2 then

19 seq ((Tag.Max, n+m)::xs) 20 else

21 seq ((Tag.Max, n+m)::(Tag.Minus,n2-m)::xs) 22 | x::xs ->x::(seq xs)

Theo đó lần lượt đưa ra các dữ liệu đầu vào, ta có một số ví dụ minh họa cho việc kiểm chứng và có kết quả trả về đúng đắn.

Bảng 3.1 Bảng kết quả kiểm thử phép toán chính tắc chuỗi số có dấu

Lượt Dữ liệu vào (lst) Kết quả

1 (Plus,1); (Minus,1) (Max,1)

2 (Plus,1); (Max,1); (Minus,1) (Max,2)

3 (Max,4); (Max,5); (Plus,3); (Max,3); (Minus,4) (Max,6);(Minus,1)

3.2.2.2. Khử dấu trừ trong chuỗi có dấu chính tắc (Join) a. Mô tả thuật toán:

Đầu vào: Cho một chuỗi cú dấu chớnh tắc mà tagset(S)∩ {ơ,+} = ∅ và cú i = first(S,

) và i # 0. Hàm join(S) được đệ quy để thay thế cỏc dấu “” thành dấu “ơ” bằng thuật toỏnnhư saujoin(S) = S khi first(S, ) = 0 nếu khụng thỡjoin(S) = s1…si-1ơ1join((|si|- 1) si+1…sk).

Đầu ra: Chuỗi đó được thay thế hoàn toàn cỏc dấu “” thành dấu “ơ”.

Bước 1: Lấy lần lượt các phần tử từ trái qua phải và kiểm tra như sau:

Nếu chuỗi không có phần tử nào chứa dấu “−” thì chuỗi đó đã được khử hoàn toàn dấu“−”.

Bước 2: Gọi x là mẫu đầu tiên chứa dấu “ ”và có giá trị là ntrong danh sách lst1, ta cầnthay thế thành dấu “ơ” như sau:

- Thờm phần tử “ơ1”vào đầu danh sỏch.

- Bổ sung phần tử “− (n-1) ” vào đầu danh sách còn lại.

- Tiếp tục gọi đệ quy với hàm join và danh sách còn lại ở trên.

b. Cài đặt và kiểm chứng:

Phương thức join được định nghĩa để biến tất cả các phần tử chứa dấu ” thành cỏc phần tử chứa dấu “ơ” như sau:

1 val join: TagSeq -> TagSeq 2 letrec join lst =

3 match lst with 4 | [] -> []

5 | (Tag.Minus, n1)::xs->

6 if n1 > 0 then

7 (Tag.Join, 1)::(join ((Tag.Minus, n1-1)::xs)) 8 else

9 (join xs) 10 | x::xs ->x::(join xs)

Theo đó lần lượt đưa ra các dữ liệu đầu vào, ta có một số ví dụ minh họa cho việc kiểm chứng và có kết quả trả về đúng đắn.

Bảng 3.2 Bảng kết quả kiểm khử dấu “−” thành dấu “ơ”

Lượt Dữ liệu vào (lst) Kết quả

1 (Minus,1) (Join,1)

2 (Minus,2) (Join,1); (Join,1)

3 (Max,3); (Minus,2) (Max,3); (Join,1); (Join,1)

3.2.2.3. Gộp 2 chuỗi số có dấu chính tắc (Merge) a. Mô tả thuật toán:

Đầu vào: Cho 2 chuỗi cú dấu chớnh tắc với số phần tử chứa dấu “ơ”bằng nhau (Cú thể bằng 0). Cho i = 1, 2 và Si được viết dưới dạng tổng quỏt: Si = #mi ơni S'i (mi và ni cú thể bằng 0). Hàm mergeđịnh nghĩa quy tắc đệ quy như sau: merge(S1, S2) =#(m1+m2) khi Si =

#mi , i =1, 2 nếu không thì:

merge(S1, S2) = (m1+m2) (n1+n2)merge(S'1, S'2)

Đầu ra: Chuỗi sau khi đã được gộp từ 2 chuỗi có dấu chính tắc ban đầu

Bước 1: Lấy lần lượt các phần tử từ trái qua phải của 2 chuỗi lst1, lst2 và kiểm tra Nếu 2 phần tử được lấy ra có dạng là #mi

- Loại 2 phần tử đầu của lst1, lst2 ra khỏi danh sách.

- Thêm phần tử #(m1 + m2) vào đầu danh sách mảng kết quả trả về của bước sau đây.

- Quay lại bước 1 với các phần tử còn lại của 2 danh sách.

Nếu 2 phần tử có dạng là mi

- Loại 2 phần tử đầu của lst1, lst2 ra khỏi danh sách.

- Thêm phần tử (m1 + m2) vào đầu danh sách mảng kết quả trả về của bước sau đây.

- Quay lại bước 1 với các phần tử còn lại của 2 danh sách.

Nếu 2 phần tử có dạng là m1 ,#m2

- Loại phần tử đầu của S2 ra khỏi danh sách.

- Quay lại bước 1 với lst1và các phần tử còn lại của lst2.

Nếu 2 phần tử có dạng là #m1 ,m2

- Loại phần tử đầu của lst1ra khỏi danh sách.

- Quay lại bước 1 với các phần tử còn lại của lst1và lst2.

b. Cài đặt và kiểm chứng:

Phương thức merge được đưa ra để có thể gộp 2 chuỗi có dấu chuẩn tắc và chứa số phần tử cú dấu “ơ”bằng nhau.

1val merge: TagSeq -> TagSeq -> TagSeq 2letrec merge lst1 lst2 =

3 if List.isEmpty lst1 then lst2 4 elif List.isEmpty lst2 then lst1 5 else

6 let tag1 = fst (List.head lst1) in 7 let tag2 = fst (List.head lst2) in

8 if tag1 = Tag.Max && tag2 = Tag.Max then

9 (Tag.Max, (snd (List.head lst1)) + (snd (List.head lst2)))::

(merge (List.tail lst1) (List.tail lst2)) 10 elif tag1 = Tag.Join && tag2 = Tag.Join then

11 (Tag.Join, (snd (List.head lst1)) + (snd (List.head lst2)))::

(merge (List.tail lst1) (List.tail lst2)) 12 elif tag1 = Tag.Max && tag2 = Tag.Join then

13 (Tag.Max, snd (List.head lst1))::(merge (List.tail lst1) lst2) 14elif tag1 = Tag.Join && tag2 = Tag.Max then

15 (Tag.Max, snd (List.head lst2))::(merge lst1 (List.tail lst2)) 16 else failwith "Error in merge"

Theo đó lần lượt đưa ra các dữ liệu đầu vào, ta có một số ví dụ minh họa cho việc kiểm chứng và có kết quả trả về đúng đắn.

Bảng 3.3 Bảng kết quả kiểm khử hàm merge

Lượt Dữ liệu vào (lst1) Dữ liệu vào (lst2) Kết quả

1 (Join,1) (Join,1) (Join,2)

2 (Join,1) (Max,1) ; (Join,1) (Max,1) ; (Join, 2)

3 (Max,2) ; (Join, 2) (Join,1) (Max,2) ; (Join, 3)

4 (Max,3) (Max,3) (Max,6)

3.2.2.4. Đồng bộ hóa các luồng song song khi kết thúc giao tác (Joint commit) a. Mô tả thuật toán:

Đầu vào: Cho 2 chuỗi chính tắc không có phần tử chứa dấu “”. Hàm joint commit được viết để kết hợp 2 chuỗi chính tắc trên thành chuỗi mới theo quy tắc sau:

jc(#n , #l) = #max(n,l)

jc(S'1+n#n , #lơlS'2) = jc(S'1+(n−1)max((n' +1), (l'+l)),S'2) Đầu ra: Chuỗi chính tắc mới từ kết hợp 2 chuỗi chính tắc trên.

Bước 1: Lấy lần lượt các phần tử trong chuỗi lst1, lst2 và kiểm tra:

- Nếu phần tử đầu của lst1 có dạng “+n1#n2” thì kiểm tra tiếp các phần tử trong lst2:

o Nếu phần tử đầu lst2 cú dạng ơl thỡ:

 Loại bỏ phần tử đầu của chuỗi lst2.

 Gọi hàm chính tắcseqvới đầu vào là chuỗi lst2 sau khi thêm vào phần tử #l (Với l là giá trị của phần tử đầu mảng lst2 được loại bỏ ở trên).

 Gọi đệ quy joint commit với chuỗi lst1 = +(n − 1) và lst2 sau khi đã thực hiện các bước trên.

o Nếu cỏc phần tử đầu lst2 cú dạng #l1ơl2 thỡ:

 Loại bỏ 2 phần tử đầu của lst2.

 Gọi đệ quy joint commit với chuỗi lst1 = +(n−1) #(max (n2+1) (l1+l2) và lst2 sau khi đã thực hiện các bước trên.

- Nếu phần tử đầu của lst1 có dạng “+n1” thì kiểm tra tiếp các phần tử trong lst2:

o Nếu phần tử đầu lst2 cú dạng ơl thỡ:

 Loại bỏ phần tử đầu của chuỗi lst2.

 Gọi hàm chính tắc seq lại chuỗi lst2 sau khi thêm vào phần tử #l (Với l là giá trị của phần tử đầu mảng lst2 được loại bỏ ở trên).

 Gọi đệ quy joint commit với chuỗi lst1 = +(n − 1) và lst2 sau khi đã thực hiện các bước trên.

o Nếu cỏc phần tử đầu lst2 cú dạng #l1ơl2 thỡ:

 Loại bỏ 2 phần tử đầu của lst2.

 Thêm phần tử #max (l1+l2) vào đầu chuỗi lst2.

 Gọi hàm chính tắc seq với đầu vào là chuỗi lst2 ở trên.

 Gọi đệ quy joint commit với chuỗi lst1 = +(n−1)và lst2 sau khi đã thực hiện các bước trên.

b. Cài đặt và kiểm chứng:

Phương thức joint commit được đưa ra để có thể gộp 2 chuỗi có dấu chính tắc không bao gồm dấu“” như sau:

1 letrec jc (lst1: TagSeq) (lst2: TagSeq) : TagSeq = 2 match lst1 with

3 | [] ->if List.isEmpty lst2 then 4 []

5 else lst2

6 | (Tag.Plus,m1)::[] ->

7 match lst2 with 8 | [] -> lst1

9 | (Tag.Max,l1)::(Tag.Join,l2)::xs2 ->

10 if m1 > 1 then

11 jc [(Tag.Plus,m1-1)] (seq((Tag.Max,l1+l2)::xs2)) 12 else

13 (Tag.Max,l1+l2)::xs2 14 |(Tag.Join, l2)::xs2 ->

15 if m1 > 1 then

16 jc [(Tag.Plus, m1-1)] (seq ((Tag.Max, l2)::xs2)) 17 else

18 (Tag.Max, l2)::xs2

19 | otherwise -> failwith "Need attention in jc 1"

20 | (Tag.Plus,n1)::(Tag.Max,n2)::[] ->

21 match lst2 with 22 | [] -> lst1

23 | (Tag.Max,l1)::(Tag.Join,l2)::xs2 ->

24 if n1 >= 1 then

25 jc ((Tag.Plus,(n1-1))::[Tag.Max,(max (n2+1) (l1+l2))]) xs2 26 else

27 jc [Tag.Join,max (n2+1) (l1+l2)] xs2 28 | (Tag.Join, l2)::xs2->

29 if n1 > 1 then

30 jc [(Tag.Plus, n1-1)] (seq ((Tag.Max, l2)::xs2)) 31 else

32 (Tag.Max, max (n2+1) l2)::xs2

33 | otherwise -> failwith "Need attention in jc 2"

34 | x::xs -> []

Theo đó lần lượt đưa ra các dữ liệu đầu vào, ta có một số ví dụ minh họa cho việc kiểm chứng và có kết quả trả về đúng đắn.

Bảng 3.4 Bảng kết quả kiểm khử hàm joint commit Lượt Dữ liệu vào (lst1) Dữ liệu vào (lst2) Kết quả

1 (Plus,1) (Join,1) (Max,1)

2 (Plus,1) (Max,1) ; (Join,1) (Max,2)

3 (Plus,1) ; (Max,1) (Max,1) ; (Join,1) (Max,2) 4 (Plus,2) ; (Max,3) (Max,2) ; (Join,1) (Plus,1) ; (Max,4)

Một phần của tài liệu Nghiên cứu ứng dụng ngôn ngữ f trong phát triển phần mềm (Trang 50 - 57)

Tải bản đầy đủ (PDF)

(64 trang)