TÀI LIỆU ĐẠI HỌC - godautre PPLT-Chuong 2 tài liệu, giáo án, bài giảng , luận văn, luận án, đồ án, bài tập lớn về tất cả...
Trang 1Chương 2 Phương pháp lập trình hàm và
ứng dụng trong NET
Trang 2• Mô hình tính toán dựa trên các khái niệm toán học của hàm
• Mỗi hàm có thể không đối số hoặc nhiều đối số Kết quả của hàm là một giá trị xác định hoặc dự báo
• Ngôn ngữ lập trình hàm: FP, Haskell, Gopher…
1 Lập trình hàm
Trang 3• Số tự nhiên: Toán học: 1,2…
Khoa học tính toán: 0,1,2…
• Ví dụ về một hàm đơn giản: Hàm tính giai thừa:
fact(n) = 1 x 2 x 3… x n với fact(0) =1
• Có thể được viết theo các dạng:
• Dễ thấy rằng, định nghĩa sau là tương đương với 2 định nghĩa trước (có thể chứng minh bằng quy nạp)
2 Định nghĩa của hàm
Trang 43 Nhìn qua về lập trình cho hàm
trong Gopher
• Hàm fact(n) với định nghĩa:
Có thể được lập trình với cách cách khác nhau trong Gopher
Trang 51 Thông qua biểu thức
-Dòng 1: Xác định kiểu cho hàm fact1 theo cú pháp object :: type
fact1 được định nghĩa như là một hàm (ký hiệu ->) với một tham
số có kiểu Int (thứ nhất) và kiểu trả lại của hàm là Int (thứ hai) (Gopher không định nghĩa tập tự nhiên nên tạm sử dụng kiểu Int)-Dòng 2: Khai báo nội dung của hàm fact1, nó có dạng:
fname params = body
Trong đó: fname: Tên của hàm
params: các tham số của hàm (có thể không có) body : một biểu thức xác định giá trị của hàm.
ví dụ trên là biểu thức if – then - else
Trang 10
Ví dụ về file nguồn facts.gs
Trang 11Một số lệnh thông dịch
Trang 13Ví dụ về thực hiện tính biểu thức và hàm thư viện
trên dòng lệnh
Khoảng thời gian cần tính và
số ô nhớ được sử dụng
Trang 15Ví dụ về việc nộp và thực hiện các hàm trong
chương trình
• Nộp file chương trình facts.gs nói trên
Trang 16• Liệt kê tên hàm trong chương trình
• Gọi thực hiện các hàm trong chương trình
Trang 185.1 Các kiểu định nghĩa sẵn (built-in types)
Giá trị: True, False
Toán tử : && (and) || (or) và not
5 Cơ bản về Gopher
Trang 19-Bao trong cặp dấu nháy đơn ‘ : ‘a’, ‘A’
- Kí tự escape : ‘\n’ , ‘\t’ , ‘\\’ , ‘\’’
- Viết dụng mã : ‘\65’ , ‘\x41’ đều cho ‘A’
Trang 20- Nếu t1 và t2 là các kiểu thì tham số kiểu là t1 và hàm có kiểu là t2
- Tên hàm và biến bắt đầu bởi một chữ cái (a z) thường, sau đó có thể là chử cái (hoa, thường), chử số, dấu nháy đơn (‘), dấu gạch dưới (_)
- Hàm có thể có nhiều tham số Ví dụ hàm cộng 2 số nguyên:
f x y = x +y ( add x y)
được hiểu như là : f x + y = (f x) + y
Vì vậy tham số được khai báo:
Hoặc
Trang 21 Kiểu danh sách là kiểu có cấu trúc đầu tiên của Gopher, nó là một chuổi các phần tử cũng kiểu giá trị.
Khai báo [t] là khai báo kiểu danh sách có các phẩn tử có giá trị kiểu t
Ví dụ [Int]
Một danh sách có thể rỗng Được ký hiệu []
Một danh sách khác rỗng có một phần tử đầu danh sách (head element)
và phần đuôi của danh sách (tail)
Toán tử : (dấu hai chấm) biểu diễn phần tử đầu và phần đuôi của danh sách Nó có dạng head:tail
Trang 22• Có 2 hàm thực hiện trên kiểu danh sách là head và tail:
Ví dụ: head [5,3,2] cho lại 5 tail [5,3,2] cho danh sách [3,2]
Trang 23• Là kiểu đã được định nghĩ như là mảng (danh sách) các ký tự:
• Hằng chuổi có thể được viết các dạng khác nhau:
• Vì là danh sách nên có thể áp dụng các hàm head và tail:
Trang 24Ví dụ về hàm tính độ dài chuổi (len)
Hàm thực hiện như sau:
Có thể mở rộng hàm len để tính số phần
tử của một danh sách với:
len :: [a] -> Int
Trang 25- t1, t2,…, tn là các kiểu (có thể khác nhau)
- Không giống như list, tuple có các phần tử không nhất thiết là
cùng kiểu và số phần tử của tuple là cố định Nó tương tự như kiểu Record trong Turbo – Pascal
- Ví dụ về tuple:
Trang 265.2 Lập trình với list
• Tổng của một list (sumlist)
- Cho danh sách gồm các phần tử nguyên v1, v2, v3, v4, … ,vn
- Tính tổng S = v1+ v2 + v3+ … + vn
- Xây dựng hàm sumlist như sau:
- Có thể thay dòng thứ 3 bởi:
Trang 27• Hàm tính độ dài của một list (length’)
Trong hàm này, ký hiệu (_) được hiểu như là “không quan tâm” đến giá trị của phần tử head
Trang 28• Bỏ đi các phần tử trùng lặp trong list
Để ý rằng, dòng thứ 2 chỉ thực hiện khi danh sách có không ít hơn 2 phần tử
Trang 29• Một số mẫu khác
Dưói đây là một số mẫu tham số, đối số tương ứng và kết quả của việc thử
Trang 305.3 Các toán tử infix
Các toán tử infix, infixl, infixr được sử dụng để định nghĩa thứ tự thực hiện các toán tử trong biểu thức
- infix : tự do
- infixl : từ trái sang phải
- infixr : từ phải sanf trái
Ví dụ về các toán tử infix chuẩn:
Trang 315.4 Các kiểu đệ quy
• Nối danh sách (toán tử ++)
- Tức là nối danh sách thứ hai vào sau danh sách thứ nhất để được một danh sách
- Hàm nối 2 danh sách được ký hiệu theo kiểu toán tử ++
- Đặt infix mức 5 cho toán tử ++ (mở rộng thêm infixr 5):
infixr 5 ++
- Định nghĩa toán tử ++
Trang 32Ví dụ thực hiện toán tử ++
Trang 34• Đảo các phẩn tử của list (rev)
Trong đó toán tử ++ chính là toán tử nối chuổi ở trên
Ví dụ:
Trang 36• Đảo với đệ quy phần đuôi của list
Trang 37• Các định nghĩa cục bộ (let và where)
let
Let thường được sử dụng trong các tập định nghĩa lồng nhau Nó có
dạng:
Let được sử dụng bất kỳ ở đâu xuất hiện một biểu thức
Ví dụ: Hàm f sau đây lập một danh sách mà mỗi phần tử là bình phương của danh sách tăng từ 1 (1,2,3…)
Trang 38 square là hàm một biến
one là một hàm không tham số (nó là một hằng = 1)
(y: ys) biểu thị một mẫu đối sánh của đối số xs của hàm f
Tham chiếu y hoặc ys của đối số xs của f có thể gây lỗi khi xs là rỗng
Các định nghĩa cục bộ square, one, y và ys có phạm vi sử dụng trong
biểu thức sau in
Các định nghĩa cục bộ có thể truy xuất các thành phần thuộc phạm vi của nó (outer) Chẳng hạn: định nghĩa (y:ys) truy xuất xs
Các định nghĩa cục bộ có thể đệ quy và gọi mỗi định nghĩa khác
Mệnh đề let trình diễn theo dạng bottom-up Nó xác định các thành phần trước khi được sử dụng
Trang 39-Tương tự như let nhưng theo kiểu top-down
- where sử dụng linh hoạt hơn let Nó cho phép định nghĩa các thành phần trong các mệnh đề trong khi let chỉ định nghĩa các thành phần
trong một biểu thức (sau in)
- Ví dụ:
Trong ví dụ này where định nghĩa cho các thành phần trong cả 3 mệnh
đề Lưu ý rằng, where được bắt đầu cùng cột với (=)
Trang 40Một ví dụ sử dụng định nghĩa cục bộ: Số Fibonacci
Số Fibonacci: f(0) = 0
f(1) = 1
f(n+2) = f(n) + f(n+1)
Định nghĩa hàm fib theo công thức trên:
Trong định nghĩa trên, hàm fib sử dụng n+2 mẫu để đối sánh các số
tự nhiên >=2
Trang 41Định nghĩa sau đây được coi là có hiệu suất cao hơn khi sử dụng đệ quy với việc cộng 2 số:
Trang 43• Ngắt một list (take và drop)
take và drop là hai toán tử chuẩn
take n xs : cho lại n phần tử đầu tiên của danh sách xs Nó được định
nghĩa như là:
drop n xs : bỏ đi n phần tử đầu tiên của danh sách xs Nó được định
nghĩa như là:
Ví dụ:
Trang 44• Liên hợp list (zip)
zip là toán tử chuẩn, cho phép hợp hai danh sách thành một danh
sách theo kiểu ghép cặp các phần tử tương ứng
Ví dụ:
zip được định nghĩa như là zip’ dưới đây:
Trang 455.6 Xây dựng tập số hữu tỷ
• Số hữu tỷ
Toán học: số hữu tỷ được định nghĩa như là: x/y với y<>0Gopher : Định nghĩa như là một tuple:
Type Rat = (Int, Int)
Ví dụ: (1,7), (-1,-7), (3,21) đều biểu diễn số 1/7
Trang 46• Chuẩn hoá số hữu tỷ
- Chuyển (a,b) thành (x,y) trong đó x,y là nguyên tố cùng nhau
- Quy ước (0,y) = (0,1)
- Hàm chuẩn hoá số hữu tỷ:
Trang 47Việc xây dựng hàm normRat dựa vào các hàm:
signum: hàm chuẩn lấy dấu của một số -1 (âm) , 0 (không), 1 (dương)
Nó được định nghĩa như là:
a được khai báo là một đối tượng thuộc lớp Num và lớp Ord
gcd: hàm chuẩn cho lại ước số chung lớn nhất Nó được định nghĩa như
là:
abs: hàm chuẩn cho lại trị tuyệt đối của một số
error: hàm chuẩn hiển thị thông báo lỗi lên màn hình
showRat: hàm hiển thị số hữu tỷ lên màn hình (xem sau)
Trang 48• Các phép toán trên số hữu tỷ
Trang 49• Hiển thị số hữu tỷ
Trong đó hàm show là một hàm chuẩn Nó convert một số
nguyên thành dạng chuổi
Trang 506 Các hàm bậc cao6.1 Hàm map
Xét hai hàm sau đây:
Bình phương mỗi phần tử trong list:
Độ dài mỗi list con trong list:
Trang 51Hàm map cho phép thực hiện một hàm f lên mỗi phần tử của một
danh sách Nó được định nghĩa như là:
Nếu áp dụng hàm map cho 2 hàm ví dụ trên, ta có:
Trang 526.2 Hàm filter
Xét hai hàm sau đây:
Hàm cho lại một list các số chẳn từ một list
Hàm cho lại một list bằng cách gấp đôi phần tử dương của một list
Trang 53Hàm chuẩn filter với một hàm điều kiện p (kiểu a->Bool) và một danh sách kiểu [a] cho lại một danh dách gồm các phần tử trong danh sách [a] thỏa hàm điều kiện p
Nó được định nghĩa như là:
Áp dụng hàm filter, các hàm trên có thể định nghĩa lại như sau:
Trang 546.3 Các hàm Fold
Xét hai hàm sau đây:
Hàm nối các chuổi thành một chuổi
Hàm cho lại tổng các phần tử trong list
Trang 55Áp dụng hàm foldr , các hàm trên được định nghĩa như sau:
Trong đó: f là một toán tử nhị phân có dạng (a->b->b) Foldr thực hiện kiểu như:
Hàm foldr được định nghĩa như sau:
Hàm foldr
Áp dụng khác:
Trang 56Hàm foldl
Nó thực hiện kiểu như:
Ví dụ áp dụng:
Trang 576.4 Tính bộ phận
Xét hai hàm sau:
Cả hai đêu cho cùng kết quả nhưng khác nhau về đối số:
add: đối số là một 2-tiple và cho lại một Int
add’: đối số là một Int và cho lại một hàm (Int->Int)
Do đó:
add 3 Lỗi
add’ 3 được hiểu là gán 3 cho đối số của nó ((add’ 3) 4) -> gán x, y với 3 và 4 và được 3+4 Tức:
Trang 58Hàm add’ định nghĩa như trên là tương tự như hàm chuẩn (+) (toán tử +)
⇒ Khi viết: (+) 3 có thể hiểu “3 là cộng với…”
Các toán tử khác như (<), (*) cũng tương tự
Ví dụ:
Chú ý rằng: (*) 2 được hiểu là 2 nhân với…
(<) 0 được hiểu là 0 là bé hơn…
Trang 60Ví dụ: Tăng đối số lên 1
Kiểm tra một số dươngHàm chia đôi
Hàm nghịch đảo
lập phương các phần tử
Trang 62Ví dụ 2: Hàm nhân đôi các phần tử dương trong danh sách
Hàm count có 2 tham số: một số nguyên n và một danh sách các danh sách Hàm cho lại số danh sách có độ dài là n
Ví dụ 1: Hàm cho lại số danh sách có độ dài là n
Có thể viết lại:
Trang 63Ví dụ 3: Các hàm cho phần tử cuối và phần khởi đầu danh sách (tức trừ phần tử cuối)
Có thể viết lại:
Ví dụ 4: Các hàm khác
Trang 65Tương tự có cho các hàm có nhiều tham số Chẳng hạn
Biểu thức (\x y -> (x+y)/2)
Biểu thị hàm không định tên với 2 đối số và kết quả trả lại là trung bình công của 2 đối số
Ví dụ khác:
Hàm không tên (\n _ -> n+1) với đối số đầu là n, đối số thứ 2 là
bất kỳ, cho lại một giá trị bằng cách tăng n lên 1
Trang 666.8 Thêm các toán tử ngắt danh sách
Ngoài toán tử chuẩn take và drop, còn có các toán tử span, break, takeWhile, dropWhile, takeUntil, dropUntil
Trang 676.8 Thêm các toán tử liên hợp danh sách
Trang 686.9 Bổ sung các toán tử quan hệ cho tập số hữu tỷ
Trang 697 Các vấn đề khác về danh sách
7.1 Chuổi
Một chuổi số học là một chuổi của các phần tử kiểu liệt kê (tức thuộc lớp Enum) Các kiểu như Int, Float, Char… thuộc Enum
• [m n] dẫn suất một ds các phần tử từ m đến n theo bước 1
Dẫn xuất này phát sinh từ hàm chuẩn enumFromTo m n
• [m, m’ n] dẫn suất một ds các phần tử từ m’ đến n với bước m’-m
Dẫn xuất này phát sinh từ hàm chuẩn enumFromThenTo m m’ n
Trang 70• [m ] , [m, m’…] tương tự như các trường hợp trên nhưng với ds vô
hạn
Các dẫn xuất này phát sinh từ hàm chuẩn enumFrom và enumFromThen
Một chuổi hình học là một chuổi mà các phần tử thuộc kiểu có thứ tự và
là kiểu số (nghĩa là thuộc lớp Ord và Num)
Ví dụ:
Trang 71Ví dụ:
Trang 72Biểu thức giá trị kiểu Bool cũng có thể được sử dụng như là một qualifier Chỉ có giá trị nào làm cho biểu thức là True mới được phát sinh cho danh sách bao hàm
Trang 73Một số ví dụ khác:
Trang 74Một số ví dụ áp dụng:
Ví dụ 1: Chuổi bao gồm các ký tự trống
Ví dụ 2: Kiểm tra số nguyên tố
Trang 75Ví dụ 3: Bình phương của các số nguyên tố
Hàm sqPrimes sau đây cho lại danh sách mà mỗi phần tử là bình phương của số nguyên tố từ m đến n:
Có thể định nghĩa khác cho hàm này:
Trang 76Ví dụ 4: Nhân đôi các phần tử dương
Ví dụ 5: Nối một danh sách của danh sách của danh sách
Hàm này cũng có thể định nghĩa:
Trang 77Ví dụ 6: Tìm vị trí xuất hiện trong danh sách
Tìm tất cả các vị trí
Tìm vị trí xuất hiện đầu tiên:
Trang 78- Tên kiểu phải viết hoa chữ cái đầu
- a1, a2,…an là các biễu diễn kiểu cho n tham số của kiểu dữ liệu
- constr1, constr2…constrn là các cấu tử dữ liệu
Trang 79Trong định nghĩa này, Grayscale ngầm định định nghĩa một hàm cấu tử dạng Int -> Color’
Cấu tử dữ liệu có thể là giá trị kiểu liên hợp
Trang 80Hàm sau đây sẽ chuyển một danh sách vào một Set
Hàm chuẩn nub loại bỏ các phần tử double trong danh sách
Trang 81Định nghĩa kiểu kết quả:
Trang 828.2 Kiểu dữ liệu đệ quy
Kiểu dữ liệu cũng có thể được định nghĩa dạng đệ quy
Ví dụ: Kiểu sau đây định nghĩa một cây nhị phân với một giá trị trêm mỗi node:
Mỗi cây có thể là empty hoặc là một Node trrong đó mỗi node gồm một giá trị kiểu a và hai cây con “trái” và “phải”
Đề nghị đọc thêm tài liệu về kiểu dữ liệu đệ quy
Trang 839 Lập trình hàm trong C#
(giới thiệu)
• Các ngôn ngữ lập trình của Microsoft xuất hiện nhiều kỹ thuật mới, thay đổi về cú pháp, câu lệnh
• Cách viết các đoạn mã gọn hơn, dễ quản lý hơn
• Các dòng lệnh mang tính ước lượng giá trị của một biểu thức toán học hơn là việc thi hành một dãy lệnh
• Các thay đổi trên có nguồn góc từ lập trình hàm.
9.1 Giới thiệu:
• Lập trình hàm thể hiện rõ trong C# và F#
• Mỗi phiên bản của C# (C# 1.0, 2.0, 3.0) có sự cải thiện về FP
• Việc đưa FP vào NET cho phép:
+ Người lập trình tiết kiệm được thời gian trong coding+ Dễ phát hiện lỗi
+ Khai thác được sức mạng của các bộ xử lý đa nhân
Trang 84Func<intlist, intlist> Sort =
Biểu thức lambda
Trang 859.2 Lập trình hàm trong C# 2.0
Delegate (đại diện):
Từ C# 1.x, chỉ có khai báo delegate cho các hàm lớp đầu (First – class function)
IntPred predicate = new IntPred(point.Above);
Trong C# 2.0, một số kỹ thuật được bổ sung:
- Anonymous method
-Generics delegate
-Closure và Currying
Trang 86Anonymous method:
- Anonymous method trong C# 2.0 khắc phục những nhược điểm
của “delegate” trong các phiên bản C# 1
-Nó cho phép “inline code” giúp giảm thiểu số dòng lệnh hay các
hàm không cần thiết đôi khi chỉ gọi một lần
Ví dụ: Hàm f(x,y) = x+y
delegate int Demo(int x, int y);
public static int Exec(int x, int y)
lại giá trị của hàm f
Trang 87Generics delegate:
Trong các ngôn ngữ C, hàm f được mô tả dưới dạng: R f (D)
Trong đó: R: Kiểu trả lại của hàm, D: Kiểu tham số
Ví dụ: Với hàm lấy tổng 2 số nguyên f(x,y) = x+y
Khai báo đầu hàm:
int f(int, int)
Khai báo nội dung hàm:
int add(int x, int y) { return x + y; }
Generics delegate cho phép biến đổi về dạng mới “Func” Khi đó cú pháp trong ngôn ngữ lập trình trở thành:
Trang 88Là khả năng các đoạn mã bên trong delegate tham chiếu đến giá trị của những biến không nằm trong phạm vi khai báo của nó
delegate int Demo(int x);
static int ClosureDemo(int x)
Kết quả trả về của hàm “ClosureDemo(1)” sẽ không phải là “1+1= 2” mà sẽ là
“100” “myClosure” sẽ không lấy giá trị “y=1” mà nó chỉ tham chiếu đến biến “y” khi cần
Do đó nó đảm bảo giá trị mới nhất luôn được sử dụng.
Trang 89Ứng Dụng Closure giải quyết tranh chấp dữ liệu trong lập trình đa luồng:
Trong lập trình đa luồng xử lý đồng thời, người lập trình thường phải giải quyết những khó khăn khi phải dùng chung biến giữa các luồng
Ví dụ: Tìm kiếm tên trong một danh sách
Xét đoạn mã chương trình sau:
static string g_Name;
static List people = new List
{ new Person{ Name=”An”, Age=41, Salary = 500},
new Person{ Name=”Huy”, Age=26, Salary = 300},
new Person{ Name=”Thanh”, Age=30, Salary = 400}, };
static bool SearchName(Person p) { return p.Name.Equals(g_Name); }static List PersonListName(Person p) {
g_Name = p.Name;
return people.FindAll(SearchName);
}