Một trong những khác biệt cốt lõi giữa PHẠM VI TĨNH (static/lexical scope) và phạm vi động là: một hàm ẩn danh được định nghĩa (closure) sẽ luôn phân giải các biến tự do theo chuỗi phạm vi tại VỊ TRÍ NÓ ĐƯỢC ĐỊNH NGHĨA trong mã nguồn, hoàn toàn không phụ thuộc vào việc sau này nó được GỌI từ đâu.
Trình biên dịch (giả lập) xử lý tuần tự các lệnh:
BEGIN: mở một phạm vi con mới, lồng vào phạm vi hiện tại.END: đóng phạm vi hiện tại, quay về phạm vi cha (phạm vi toàn cục không bao giờ bị đóng).DECL <ten> <giatri>: khai báo biến <ten> với giá trị nguyên <giatri> trong phạm vi hiện tại. Nếu <ten> đã tồn tại trong CHÍNH phạm vi hiện tại, giá trị mới sẽ GHI ĐÈ giá trị cũ (biến này, và mọi closure đã trỏ tới đúng phạm vi này, đều sẽ thấy giá trị MỚI).DEFCLOSURE <id>: định nghĩa một closure định danh bởi <id> (một chuỗi không chứa khoảng trắng) NGAY TẠI phạm vi hiện tại — closure này ghi nhớ (bằng tham chiếu, không phải bản sao) toàn bộ chuỗi phạm vi hiện tại (phạm vi hiện tại và tất cả phạm vi cha, ông, ... của nó tại đúng thời điểm này). Dữ liệu đảm bảo mỗi <id> chỉ được định nghĩa (bằng DEFCLOSURE) đúng một lần trong suốt chương trình.CALLCLOSURE <id> <ten>: gọi closure <id> để tham chiếu biến <ten>. Việc phân giải <ten> phải DÙNG CHUỖI PHẠM VI ĐÃ GHI NHỚ của closure <id> (tìm trong phạm vi mà DEFCLOSURE <id> từng đứng, rồi ra dần các phạm vi cha của NÓ) — hoàn toàn bỏ qua phạm vi nào đang hiện hành tại thời điểm gọi CALLCLOSURE này. In ra giá trị nguyên tìm được, hoặc UNDEFINED nếu không tìm thấy <ten> ở bất kỳ phạm vi nào trong chuỗi đã ghi nhớ. Dữ liệu đảm bảo <id> được dùng trong CALLCLOSURE luôn đã được định nghĩa trước đó bằng DEFCLOSURE.Lưu ý quan trọng: một phạm vi được closure ghi nhớ vẫn "sống" (có thể được truy vấn) ngay cả sau khi nó đã bị END đóng lại từ lâu — miễn là còn ít nhất một closure đang tham chiếu tới nó.
Ví dụ 1 (một phạm vi bị đóng nhưng vẫn được closure giữ lại, tách biệt với phạm vi anh em ra đời sau):
9
BEGIN
DECL x 5
DEFCLOSURE cA
END
BEGIN
DECL x 99
CALLCLOSURE cA x
END
CALLCLOSURE cA x
Kết quả:
5
5
(dù phạm vi con thứ hai cũng khai báo x = 99, nó là một phạm vi HOÀN TOÀN KHÁC với phạm vi mà cA đã ghi nhớ, nên không ảnh hưởng.)
Ví dụ 2 (ghi đè biến ngay trong chính phạm vi mà closure đã ghi nhớ, trước khi phạm vi đó đóng lại):
4
DECL x 10
DEFCLOSURE c1
DECL x 20
CALLCLOSURE c1 x
Kết quả: 20 (vì c1 ghi nhớ CHÍNH phạm vi toàn cục bằng tham chiếu; lệnh DECL x 20 sau đó ghi đè giá trị của x ngay trong phạm vi đó, nên khi gọi CALLCLOSURE c1 x, giá trị mới nhất 20 được trả về, không phải 10 tại thời điểm định nghĩa closure).
Dòng đầu chứa số nguyên n (0≤n≤3000) — số lệnh. n dòng tiếp theo, mỗi dòng là một trong bốn dạng: BEGIN, END, DECL <ten> <giatri> (<giatri> là số nguyên, có thể âm), DEFCLOSURE <id>, CALLCLOSURE <id> <ten>. Dữ liệu đảm bảo số lệnh END không vượt quá số BEGIN chưa đóng tương ứng, mỗi <id> chỉ xuất hiện trong đúng một lệnh DEFCLOSURE, và mọi <id> dùng trong CALLCLOSURE đã được định nghĩa trước đó.
Với mỗi lệnh CALLCLOSURE, in một dòng chứa giá trị nguyên tìm được hoặc chuỗi UNDEFINED, theo đúng thứ tự xuất hiện. Nếu không có lệnh CALLCLOSURE nào, không in gì.
Ví dụ:
Đầu vào:
0
Đầu ra:
Đầu vào:
4
DECL x 10
DEFCLOSURE c1
DECL x 20
CALLCLOSURE c1 x
Đầu ra:
20
Đang tải editor...