Dưới đây let
, tất cả các biểu thức khởi tạo biến đều nhìn thấy chính xác cùng một môi trường từ vựng: môi trường bao quanh let
. Nếu những biểu thức đó xảy ra để nắm bắt các bao đóng từ vựng, tất cả chúng có thể chia sẻ cùng một đối tượng môi trường.
Dưới đây let*
, mọi biểu thức khởi tạo đều nằm trong một môi trường khác nhau. Đối với mỗi biểu thức kế tiếp, môi trường phải được mở rộng để tạo ra một biểu thức mới. Ít nhất trong ngữ nghĩa trừu tượng, nếu các bao đóng được nắm bắt, chúng có các đối tượng môi trường khác nhau.
A let*
phải được tối ưu hóa tốt để thu gọn các phần mở rộng môi trường không cần thiết để phù hợp như một sự thay thế hàng ngày cho let
. Phải có một trình biên dịch hoạt động mà các biểu mẫu đang truy cập những gì và sau đó chuyển đổi tất cả các biểu mẫu độc lập thành lớn hơn, kết hợp let
.
(Điều này đúng ngay cả khi let*
chỉ là một toán tử macro phát ra các let
dạng xếp tầng ; việc tối ưu hóa được thực hiện trên các dạng xếp tầng let
đó).
Bạn không thể triển khai let*
như một người đơn thuần let
, với các phép gán biến ẩn để thực hiện khởi tạo vì việc thiếu phạm vi phù hợp sẽ được tiết lộ:
(let* ((a (+ 2 b))
(b (+ 3 a)))
forms)
Nếu điều này được biến thành
(let (a b)
(setf a (+ 2 b)
b (+ 3 a))
forms)
nó sẽ không hoạt động trong trường hợp này; bên trong phủ b
bóng bên ngoài, b
vì vậy chúng tôi kết thúc thêm 2 vào nil
. Loại biến đổi này có thể được thực hiện nếu chúng ta đổi tên alpha cho tất cả các biến này. Môi trường sau đó được làm phẳng một cách độc đáo:
(let (#:g01 #:g02)
(setf #:g01 (+ 2 b)
#:g02 (+ 3 #:g01))
alpha-renamed-forms)
Vì vậy, chúng tôi cần xem xét hỗ trợ gỡ lỗi; nếu lập trình viên bước vào phạm vi từ vựng này bằng trình gỡ lỗi, chúng tôi có muốn họ xử lý #:g01
thay vì a
.
Về cơ bản, let*
cấu trúc phức tạp phải được tối ưu hóa tốt để thực hiện cũng như let
trong các trường hợp khi nó có thể giảm xuống let
.
Điều đó một mình sẽ không biện minh cho sự ưu ái let
hơn let*
. Giả sử chúng ta có một trình biên dịch tốt; tại sao không sử dụng let*
tất cả các thời gian?
Về nguyên tắc chung, chúng ta nên ưu tiên các cấu trúc cấp cao hơn giúp chúng ta làm việc hiệu quả và giảm sai sót, hơn các cấu trúc cấp thấp dễ mắc lỗi và dựa nhiều nhất có thể vào việc triển khai tốt các cấu trúc cấp cao hơn để chúng ta hiếm khi phải hy sinh sử dụng chúng vì lợi ích của hiệu suất. Đó là lý do tại sao chúng tôi đang làm việc bằng một ngôn ngữ như Lisp ngay từ đầu.
Suy luận đó không áp dụng cho let
so với so với let*
, bởi vì let*
rõ ràng không phải là mức trừu tượng cao hơn so với let
. Họ về "đẳng cấp ngang hàng". Với let*
, bạn có thể giới thiệu một lỗi được giải quyết bằng cách chuyển sang let
. Và ngược lại . let*
thực sự chỉ là một đường cú pháp nhẹ để let
làm tổ thu gọn trực quan , và không phải là một sự trừu tượng mới đáng kể.