lokalność zmiennych w skrypcie

Moderator: Inkwizytor

Awatar użytkownika
kondor
Posty: 1526
Rejestracja: 13 marca 2007, 11:50
ID Steam: Przemek_kondor
Kontakt:

lokalność zmiennych w skrypcie

Post autor: kondor »

Witam,
w pewnej misji w różnych skryptach często używam m.in. zmiennej lokalnej _side, która w różnych skryptach (wykonywanych niekiedy równolegle) ma różne zastosowanie.

Kod: Zaznacz cały

...
_side = _this select 1;
...
if( _side != (_this select 1) )then
{
  hint "uuuuu! zmienna _side zmienila swoja wartosc!";
};
Po testach okazało się, że _side zmienia swoją wartość gdzieś na zewnątrz i psuje mi powyższy skrypt
Do tej pory wydawało mi się, że zmienne z prefiksem "_" są lokalne tylko w skrypcie i nie są dzielone między różne skrypty.
Próbowałem "sprywatyzować" zmienną:

Kod: Zaznacz cały

private "_side";
przed jej pierwszym wywołaniem, lecz nic to nie zmieniło.
Pomogła dopiero zmiana nazwy na unikalną. Jednak jest to rozwiązanie conajmniej brzydkie (i wymaga sprawdzania czy takiej zmiennej już nie było).
1. Czy ktoś zna sposób jak zapobiec współdzieleniu takiej zmiennej?
2. Czy nie temu właśnie służy komenda "private"?

z góry dzięki za odpowiedź
Awatar użytkownika
aszek
Posty: 133
Rejestracja: 11 lipca 2008, 06:17
ID Steam:
Numer GG: 8568774
ID gracza: 0

Re: lokalność zmiennych w skrypcie

Post autor: aszek »

Zmienną którą tworzysz (przypisujesz jej wartość) w obszarze skryptu będzie miała zasięg w przestrzeni adresowej całej gry.

'private' zawęża zmieną (jej wartość) do lokalnej przestrzeni skryptu (jego wątku), choć odwołania do takiej zmiennej z poza skryptu działają, pod warunkiem, że skrypt nie zakończył jeszcze działanie (exit). 'private' powoduje zamazanie wartości zmiennej (coś jak polimorfizm w klasach).

'_' deklaruje zmienną tylko w lokalnej przestrzeni skryptu, i odwołania do niej z poza skryptu są niemożliwe (bez potrzeby zakończenia skryptu).

Innymi słowy, do momentu zakończenia działania skryptu zmienna 'private' jest widziana jako globalna, zaś '_' zawszę bedzie lokalna.

ad. 1
_zmienna

ad. 2
dopuki skrypt działa, zmianna 'private' nie jest niszczona i jest dostępna, odwołania do niej są możliwe (ale nie do jej wartości, która jest pokrywana - jest w tym samym miejscu w RAM - przez inną zminną o tej samej nazwie)
"True glory consists in doing what deserves to be written, in writing what deserves to be read." - Pliny The Elder
Awatar użytkownika
kondor
Posty: 1526
Rejestracja: 13 marca 2007, 11:50
ID Steam: Przemek_kondor
Kontakt:

Re: lokalność zmiennych w skrypcie

Post autor: kondor »

Dzięki,
napisałem jednak, że używałem zmiennej z podkreśleniem (_side), co jednak nie pozwoliło uniknąć współdzielenia tej zmiennej.
Przed chwilą wyczytałem:
The whole code in this script has access to the (local) variable, that includes also functions called within the script.
I zauważyłem, że mój skrypt wywołuje w międzyczasie inny skrypt (który wg powyższego różnież ma dostęp do zmiennych lokalnych "skrytpu-ojca") i również używa zmiennej lokalnej _side (i nadpisuje ją!). Tu był problem.
Innymi słowy, do momentu zakończenia działania skryptu zmienna 'private' jest widziana jako globalna, zaś '_' zawszę bedzie lokalna.
Skoro
biwiki o private pisze:Sets a variable to the innermost scope. The variable has to be local.
to jak może taka "prywatyzowana" zmienna być globalną?
odwołania do niej są możliwe (ale nie do jej wartości, która jest pokrywana - jest w tym samym miejscu w RAM - przez inną zminną o tej samej nazwie)
jeśli by nowa zmienna znajdowała się w tym samym miejscu w pamięci, to czy niemożliwe byłoby odzyskanie starej wartości?
Bo przecież mamy:

Kod: Zaznacz cały

_foo = 10;
if (true) then
{
    private ["_foo"];
    _foo = 5;    
};
player sideChat format ["%1", _foo];
wyświetli "10"
Awatar użytkownika
aszek
Posty: 133
Rejestracja: 11 lipca 2008, 06:17
ID Steam:
Numer GG: 8568774
ID gracza: 0

Re: lokalność zmiennych w skrypcie

Post autor: aszek »

Kiedy zmianna '_foo' została już powołana do życia, redeklaracja jej jako 'private' w tej samej przestrzeni wątku jest nieporozumieniem, bo '_' czyni z niej zmienną lokalną.

Sprawdź sobie coś takiego:

"testLocalA.sqs"

Kod: Zaznacz cały

_side = "A";
player sideChat _side;
@false;
exit;
"testLocalB.sqs"

Kod: Zaznacz cały

_side = "B";
player sideChat _side;
exit;

Kod: Zaznacz cały

this exec "testLocalA.sqs"; this exec "testLocalB.sqs"; 
i co ? A wiesz dlaczego ?


Kiedy Arma wywołuje skrypt tworzy dla niego wątek i dynamicznie przydziela pamięć na zmienne. Zmienne globalne jak każde inne są trzymane w strukturze, według zasięgu (wątku):

Kod: Zaznacz cały

type variant = record
{
    name :string;
    type  :byte; {"ARRAY", "BOOL", "CODE", "CONFIG", "CONTROL", "DISPLAY", "GROUP", "OBJECT", "SCALAR", "SCRIPT", "SIDE", "STRING", "TEXT"}
    value :variant;
}
type This = array of variant;
kiedy deklarujesz zmianną trafia ona do tablicy zmiennnych według zasięgu:

Kod: Zaznacz cały

This = This + [[name = "foo"; type = "SCALAR"; value = 10]]
Arma tworzy zmienną w takiej tablicy wariantów (pole 'value' może przybrać postać np: tekstu, liczby rzeczywistej). Do której zmiennej 'This' zostanie dodana Twoja zmienna ? - decyduje jej deklaracja.

'zmienna' trafia do globalnej tablicy.

'private' dodaje zmienną do tablicy zmiennych na stosie wątku który TWORZY zmienną. Jeśli w ramach wątku tworzysz wątek (przez skrypt) mu potomny, to skrypt potomny ma dostęp do takiej zmiennej. Jeśli skrypty są wywoływane odzielnie to nie dzielą przestrzeni adresowej, ani nazwy zmiennej 'private'.

'_' wstawia zmienną do loklalnej tablicy wątku w którym go tworzy.

kiedy odwołujesz się do zmiennej (przez nazwę), Arma sprawdza czy w tablicy variantów zmienna o tej nazwie istnieje i zwraca pole 'value' lub 'Null'. 'private' pozwala na dostęp do zmiennej w ramach tzw. enkapsulacji przesztrzeni adresowej (hierarhiczna i polimorficzna tablica variantów), która przechodzi przez dziedziczenie do wątków potomnych, więc deklarując zmienną jako 'private' w wątku potomnym zamazujesz jej wartość w wątku który do wywołał (this exec).
"True glory consists in doing what deserves to be written, in writing what deserves to be read." - Pliny The Elder
Awatar użytkownika
kondor
Posty: 1526
Rejestracja: 13 marca 2007, 11:50
ID Steam: Przemek_kondor
Kontakt:

Re: lokalność zmiennych w skrypcie

Post autor: kondor »

Ad "testLocalA.sqs" i "testLocalB.sqs" nie wiem po co to piszesz, skoro wiemy obaj co się wyświetli i mieliśmy niesprzeczną więdzę (może z wyjątkiem, że ja nie wiedziałem, że potomny skrypt dziedziczy przestrzeń nazw).
Przykład z deklaracją prywatnego _foo w bloku jest czysto teoretyczny i miał na celu uzmysłowić, iż zmienne _foo i to drugie _foo nie jest w tym samym miejscu w pamięci i nic więcej.

Ad "private"
więc deklarując zmienną jako 'private' w wątku potomnym zamazujesz jej wartość w wątku który do wywołał (this exec).
Czyli mam rozumieć, że
dla:
tato.sqf

Kod: Zaznacz cały

_val = 1;
call compile preprocessFile "potomek.sqf";
while{true}do
{
 hint format["%1", _val];
 sleep 1;
};
potomek.sqf:

Kod: Zaznacz cały

private "_val";
_val = 10;
wartość _val w tato.sqf zostanie zmieniona?
Awatar użytkownika
aszek
Posty: 133
Rejestracja: 11 lipca 2008, 06:17
ID Steam:
Numer GG: 8568774
ID gracza: 0

Re: lokalność zmiennych w skrypcie

Post autor: aszek »

Byłaby zmieniona jeśli w 'tato.sqf' zadeklarujesz:
private "val";

i w 'potomek.sqf' pokryjesz wcześniejszą deklarację:
private "val";

Tłumaczyłem przecież co się dzieje kiedy deklaracja jest lokalna '_'.

W Twoim przykładzie w 'tato.sqf' '_val' jest lokalna, a w 'potomek.sqf' 'private "_val"' jest prywatna.

Zdecyduj się, czy chcesz mieć: LOKALNĄ ('_') czy PRYWATNĄ ('private "zmienna"').

Deklaracja 'private _zmiena' impikuje tym, że masz tą samą zmienną w dwóch kopiach. Jedna w tablicy LOKALNEJ, a drugą w tablicy DZIEDZICZONEJ. Konstrukcja 'private _val' jest poprawna jeśli masz zamiar korzystać z takiej zmiennej w ramach skryptu potomka (chcesz pokryć jej wartość). Jeśli nie chcesz mieć 'przecieków' zmiennych o tej samej nazwie powinieneś deklarować ją jako '_val' (bez 'private').

Sprawdź sobie swoje skrypty pod kątem deklaracji 'private _zmienna' i zamień je na 'private ["zmienna"]', lub '_zmienna'. UNIKAJ deklaracji zmiennych jako 'private _zmienna', bo to powoduje problemy z dziedziczeniem (nadpisywaniem zmiennej) w skrypcie potomnym.
"True glory consists in doing what deserves to be written, in writing what deserves to be read." - Pliny The Elder
Awatar użytkownika
kondor
Posty: 1526
Rejestracja: 13 marca 2007, 11:50
ID Steam: Przemek_kondor
Kontakt:

Re: lokalność zmiennych w skrypcie

Post autor: kondor »

1.
Byłaby zmieniona jeśli w 'tato.sqf' zadeklarujesz:
private "_val";

i w 'potomek.sqf' pokryjesz wcześniejszą deklarację:
private "_val";
sprawdzam i nie widzę zmiany

2. Nie można użyć zmiennej nie będącą lokalną w komendzie private. To jest:

Kod: Zaznacz cały

private "zmienna";
spowoduje błąd
Awatar użytkownika
aszek
Posty: 133
Rejestracja: 11 lipca 2008, 06:17
ID Steam:
Numer GG: 8568774
ID gracza: 0

Re: lokalność zmiennych w skrypcie

Post autor: aszek »

ad1. ze jednego skryptu wywołaj skrypt potomny, a w skrypcie potomnym 'private' zamaże zmienną.

ad2. ależ można, bez cudzysłowów:

Kod: Zaznacz cały

private zmienna;
zmienna = "ala ma kota";
player sideChat zmienna;
"True glory consists in doing what deserves to be written, in writing what deserves to be read." - Pliny The Elder
Awatar użytkownika
kondor
Posty: 1526
Rejestracja: 13 marca 2007, 11:50
ID Steam: Przemek_kondor
Kontakt:

Re: lokalność zmiennych w skrypcie

Post autor: kondor »

