<<-
é mais útil em conjunto com fechamentos para manter o estado. Aqui está uma seção de um artigo recente:
Um fechamento é uma função escrita por outra função. Os fechamentos são assim chamados porque incluem o ambiente da função pai e podem acessar todas as variáveis e parâmetros nessa função. Isso é útil porque nos permite ter dois níveis de parâmetros. Um nível de parâmetros (o pai) controla como a função funciona. O outro nível (a criança) faz o trabalho. O exemplo a seguir mostra como usar essa ideia para gerar uma família de funções de poder. A função pai ( power
) cria funções filho ( square
e cube
) que realmente fazem o trabalho duro.
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
A capacidade de gerenciar variáveis em dois níveis também possibilita manter o estado através de invocações de funções, permitindo que uma função modifique variáveis no ambiente de seu pai. A chave para gerenciar variáveis em diferentes níveis é o operador de atribuição de seta dupla <<-
. Diferente da atribuição usual de seta única ( <-
) que sempre funciona no nível atual, o operador de seta dupla pode modificar variáveis nos níveis pai.
Isso torna possível manter um contador que registra quantas vezes uma função foi chamada, como mostra o exemplo a seguir. Cada vez que new_counter
é executado, ele cria um ambiente, inicializa o contador i
nesse ambiente e cria uma nova função.
new_counter <- function() {
i <- 0
function() {
# do something useful, then ...
i <<- i + 1
i
}
}
A nova função é um fechamento e seu ambiente é o ambiente envolvente. Quando os fechamentos counter_one
e counter_two
são executados, cada um modifica o contador em seu ambiente fechado e, em seguida, retorna a contagem atual.
counter_one <- new_counter()
counter_two <- new_counter()
counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1
Isso ajuda a pensar
<<-
como equivalente aassign
(se você definir oinherits
parâmetro nessa função comoTRUE
). O benefícioassign
é que ele permite que você especifique mais parâmetros (por exemplo, o ambiente), então eu prefiro usarassign
mais<<-
na maioria dos casos.Usar
<<-
eassign(x, value, inherits=TRUE)
significa que "os ambientes fechados do ambiente fornecido são pesquisados até que a variável 'x' seja encontrada". Em outras palavras, ele continuará percorrendo os ambientes em ordem até encontrar uma variável com esse nome e a atribuirá a isso. Isso pode estar no escopo de uma função ou no ambiente global.Para entender o que essas funções fazem, você também precisa entender ambientes R (por exemplo, usando
search
).Uso regularmente essas funções quando estou executando uma simulação grande e quero salvar resultados intermediários. Isso permite que você crie o objeto fora do escopo da função ou
apply
loop especificado . Isso é muito útil, especialmente se você tiver alguma preocupação com um loop grande que termina inesperadamente (por exemplo, uma desconexão do banco de dados); nesse caso, você poderá perder tudo no processo. Isso seria equivalente a gravar seus resultados em um banco de dados ou arquivo durante um longo processo, exceto que, em vez disso, os resultados são armazenados no ambiente R.Meu aviso principal com isso: tenha cuidado, porque agora você está trabalhando com variáveis globais, especialmente ao usar
<<-
. Isso significa que você pode acabar com situações em que uma função está usando um valor de objeto do ambiente, quando você esperava que estivesse usando um que foi fornecido como parâmetro. Essa é uma das principais coisas que a programação funcional tenta evitar (consulte os efeitos colaterais ). Eu evito esse problema atribuindo meus valores a nomes de variáveis exclusivos (usando colar com um conjunto ou parâmetros exclusivos) que nunca são usados na função, mas usados apenas para cache e no caso de eu precisar me recuperar mais tarde (ou fazer alguma meta -Análise dos resultados intermediários).fonte
Um lugar onde eu usei
<<-
foi em GUIs simples usando tcl / tk. Alguns dos exemplos iniciais têm isso - pois você precisa fazer uma distinção entre variáveis locais e globais para obter a integridade do estado. Veja por exemploqual usa
<<-
. Caso contrário, concordo com Marek :) - uma pesquisa no Google pode ajudar.fonte
tkdensity
no R 3.6.0.fonte
<<-
. Um loop for seria mais claro nesse caso.Sobre esse assunto, gostaria de salientar que o
<<-
operador se comportará estranhamente quando aplicado (incorretamente) em um loop for (pode haver outros casos também). Dado o seguinte código:você pode esperar que a função retorne a soma esperada, 6, mas retorne 0, com uma variável global
mySum
sendo criada e atribuída o valor 3. Não posso explicar completamente o que está acontecendo aqui, mas certamente o corpo de um para loop não é um novo 'nível' de escopo. Em vez disso, parece que R parece fora dafortest
função, não consegue encontrar umamySum
variável à qual atribuir, portanto cria uma e atribui o valor 1, pela primeira vez no loop. Nas iterações subseqüentes, o RHS na atribuição deve estar se referindo àmySum
variável interna (inalterada) enquanto o LHS se refere à variável global. Portanto, cada iteração substitui o valor da variável global pelo valor dessa iteraçãoi
, portanto, ele tem o valor 3 ao sair da função.Espero que isso ajude alguém - isso me deixou perplexo por algumas horas hoje! (BTW, basta substituir
<<-
por<-
e a função funciona conforme o esperado).fonte
mySum
nunca é incrementado, mas apenas o globalmySum
. Portanto, a cada iteração do loop for, o globalmySum
obtém o valor0 + i
. Você pode seguir isso comdebug(fortest)
.<-
todos os locais de forma consistente na função se você quiser atualizar apenas a variável local dentro da função.O
<<-
operador também pode ser útil para classes de referência ao escrever métodos de referência . Por exemplo:fonte