Szukaj…


Uwagi

Kolejną korzyścią wynikającą ze stosowania wzmocnionych ciągów wyrażeń jest to, że kompilator bajtów zwykle może generować z nich bardziej wydajny kod (5 - 10 razy szybszy).

Problemy z wyrażeniami nieskrępowanymi

Dobrą praktyką jest dostarczanie argumentów ciągu wyrażeń jako łańcuchów stężonych. Nagłówek „Podwójna zamiana” przedstawia ważne przyczyny tego samego.

Komenda expr ocenia łańcuch wyrażeń oparty na operatorze, aby obliczyć wartość. Ciąg ten jest zbudowany z argumentów w wywołaniu.

expr 1 + 2    ; # three arguments
expr "1 + 2"  ; # one argument
expr {1 + 2}  ; # one argument

Te trzy wywołania są równoważne, a ciąg wyrażeń jest taki sam.

Polecenia if , for i while używają tego samego kodu oceniającego jako argumentu warunku:

if {$x > 0} ...
for ... {$x > 0} ... ...
while {$x > 0} ...

Główną różnicą jest to, że ciąg wyrażenia warunkowego musi zawsze być pojedynczym argumentem.

Jak w przypadku każdego argumentu w wywołaniu polecenia w Tcl, treść może, ale nie musi, zostać podstawiona, w zależności od tego, w jaki sposób są one cytowane / zmienione:

set a 1
set b 2
expr $a + $b   ; # expression string is {1 + 2}
expr "$a + $b" ; # expression string is {1 + 2}
expr \$a + \$b ; # expression string is {$a + $b}
expr {$a + $b} ; # expression string is {$a + $b}

Istnieje różnica w trzecim i czwartym przypadku, ponieważ ukośniki odwrotne / nawiasy klamrowe zapobiegają zamianie. Wynik jest nadal taki sam, ponieważ ewaluator wewnątrz expr może sam wykonać podstawienie zmiennej Tcl i przekształcić ciąg znaków na {1 + 2} .

set a 1
set b "+ 2"
expr $a $b   ; # expression string is {1 + 2}
expr "$a $b" ; # expression string is {1 + 2}
expr {$a $b} ; # expression string is {$a $b}: FAIL!

Tutaj mamy problem z argumentem usztywnionym: gdy ewaluator w expr wykonuje podstawienia, łańcuch wyrażeń został już parsowany na operatory i operandy, więc to, co oceniający widzi, jest łańcuchem złożonym z dwóch operandów bez operatora między nimi. (Komunikat o błędzie to „ missing operator at _@_ in expression "$a _@_$b" ”.)

W takim przypadku zastąpienie zmiennej przed expr zapobiegło błędowi. Zbieranie argumentu zapobiegało podstawianiu zmiennych aż do oceny wyrażenia, co spowodowało błąd.

Mogą wystąpić takie sytuacje, najczęściej gdy wyrażenie do oceny jest przekazywane jako zmienna lub parametr. W takich przypadkach nie ma innego wyjścia, jak pozostawić argument bez zmian, aby umożliwić analizatorowi argumentów „rozpakowanie” ciągu wyrażeń w celu dostarczenia do expr .

Jednak w większości innych przypadków wzmocnienie wyrażenia nie szkodzi i rzeczywiście może zapobiec wielu problemom. Oto kilka przykładów:

Podwójna zamiana

set a {[exec make computer go boom]}
expr $a      ; # expression string is {[exec make computer go boom]}
expr {$a}    ; # expression string is {$a}

Nieskrępowana forma spowoduje zastąpienie polecenia, które w jakiś sposób niszczy komputer (albo szyfruje lub formatuje dysk twardy, albo co masz). Usztywniona forma wykona zmienne podstawienie, a następnie spróbuje (i nie powiedzie się) stworzyć coś z ciągu „[exec make komputer go boom]”. Katastrofa odwrócona.

Niekończące się pętle

set i 10
while "$i > 0" {puts [incr i -1]}

Ten problem dotyczy zarówno for jakiś czas, jak i na while . Chociaż wydaje się, że ta pętla odliczałaby do zera i kończyła działanie, argument warunku do while w rzeczywistości zawsze wynosi 10>0 ponieważ właśnie taki argument był oceniany while aktywacji polecenia while . Gdy argument jest zbrojony, jest przekazywany do komendy while jako $i>0 , a zmienna zostanie podstawiona raz dla każdej iteracji. Użyj tego zamiast:

while {$i > 0} {puts [incr i -1]}

Ogólna ocena

set a 1
if "$a == 0 && [incr a]" {puts abc}

Jaka jest wartość po uruchomieniu tego kodu? a Ponieważ operator && ocenia prawy operand tylko wtedy, gdy lewy operand jest prawdziwy, wartość powinna nadal wynosić 1. Ale tak naprawdę to 2. Jest tak, ponieważ ewaluator argumentów wykonał już wszystkie podstawienia zmiennych i poleceń do czasu, gdy łańcuch wyrażeń jest ocenione. Użyj tego zamiast:

if {$a == 0 && [incr a]} {puts abc}

Zdefiniowano kilka operatorów (łączniki logiczne || i && oraz operator warunkowy ?: && , Aby nie oceniały wszystkich swoich operandów, ale mogą działać zgodnie z przeznaczeniem, jeśli łańcuch wyrażeń jest wzmocniony.

Mnożenie zmiennej przez 17

set myVariable [expr { $myVariable * 17 }]

To pokazuje, jak można użyć prostego wyrażenia do zaktualizowania zmiennej. Polecenie expr nie aktualizuje zmiennej; musisz wziąć jego wynik i zapisać go do zmiennej z set .

Pamiętaj, że znaki nowego wiersza nie są ważne w małym języku rozumianym przez expr , a dodanie ich może znacznie ułatwić czytanie dłuższych wyrażeń.

set myVariable [expr {
    $myVariable * 17
}]

To robi dokładnie to samo.

Wywoływanie komendy Tcl z wyrażenia

Czasami musisz wywołać komendę Tcl ze swojego wyrażenia. Załóżmy na przykład, że potrzebujesz w nim długości łańcucha. Aby to zrobić, po prostu użyj sekwencji [...] w wyrażeniu:

set halfTheStringLength [expr { [string length $theString] / 2 }]

Możesz wywołać dowolne polecenie Tcl w ten sposób, ale jeśli okaże się, że sam wywołujesz expr , przestań! i pomyśl, czy naprawdę potrzebujesz tego dodatkowego połączenia. Zwykle można to zrobić dobrze, umieszczając wyrażenie wewnętrzne w nawiasach.

Nieprawidłowy błąd bez słów

W samym Tcl ciąg znaków składający się z jednego słowa nie musi być cytowany. W języku łańcuchów wyrażeń, który ocenia expr , wszystkie operandy muszą mieć możliwy do zidentyfikowania typ.

Operandy numeryczne są zapisywane bez dekoracji:

expr {455682 / 1.96e4}

Podobnie stałe logiczne:

expr {true && !false}

Rozpoznawana jest składnia podstawienia zmiennej Tcl: operand zostanie ustawiony na wartość zmiennej:

expr {2 * $alpha}

To samo dotyczy zastępowania poleceń:

expr {[llength $alpha] > 0}

Operandy mogą być również wywołaniami funkcji matematycznych, z oddzieloną przecinkami listą operandów w nawiasach:

expr {sin($alpha)}

Operand może być łańcuchem podwójnym lub podwójnym. Ciąg cudzysłowu będzie podlegał podstawieniu tak jak w wierszu poleceń.

expr {"abc" < {def}}

Jeśli operand nie jest jednym z powyższych, jest nielegalny. Ponieważ nie ma podpowiedzi, która pokazuje, jakie to słowo, expr oznacza błąd bez słowa.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow