Khởi tạo các Procedure sử dụng lambda – Thủ tục vô danh.

Trong việc sử dụng sum ở bài trước , có vẻ rất khó khi phải xác định các procedure ít quan trọng như pi-term và pi-next để chúng ta có thể sử dụng chúng làm đối số cho thủ tục bậc cao hơn. Thay vì định nghĩa pi-next và pi-term, sẽ thuận tiện hơn nếu có một cách để chỉ định trực tiếp “procedure trả về kết quả tăng + 4 với giá trị input đầu vào” và “thủ tục trả về nghịch đảo kết quả của input nhân với input + 2.” Chúng ta có thể đó bằng sử dụng biểu mẫu lambda đặc biệt để tạo ra các thủ tục

Sử dụng lambda

Sử dụng lambda chúng ta có thể diễn tả những gì mong muốn:

(lambda (x) (+ x 4))
vaf
(lambda (x) (/ 1.0 (* x (+ x 2))))

Sau đó procedure pi-sum có thể được trình bày mà không cần định nghĩa thêm bất kì thủ tục trợ giúp.

(define (pi-sum a b)
(sum (lambda (x) (/ 1.0 (* x (+ x 2))))
a
(lambda (x) (+ x 4))
b))

Với việc sử dụng lambda, chúng ta cũng có thể viết procedure tích phân không cần phải sử dụng thủ tục add-dx như sau:

(define (integral f a b dx)
(* (sum f
(+ a (/ dx 2.0))
(lambda (x) (+ x dx))
b)
dx))

Nhìn chung, lambda được sử dụng để tạo các procedure trong cách giống nhau như define, ngoại trừ không có cái tên cụ thể cho procedure.

(lambda (⟨formal-parameters⟩) ⟨body⟩)

Kết quả của procedure cũng giống như một procedure được tạo ra bằng cách sử dụng define. Sự khác biệt duy nhất là nó không được liên kết với bất kỳ tên nào trong môi trường. Trong thực tế

(define (plus4 x) (+ x 4)) tương đương với (define plus4 (lambda (x) (+ x 4)))

Chúng ta có thể đọc một biểu thức lambda như sau:

(lambda                   (x)    (+  x     4))
|                          |      |  |     |
procedure với một argument x nó cộng x và  4

Giống như bất kỳ biểu thức nào có giá trị là thủ tục, biểu thức lambda có thể được sử dụng làm toán tử trong một tổ hợp chẳng hạn như:

((lambda (x y z) (+ x y (square z)))
1 2 3)
12

hoặc nói chung hơn, trong bất kỳ ngữ cảnh nào mà chúng ta thường sử dụng tên thủ tục.

Sử dụng let để tạo ra các biến local

Another use of lambda is in creating local variables. We oen need local variables in our procedures other than those that have been bound
as formal parameters. For example, suppose we wish to compute the
function

Một hữu ích khác của lambda là tạo các biến cục bộ. Chúng ta thường cần các biến cục bộ trong các procedure của mình ngoài những biến đã bị ràng buộc như các tham số hình thức. Ví dụ: giả sử chúng ta muốn tính hàm

Chúng ta có thể trình bày như sau:

Khi viết thủ tục để tính hàm f, chúng ta cần các biến cục bộ không chỉ x và y mà còn tên của các biến trung gian như a và b. Một cách để đạt được điều này là sử dụng một thủ tục hỗ trợ để liên kết các biến cục bộ lại với nhau:

(define (f x y)
(define (f-helper a b)
(+ (* x (square a))
(* y b)
(* a b)))
(f-helper (+ 1 (* x y))
(- 1 y)))

Tất nhiên, chúng ta cũng có thể sử dụng biểu thức lambda để chỉ định một procedure vô danh nhằm liên kết các biến cục bộ. Phần thân của hàm f sau đó sẽ trở thành một lệnh đơn gọi tới thủ tục đó:

(define (f x y)
((lambda (a b)
(+ (* x (square a))
(* y b)
(* a b)))
(+ 1 (* x y))
(- 1 y)))

Cấu trúc này hữu ích đến mức có một dạng đặc biệt gọi là let để giúp việc sử dụng biến cục bộ thuận tiện hơn. Sử dụng let, thủ tục f có thể được viết lại như sau:

(define (f x y)
(let ((a (+ 1 (* x y)))
(b (- 1 y)))
(+ (* x (square a))
(* y b)
(* a b))))

Mẫu chung của một biểu thức let là

(let ((⟨var1⟩ ⟨exp1⟩)
      (⟨var2⟩ ⟨exp2⟩)
        . . .
      (⟨varn⟩ ⟨expn⟩))
       ⟨body⟩)

Điều đó cũng có ý nghĩa như là

let ⟨var1⟩ có giá trị ⟨exp1⟩ vaf
    ⟨var2⟩ có giá trị ⟨exp2⟩ và
      . . .
    ⟨varn⟩ có giá trị ⟨expn⟩
bên trong ⟨body⟩

Phần đầu tiên của biểu thức let là danh sách các cặp tên-biểu thức. Khi let được đánh giá, mỗi tên được liên kết với giá trị của biểu thức tương ứng. Phần thân của let được đánh giá bằng những biến này bị ràng buộc như các biến cục bộ. Cách điều này diễn ra là biểu thức let được trình thông dịch hiểu như một cú pháp thay thế cho lambda như sau:

((lambda (⟨var1⟩ . . . ⟨varn⟩)
         ⟨body⟩)
   ⟨exp1⟩
   . . .
   ⟨expn⟩)

Không cần cơ chế mới nào cho trình thông dịch để hỗ trợ các biến cục bộ. Biểu thức let chỉ đơn giản là cú pháp cho ứng dụng lambda cơ bản.

Từ sự tương đồng này, chúng ta có thể thấy rằng phạm vi của một biến được chỉ định bởi biểu thức let chính là phần thân của let. Điều này ngầm ý định rằng:

  • Ví dụ, nếu giá trị của x là 5 thì , giá trị của biểu thức (+ (let ((x 3))
    (+ x (* x 10))) x) là 38. Lý giải, x trong body của let là 3, giá trị của biểu thức let sẽ là 33. Mặc khác, x là đối số nằm phía ngoài cùng của toán tử + vẫn là 5. Nên giá trị của biểu thức là (+ 33 5) là 38.
  • Ví dụ: Nếu x là 2, thì biểu thức (let ((x 3) (y (+ x 2))) (* x y)) có gí trị là 12, vì bên trong body của let, x sẽ là 3 và y sẽ là 4 ( x bên ngoài let là 2).

Sử dụng các định nghĩa cục bộ bên trong define khác

Đôi khi chúng ta có thể sử dụng các định nghĩa nội bộ bên trong một define khác để đạt được hiệu quả tương tự như với let. Ví dụ: chúng ta có thể định nghĩa thủ tục f ở trên như sau:

(define (f x y)
(define a (+ 1 (* x y)))
(define b (- 1 y))
(+ (* x (square a))
(* y b)
(* a b)))

Tuy nhiên, chúng ta nên sử dụng let trong những trường hợp như thế này hơn và chỉ sử dụng define nội bộ cho các thủ tục nội bộ.

Bài viết tiếp theo: Procedures as General Methods

One thought on “Khởi tạo các Procedure sử dụng lambda – Thủ tục vô danh.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *