<<-
jest najbardziej przydatna w połączeniu z zamknięciami w celu utrzymania stanu. Oto sekcja z mojej ostatniej pracy:
Zamknięcie to funkcja zapisana przez inną funkcję. Zamknięcia są tak nazywane, ponieważ obejmują środowisko funkcji nadrzędnej i mają dostęp do wszystkich zmiennych i parametrów tej funkcji. Jest to przydatne, ponieważ pozwala nam mieć dwa poziomy parametrów. Jeden poziom parametrów (rodzic) kontroluje działanie funkcji. Drugi poziom (dziecko) wykonuje pracę. Poniższy przykład pokazuje, jak można wykorzystać ten pomysł do wygenerowania rodziny funkcji potęgowych. Funkcja nadrzędna ( power
) tworzy funkcje potomne ( square
i cube
), które faktycznie wykonują ciężką pracę.
power <- function(exponent) {
function(x) x ^ exponent
}
square <- power(2)
square(2) # -> [1] 4
square(4) # -> [1] 16
cube <- power(3)
cube(2) # -> [1] 8
cube(4) # -> [1] 64
Możliwość zarządzania zmiennymi na dwóch poziomach umożliwia również utrzymanie stanu między wywołaniami funkcji, umożliwiając funkcji modyfikowanie zmiennych w środowisku jej rodzica. Kluczem do zarządzania zmiennymi na różnych poziomach jest operator przypisania podwójnej strzałki <<-
. W przeciwieństwie do zwykłego przypisania pojedynczej strzałki ( <-
), które zawsze działa na bieżącym poziomie, operator podwójnej strzałki może modyfikować zmienne na poziomach nadrzędnych.
Umożliwia to utrzymanie licznika, który rejestruje, ile razy funkcja została wywołana, jak pokazano w poniższym przykładzie. Za każdym razem, gdy new_counter
jest uruchamiany, tworzy środowisko, inicjuje licznik i
w tym środowisku, a następnie tworzy nową funkcję.
new_counter <- function() {
i <- 0
function() {
# do something useful, then ...
i <<- i + 1
i
}
}
Nowa funkcja to zamknięcie, a jej otoczenie to otaczające środowisko. Kiedy zamknięcia counter_one
i counter_two
są uruchamiane, każdy z nich modyfikuje licznik w swoim otaczającym środowisku, a następnie zwraca bieżącą liczbę.
counter_one <- new_counter()
counter_two <- new_counter()
counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1
Dobrze jest myśleć o tym
<<-
jako o ekwiwalencieassign
(jeśli ustawiszinherits
parametr w tej funkcji naTRUE
). Korzyściąassign
jest to, że pozwala na określenie większej liczby parametrów (np środowisko), więc wolę używaćassign
ciągu<<-
w większości przypadków.Użycie
<<-
iassign(x, value, inherits=TRUE)
oznacza, że „otaczające środowiska podanego środowiska są przeszukiwane do momentu napotkania zmiennej 'x'”. Innymi słowy, będzie przechodzić przez środowiska po kolei, dopóki nie znajdzie zmiennej o tej nazwie i przypisze ją do niej. Może to być w zakresie funkcji lub w środowisku globalnym.Aby zrozumieć, do czego służą te funkcje, musisz również zrozumieć środowiska języka R (np. Używanie
search
).Regularnie używam tych funkcji, gdy prowadzę dużą symulację i chcę zapisać wyniki pośrednie. Pozwala to na utworzenie obiektu poza zakresem danej funkcji lub
apply
pętli. Jest to bardzo pomocne, szczególnie jeśli masz jakiekolwiek obawy, że duża pętla zakończy się nieoczekiwanie (np. Rozłączenie bazy danych), w którym to przypadku możesz stracić wszystko w procesie. Byłoby to równoważne z zapisywaniem wyników do bazy danych lub pliku podczas długotrwałego procesu, z wyjątkiem tego, że zamiast tego przechowuje wyniki w środowisku języka R.Moje podstawowe ostrzeżenie: bądź ostrożny, ponieważ teraz pracujesz ze zmiennymi globalnymi, zwłaszcza gdy używasz
<<-
. Oznacza to, że możesz skończyć z sytuacjami, w których funkcja używa wartości obiektu ze środowiska, gdy spodziewasz się, że używa wartości, która została dostarczona jako parametr. Jest to jedna z głównych rzeczy, których programowanie funkcjonalne stara się unikać (patrz efekty uboczne ). Unikam tego problemu, przypisując moje wartości do unikalnych nazw zmiennych (za pomocą wklejania z ustawionymi lub unikalnymi parametrami), które nigdy nie są używane w funkcji, ale są używane tylko do buforowania i na wypadek, gdybym musiał później odzyskać (lub zrobić kilka meta -analiza wyników pośrednich).źródło
Jednym miejscem, w którym korzystałem,
<<-
były proste GUI używające tcl / tk. Niektóre z początkowych przykładów mają to - ponieważ musisz dokonać rozróżnienia między zmiennymi lokalnymi i globalnymi dla zachowania stanu. Zobacz na przykładktóry używa
<<-
. Inaczej zgadzam się z Markiem :) - może pomóc wyszukiwarka Google.źródło
tkdensity
w R 3.6.0.źródło
<<-
. W tym przypadku pętla for byłaby wyraźniejsza.W związku z tym chciałbym zwrócić uwagę, że
<<-
operator będzie zachowywał się dziwnie, gdy zostanie zastosowany (nieprawidłowo) w pętli for (mogą być też inne przypadki). Biorąc pod uwagę następujący kod:można by się spodziewać, że funkcja zwróci oczekiwaną sumę, 6, ale zamiast tego zwraca 0, z utworzoną zmienną globalną
mySum
i przypisaną jej wartością 3. Nie mogę w pełni wyjaśnić, co się tutaj dzieje, ale z pewnością treść funkcji for pętla nie jest nowym „poziomem” zakresu. Zamiast tego wydaje się, że R wygląda pozafortest
funkcją, nie może znaleźćmySum
zmiennej do przypisania, więc tworzy ją i przypisuje wartość 1, za pierwszym razem w pętli. W kolejnych iteracjach RHS w przypisaniu musi odnosić się do (niezmienionej)mySum
zmiennej wewnętrznej , podczas gdy LHS odnosi się do zmiennej globalnej. Dlatego każda iteracja nadpisuje wartość zmiennej globalnej na wartość tej iteracjii
, stąd przy wyjściu z funkcji ma wartość 3.Mam nadzieję, że to komuś pomoże - dzisiaj zdumiało mnie to na kilka godzin! (BTW, po prostu wymień
<<-
z<-
utworów, a funkcja zgodnie z oczekiwaniami).źródło
mySum
nigdy nie jest zwiększana, ale tylko globalnamySum
. Stąd w każdej iteracji pętli for, globalnymySum
otrzymuje wartość0 + i
. Możesz to śledzić za pomocądebug(fortest)
.<-
wszędzie konsekwentnie w funkcji, jeśli chcesz tylko zaktualizować zmienną lokalną wewnątrz funkcji.<<-
Operator może być również przydatna dla klas Reference pisząc metod referencyjnych . Na przykład:źródło