Tầm vực (khoa học máy tính)

Trong lập trình máy tính, tầm vực (tiếng Anh: "scope") của một ràng buộc tên – liên kết tên với một thực thể, ví dụ như biến – là một vùng của chương trình máy tính nơi liên kết hợp lệ: khi tên có thể được dùng để chỉ đến thực thể. Một khu vực như vậy được gọi là ("scope block"). Trong các phần khác của chương trình, tên có thể chỉ để các thực thể khác (nó có thể có ràng buộc khác), hay không có gì cả (nó có thể không bị ràng buộc).
Phạm vi của một ràng buộc còn được gọi là khả năng hiển thị ("visibility") của một thực thể, đặc biệt trong các tài liệu kỹ thuật cũ hơn – điều này xuất phát từ quan điểm thực thể được tham chiếu, chứ không phải tên tham chiếu. Tầm vực là một phần của chương trình hoặc có thể là tầm vực cho một tập hợp các ràng buộc – một định nghĩa chính xác rất khó, nhưng trong trường hợp thông thường và trong thực tế thường tương ứng với một khối, một hàm, hay một tập tin, tùy thuộc vào ngôn ngữ và loại của thực thể. Thuật ngữ "tầm vực" cũng được sử dụng để chỉ tập hợp "tất cả" các thực thể có thể thấy được hay tên hợp lệ trong một phần của chương trình hoặc tại một thời điểm nhất định trong chương trình, mà thường được gọi chính xác hơn là "ngữ cảnh" ("context") hay "môi trường" ("environment").
Nói một cách chặt chẽ hơn và trong thực tế với hầu hết các ngôn ngữ lập trình, "một phần của chương trình" đề cập đến "một phần của mã nguồn (khu vực văn bản)", và được gọi là tầm vực từ vựng ("lexical scope"). Tuy nhiên trong một số ngôn ngữ, "một phần chương trình" đề cập đến "một phần của thời gian chạy (khoảng thời gian trong khi thực thi)", và được gọi là tầm vực động ("dynamic scope"). Cả hai thuật ngữ đó đều có chút sai lệch – chúng sử dụng sai thuật ngữ kỹ thuật, như đã thảo luận trong phần định nghĩa – nhưng bản thân sự khác biệt là chính xác, và đó là những thuật ngữ tương ứng tiêu chuẩn. Tầm vực từ vựng là trọng tâm của bài viết này, với tầm vực động được hiểu là ngược lại với tầm vực từ vựng.
Trong hầu hết các trường hợp, phân giải tên ("name resolution") dựa trên tầm vực từ vựng tương đối đơn giản để sử dụng và hiện thực, vì khi sử dụng người ta có thể đọc ngược mã nguồn để xác định thực thể nào mà tên chỉ đến, và khi hiện thực người ta có thể duy trì một danh sách các tên và ngữ cảnh khi biên dịch hay thông dịch một chương trình. Khó khăn phát sinh khi name masking, forward declaration, và hoisting, trong khi những khó khăn đáng kể phát sinh với biến phi cục bộ, đặc biệt trong bao đóng.
Các cấp độ của tầm vực.
Tầm vực có thể thay đổi từ mức độ ít nhất như một biểu thức đơn cho đến nhiều nhất là toàn bộ chương trình, với nhiều mức độ ở giữa. Quy tắc tầm vực đơn giản nhất là tầm vực toàn cục (global scope) – tất cả các thực thể đều được hiển thị (visible) trong toàn bộ chương trình. Quy tắc tầm vực mô đun cơ bản nhất là tầm vực hai cấp, với một tầm vực toàn cục trong chương trình, và tầm vực cục bộ (local scope) trong một hàm. Lập trình mô đun phức tạp hơn cho phép một tầm vực mô đun tách rời, trong đó tên được hiển thị trong mô đun (riêng tư (private) với mô đun) nhưng không hiển thị bên ngoài mô đun đó. Với hàm, trong một số ngôn ngữ như C, cho phép tầm vực khối (block scope) để giới hạn tầm vực đối với môtj tập con của một hàm; còn với một số ngôn ngữ lập trình chức năng đáng chú ý khác, lại cho phép tầm vực biểu thức (expression scope), để giới hạn tầm vực cho một biểu thức đơn. Các tầm vực khác bao gồm tầm vực tập tin (file scope) (đáng chú ý trong C), có chức năng tương tự như tầm vực mô đun, và tầm vực khối ở bên ngoài của hàm (đáng chú ý trong Perl).
Một vấn đề khó phân biệt chính xác là khi nào một tầm vực bắt đầu và kết thúc. Trong một số ngôn ngữ như C, một tầm vực bắt đầu khi khai báo, và do vậy các tên khác nhau được khai báo trong một khối nhất định có thể có tầm vực khác nhau. Điều này đòi hỏi phải khai báo hàm trước khi dùng, mặc dù không nhất thiết phải định nghĩa chúng, và yêu cầu tiền khai báo (forward declaration) trong một số trường hợp, đáng chú ý trong đệ quy hỗ tương (mutual recursion). Trong các ngôn ngữ khác, như JavaScript hay Python, tầm vực của tên bắt đầu khi có khối liên quan (như bắt đầu của một hàm), bất kể nó được định nghĩa ở đâu, và tất cả tên trong một khối nhất định có cùng tầm vực; trong JavaScript điều này được gọi là "variable hoisting". Tuy nhiên, khi tên bị ràng buộc với một giác khi khác nhau, và hành vi của các tên trong ngữ cảnh có giá trị không được xác định (undefined variable) khác nhau: trong Python việc sử dụng biến không xác định sẽ gây ra lỗi lúc chạy (runtime error), trong khi với JavaScript biến không xác định cón thể sử dụng được (với giá trị không xác định), nhưng định nghĩa hàm cũng được kéo lên đỉnh của hàm chứa và có thể sử dụng trong toàn hàm.