Khi intrreter Tcl nhập một thủ tục được viết bằng Tcl, nó tạo ra một bảng biến đặc biệt cục bộ cho thủ tục đó trong khi mã của nó đang được thực thi. Bảng này có thể chứa cả các biến cục bộ "thực" và "liên kết" đặc biệt cho các biến khác. Các liên kết này không thể phân biệt được với các biến "thực" miễn là các lệnh Tcl (chẳng hạn như set
, unset
v.v.) có liên quan.
Các liên kết này được tạo bởi lệnh upvar
và có thể tạo liên kết tới bất kỳ biến nào trên khung ngăn xếp (bao gồm phạm vi toàn cầu — khung 0).
Kể từ Tcl là rất năng động, các biến của nó có thể đến và đi bất cứ lúc nào và do đó, một biến liên quan đến bằng cách upvar
có thể không tồn tại đồng thời liên kết với nó được tạo ra, quan sát:
% unset foo
can't unset "foo": no such variable
% proc test name { upvar 1 $name v; set v bar }
% test foo
bar
% set foo
bar
Lưu ý rằng Lần đầu tiên tôi chứng minh rằng biến có tên "foo" không tồn tại, sau đó đặt biến trong thủ tục sử dụng upvar
(và biến được tự động xử lý) và sau đó chứng minh rằng biến tồn tại sau khi thủ tục đã thoát.
Cũng lưu ý rằng upvar
không phải là về truy cập biến toàn cầu — điều này thường đạt được bằng cách sử dụng các lệnh global
và variable
; thay vào đó, upvar
được sử dụng để làm việc với các biến số thay vì giá trị . Điều này thường là cần thiết khi chúng ta cần phải thay đổi một cái gì đó "tại chỗ"; một trong những ví dụ tốt hơn về điều này là lệnh lappend
chấp nhận tên của một biến có chứa danh sách và nối thêm một hoặc nhiều phần tử vào danh sách đó, thay đổi nó tại chỗ. Để đạt được điều này, chúng tôi vượt qua lappend
tên của một biến, không chỉ là giá trị danh sách. Bây giờ, hãy so sánh điều này với lệnh linsert
chấp nhận giá trị, không phải biến, do đó, cần có danh sách và tạo một danh sách khác.
Một điều cần lưu ý là theo mặc định (trong biểu mẫu hai đối số), upvar
liên kết đến biến có tên được chỉ định một cấp lên ngăn xếp, không đến biến toàn cầu. Ý tôi là, bạn có thể làm điều này:
proc foo {name value} {
upvar $name v
set v $value
}
proc bar {} {
set x ""
foo x test
puts $x ;# will print "test"
}
Trong ví dụ này, các thủ tục "foo" thay đổi biến cục bộ thủ tục "thanh".
Do đó, để làm cho mục đích rõ ràng hơn, nhiều người thích luôn chỉ định số khung ngăn xếp upvar
nên "trèo lên", như trong upvar 1 $varName v
tương tự như upvar $varName v
nhưng rõ ràng hơn.
Một ứng dụng hữu ích của việc này là đề cập đến biến địa phương, bằng cách xác định zero mức ngăn xếp để leo lên — thủ thuật này đôi khi hữu ích để thuận tiện hơn truy cập các biến trong mảng:
proc foo {} {
set a(some_long_name) test
upvar 0 a(some_long_name) v
puts $v ;# prints "test"
upvar a(some_even_more_long_name) x
if {![info exists x]} {
set x bar
}
}
Như một phần thưởng, lưu ý rằng upvar
cũng hiểu số lượng tuyệt đối của khung ngăn xếp được chỉ định bằng tiền tố "#" và "# 0" có nghĩa là phạm vi toàn cầu. Bằng cách đó, bạn có thể liên kết với một biến toàn cục trong khi quy trình trong ví dụ ban đầu của bạn sẽ chỉ liên kết với các biến chung nếu được thực hiện trong phạm vi toàn cục.
nếu name1 và name2 là các tham số đơn giản, sau đó trong chế độ thuần hóa proc, upvar có nghĩa là gì? ý nghĩa tương tự như "set name1 Ronaldo"? –
Xin chào! "upvar $ name1 Ronalod" theo nghĩa đen là tạo ra một biến có phạm vi cục bộ được gọi là Ronalod, được liên kết với biến được tham chiếu bởi $ name1. Sau đó, bất kỳ thay đổi nào đối với Ronalod cũng sẽ thay đổi biến được tham chiếu bởi $ name1. Bạn có thể thấy rằng trong ví dụ của tôi, giá trị của "cuối cùng" đã được thay đổi bởi tập Dom "Dom". – TrojanName
cảm ơn bạn, nhưng trong mã của tôi, không có biến toàn cầu name1, name2, vì vậy nếu bạn đặt Dom "Dom", có nghĩa là biến Dom được thay đổi và biến được tham chiếu bởi $ name1 cũng được thay đổi, nhưng trong trường hợp của tôi, biến được tham chiếu bởi $ name1 không tồn tại? –