chyba się nie dogadaliśmy:
aszek pisze:więc deklarując zmienną jako 'private' w wątku potomnym zamazujesz jej wartość w wątku który do wywołał (this exec).
kondor pisze:Czyli mam rozumieć, że (...) wartość _val w tato.sqf zostanie zmieniona?
aszek pisze:Byłaby zmieniona jeśli w 'tato.sqf' zadeklarujesz:
private "val";

i w 'potomek.sqf' pokryjesz wcześniejszą deklarację:
private "val";
kondor pisze:sprawdzam i nie widzę zmiany
aszek pisze:ze jednego skryptu wywołaj skrypt potomny, a w skrypcie potomnym 'private' zamaże zmienną.
z 1. Twojej wypowiedzi można wywnioskować, że wartość zmiennej zmieni się również w skrypcie ojcowskim, natomiast z ostatniej, że mówisz o zmianie w skrypcie potomnym (co jest i dla mnie oczywiste).
Więc, czy w 1. wypowiedzi (tu zacytowanej) miałeś na myśli, że zmienna zmieni swoją wartość w skrypcie ojcowskim (po zmianie w skrypcie potomnym), czy źle zrozumiałem/źle sformułowałeś zdanie?
Awatar użytkownika
aszek
Posty: 133
Rejestracja: 11 lipca 2008, 06:17
ID Steam:
Numer GG: 8568774
ID gracza: 0

Re: lokalność zmiennych w skrypcie

Post autor: aszek »

Jeśli wrócisz do początek wątku i spojrzysz raz jeszcze na problem i własne doświadczenia, to odpowiedź jest chyba oczywista.

Tak, zmienna 'private' jest nadpisywana w wątku potomnym i w skrypcie wywołującym (ojcowskim) taka zmienna zostanie zmieniona (globalnie).

Nieporozumienie bierze się z tąd, iż deklarujesz zmienne jako lokalno-prywatne. Taka zmienna w skrypcie ojcowskim zachowuje swoją wartość, dopuki w skrypcie potomnym nie zmienisz jej wartości (odwołania lokalne mają priorytet nad globalnymi).

deklaracja:
'private "_zmienna"'

nie jest substytutem deklaracji:
_zmienna

'private' puszcza zmienną w dół hierarchi wywołań (ale nie w górę - ponad wątek ją deklarujący, bo zmienna jest lokalna tyle że dziedziczona w dół).

Jak już sugerowałem (post pierwszy), aby tego uniknąć - użyj zmiennej lokalnej:

_zmienna

upewniwszy się, że nie używasz deklaracji 'private'.

chyba, że jednak chesz mieć wpływ w skrypcie potomnym na wartość zmiennej w procesie ojcowskim, wówczas użyj deklaracji 'private'.

private "zmienna",
"True glory consists in doing what deserves to be written, in writing what deserves to be read." - Pliny The Elder
Awatar użytkownika
kondor
Posty: 1526
Rejestracja: 13 marca 2007, 11:50
ID Steam: Przemek_kondor
Kontakt:

Re: lokalność zmiennych w skrypcie

Post autor: kondor »

Nieporozumienie bierze się z tąd, iż deklarujesz zmienne jako lokalno-prywatne. Taka zmienna w skrypcie ojcowskim zachowuje swoją wartość, dopuki w skrypcie potomnym nie zmienisz jej wartości (odwołania lokalne mają priorytet nad globalnymi).
właśnie dla lokalno - prywatnych działa dobrze, gdzie dobrze znaczy: w każdym skrypcie (mogą być wywoływane jedne w drugich) taka zmienna _side ma odpowiadać za coś innego.
Błąd mój brał się z tego, że użyłem private tylko w skrypcie ojcowskim.
Lokalność takiej zmiennej daje mi porządek (bo jestem pewny, że nie zostanie niepotrzebnie w pamięci oraz jej identyfikator wskazuje, że mam ją lokalnie traktować) i zgodność ze specyfikacją.
Prywatność takiej zmiennej daje niezależność między wywołaniami skryptów w skryptach.
W sumie we wszystkich przypadkach (z którymi się spotkałem), gdy podawane są przykłady private, używane są zmienne lokalne.
Zablokowany

Wróć do „Edytor - tworzenie misji, skrypty oraz programowanie”