AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 78990989
Accepted
mysteriarcha
mysteriarcha
Asked: 2024-09-17 00:19:21 +0800 CST2024-09-17 00:19:21 +0800 CST 2024-09-17 00:19:21 +0800 CST

Mudança aleatória na saída obj_addr() ao incluir os objetos em uma lista e vetorizá-los

  • 772

Há um comportamento estranho de lobstr::obj_addr causado por sua vetorização sobre listas, quando a própria lista não altera o endereço

Acabei de começar Advanced R de Wickham (2ª ed.) e cheguei ao primeiro exercício 2.2.2 Exercises. Eu supus que, dado:

a <- 1:10; b <- a; c <- b

todos eles teriam o mesmo endereço de memória recuperado pela lobstr::obj_addrfunção. Isso é verdade se usarmos apenas a, b ou c como entradas, mas como sou preguiçoso e queria ter todos os valores de uma vez, fiz:

list(a, b, c) |> lapply(obj_addr) # lapply or sapply 

Então obtemos um conjunto diferente de valores entre os diferentes nomes toda vez que a função é executada. Isso ainda acontece se definimos x <- list(a, b, c)antes de chamar a função por meio de lapply, e obj_addr(x[[1]]) == obj_addr(x[[2]]) == obj_addr(x[[3]]) == obj_addr(a), então não é uma questão de criar uma nova lista toda vez. Alguém sabe o que está acontecendo aqui? Eu entendo que até certo ponto cada chamada gera um novo objeto de saída que terá seu próprio endereço de memória, mas não sei como lapplypode interferir em uma função constante para um determinado objeto como obj_addr.

Agradeço antecipadamente!

  • 2 2 respostas
  • 96 Views

2 respostas

  • Voted
  1. Best Answer
    SamR
    2024-09-17T17:40:36+08:002024-09-17T17:40:36+08:00

    Isso é causado por um bug na forma como lobstridentifica o ambiente

    MrFlick apontou nos comentários que x |> lapply(function(x) obj_addr(x))retorna a resposta certa e x |> lapply(obj_addr)retorna a errada. Isso indica que algo estranho está acontecendo com os ambientes. A questão é se isso é causado por lobstr::obj_addr()ou lapply(). Acontece que isso decorre da maneira como lobstrusa rlang, o que faz com que ele procure o objeto no ambiente incorreto.

    O que faz lobstr::obj_addr()?

    A fonte (ligeiramente simplificada) é a seguinte:

    obj_addr <- function(x) {
      x <- enquo(x)
      obj_addr_(quo_get_expr(x), quo_get_env(x))
    }
    

    obj_addr_()é a função workhorse escrita em C que obtém o endereço de memória. No entanto, primeiro o R obj_addr()usa as rlang ferramentas quosure como uma forma de acessar o objeto sem aumentar a contagem de referências . Isso permite que a coleta de lixo aconteça corretamente mais tarde, garantindo que não haja referências ao objeto por aí. No entanto, no caso de lapply(x, lobstr::obj_addr), ele não acessa corretamente o ambiente dos elementos de x, que é onde o erro surge.

    O que acontece com os ambientes em lapply()?

    Considere a seguinte função para obter o ambiente de um objeto do qual uma função é chamada:

    f <- function(x) {
        rlang::quo_get_env(rlang::enquo(x))
    }
    

    Podemos chamar isso de lapply()ambas as maneiras:

    x <- list(a = 1, b = 2, c = 3)
    
    lapply(x, \(y) f(y)) # anonymous function
    # $a
    # <environment: 0x557c265eb438>
    
    # $b
    # <environment: 0x557c265ea910>
    
    # $c
    # <environment: 0x557c26e275c8>
    
    lapply(x, f) # function provided directly
    # $a
    # <environment: R_EmptyEnv>
    
    # $b
    # <environment: R_EmptyEnv>
    
    # $c
    # <environment: R_EmptyEnv>
    

    O primeiro conjunto de resultados faz sentido. lapply()cria um ambiente temporário cada vez que chama a função anônima (o fechamento da função).

    No entanto, não faz sentido que lapply(x, f)esteja sendo executado no ambiente vazio. Sabemos que podemos nos referir a objetos no ambiente global com lapply(). Mas o ambiente vazio por definição não contém objetos e não tem pai:

    f_parent <- function(x) {
        e <- rlang::quo_get_env(enquo(x))
        message("Objects in environment: ", length(ls(e)))
        message("Parent environment: ", parent.env(e))
    }
    
    lapply(x, f_parent)
    # Objects in environment: 0
    # Error in parent.env(e) : the empty environment has no parent
    

    Então rlang::quo_get_env(rlang::enquo(x))claramente retorna o ambiente errado. Vamos tentar encontrar o ambiente pai da função chamada usando o lapply()R base:

    f2 <- function(x) {
        parent.env(environment())
    }
    lapply(x, f2)
    # $a
    # <environment: R_GlobalEnv>
    
    # $b
    # <environment: R_GlobalEnv>
    
    # $c
    # <environment: R_GlobalEnv>
    

    Isso faz mais sentido e nos dá uma pista sobre o que está acontecendo.

    Escrevendo nossa própria função para obter o ponteiro

    Para descartar lapply()como fonte dessa inconsistência, vamos escrever nossa própria versão de lobstr::obj_addr()que não mexe com ambientes. A linha relevante da obj_addr_()função de nível C é onde ela converte o SEXPpara um ponteiro:

    static_cast<void *>(x);
    

    Aqui está uma função semelhante para obter o ponteiro que ignora o rlangconteúdo:

    get_pointer <- inline::cfunction(
        sig = c(x = "integer"),
        body = '
        // cast SEXP to a void pointer like lobstr
        void* ptr = (void*) x;
    
        // put the pointer in a character array
        char addr_chr[32];
        snprintf(addr_chr, sizeof(addr_chr), "%p", ptr);
    
        // put address in character vector and return it
        SEXP addr = PROTECT(allocVector(STRSXP, 1));
        SET_STRING_ELT(addr, 0, mkChar(addr_chr));
        UNPROTECT(1);
        return addr;',
        includes = "#include <stdio.h>"
    )
    

    Comparando get_pointer()comlobstr::obj_addr()

    Vamos definir xe verificar os endereços individualmente:

    x <- list(a = 1, b = 2, c = 3)
    
    lobstr::obj_addr(x[[1]]) # [1] "0x557c22e8eb28"
    lobstr::obj_addr(x[[2]]) # [1] "0x557c22e8eb60"
    lobstr::obj_addr(x[[3]]) # [1] "0x557c22e8eb98"
    

    Agora podemos comparar os resultados usando lobstrde três maneiras. Usarei sapply()em vez de lapply()pois imprime melhor. Podemos ver que sapply(x, lobstr::obj_addr)não está correto.

    lobstr::obj_addrs(x) # correct
    # [1] "0x557c22e8eb28" "0x557c22e8eb60" "0x557c22e8eb98"
    
    sapply(x, \(y) lobstr::obj_addr(y)) # correct
    #                a                b                c
    # "0x557c22e8eb28" "0x557c22e8eb60" "0x557c22e8eb98"
    
    sapply(x, lobstr::obj_addr) # incorrect
    #                a                b                c
    # "0x557c24fdd7a0" "0x557c24fdd8f0" "0x557c24fdda78"
    

    A questão é se podemos obter os resultados corretos se pularmos as coisas do ambiente. É aqui que podemos usar get_pointer():

    sapply(x, \(y) get_pointer(y)) # correct
    #                a                b                c 
    # "0x557c22e8eb28" "0x557c22e8eb60" "0x557c22e8eb98"
    
    sapply(x, get_pointer) # correct
    #                a                b                c 
    # "0x557c22e8eb28" "0x557c22e8eb60" "0x557c22e8eb98"
    

    Então get_pointer()obtém os resultados corretos nas duas vezes. Isso indica que o problema está no lobstruso de ferramentas de quosure do rlang. Na verdade, não tenho certeza se isso é um rlangproblema ou se o problema é como lobstrestá usando rlang. No entanto, como ambos os pacotes são parte do r-lib , imagino que um relatório de bug arquivado em qualquer um deles encontraria seu caminho para o lugar certo bem rápido.

    • 3
  2. alexis_laz
    2024-09-17T17:23:04+08:002024-09-17T17:23:04+08:00

    Parece que esse comportamento se deve ao fato de que obj_addrestá enquoing seu argumento antes de recuperar seu endereço (veja a definição da função). Então, podemos examinar o comportamento de enquoseparadamente da parte real de recuperação de endereço.

    Para verificação, podemos definir uma função baseada em obj_addrque recupera o endereço como:

    .internal.address = function(.) lobstr:::obj_addr_(rlang::quo_get_expr(.), 
                                                       rlang::quo_get_env(.))
    

    Para referência, o objeto aestá localizado em:

    .Internal(inspect(a))
    #@58398aa88108 13 INTSXP g1c0 [MARK,REF(65535)]  1 : 10 (expanded)
    

    Quando fora do corpo de um fechamento, enquoretorna algo inútil para recuperar o endereço do objeto, pois enquoparece ser capaz de avaliar um símbolo completamente (em contraste com, substitutepor exemplo) e retornar um objeto recém-construído:

    enquo(a)
    #<quosure>
    #expr: ^<int: 1L, 2L, 3L, 4L, 5L, ...>
    #env:  empty
    .internal.address(enquo(a))
    #[1] "0x5839a29c0018"
    .internal.address(enquo(a))
    #[1] "0x5839a29c0088"
    

    Dentro de um fechamento, porém, tudo funciona como esperado:

    f1 = function(x) enquo(x)
    f1(a)
    #<quosure>
    #expr: ^a
    #env:  global
    .internal.address(f1(a))
    #[1] "0x58398aa88108"
    .internal.address(f1(a))
    #[1] "0x58398aa88108"
    

    lapplyavalia seus argumentos durante a construção da chamada e, provavelmente, podemos simular seu comportamento com force(para deixar claro) assim:

    force1 = function(x) { force(x); enquo(x)}
    force1(a)
    #<quosure>
    #expr: ^<int: 1L, 2L, 3L, 4L, 5L, ...>
    #env:  empty
    .internal.address(force1(a))
    #[1] "0x5839a2816848"
    .internal.address(force1(a))
    #[1] "0x5839a2816b58"
    

    já que enquonão retorna mais um símbolo pesquisável.

    Como MrFlick observa nos comentários, envolver com outra camada de fechamento funciona como esperado, pois enquonão parece chegar a uma avaliação completa de seu argumento:

    force2 = function(y) { force(y); f1(y)}
    force2(a)
    #<quosure>
    #expr: ^y
    #env:  0x58399eef63d8
    .internal.address(force2(a))
    #[1] "0x58398aa88108"
    

    Além disso, por exemplo, se redefinirmos obj_addrao longo das linhas de:

    obj_addr2 = function(x) lobstr:::obj_addr_(substitute(x), parent.frame())
    

    então lapplynão causa confusão:

    obj_addr(a)
    #[1] "0x58398aa88108"
    obj_addr2(a)
    #[1] "0x58398aa88108"
    lapply(list(a, b), obj_addr)
    #[[1]]
    #[1] "0x58399a991748"
    #
    #[[2]]
    #[1] "0x58399a9917b8"
    
    lapply(list(a, b), obj_addr2)
    #[[1]]
    #[1] "0x58398aa88108"
    #
    #[[2]]
    #[1] "0x58398aa88108"
    
    • 2

relate perguntas

  • Adicionar número de série para atividade de cópia ao blob

  • A fonte dinâmica do empacotador duplica artefatos

  • Selecione linhas por grupo com 1s consecutivos

  • Lista de chamada de API de gráfico subscritoSkus estados Privilégios insuficientes enquanto os privilégios são concedidos

  • Função para criar DFs separados com base no valor da coluna

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve