Skip to content
Menu
CDhistory
CDhistory

Bindings and Lexical Scope in Ruby

Posted on Novembro 10, 2021 by admin

Feliz Ano Novo, e bem-vindo de volta à Ruby Magic! Neste episódio de inverno, vamos mergulhar nas encadernações e nos escopos. Então ponham os vossos esquis e sigam-nos até ao fundo da floresta.

Pois passado, olhámos para os fechamentos em Ruby comparando blocos, procs e lambdas. Além das diferenças entre os três tipos, tocamos no que define um fechamento.

Um fechamento é uma função de primeira classe com um ambiente. O ambiente é um mapeamento para as variáveis que existiam quando o fechamento foi criado. O fechamento mantém seu acesso a essas variáveis, mesmo que elas estejam definidas em outro escopo.

Exploramos o equivalente do Ruby a funções de primeira classe, mas convenientemente pulamos sobre os ambientes. Neste episódio, vamos ver como esse ambiente funciona para fechamentos, classes e instâncias de classe, examinando como o Ruby lida com o escopo léxico através de seus bindings.

  • Escopo léxico
  • Escopos e Encadernações de Processos Merecidos
  • Bindings
  • A Real-Life Example
  • Perdemos você na floresta?

Escopo léxico

Na programação, escopo refere-se aos bindings disponíveis em uma parte específica do código. Um binding, ou ligação de nome, liga um nome a uma referência de memória, como o nome de uma variável ao seu valor. O escopo define o que significa self, os métodos que podem ser chamados, e as variáveis que estão disponíveis.

Ruby, como a maioria das linguagens de programação modernas, usa um escopo estático, muitas vezes chamado de escopo léxico (ao contrário de escopo dinâmico). O escopo atual é baseado na estrutura do código e determina as variáveis disponíveis em partes específicas do código. Isto significa que o escopo muda quando o código salta entre métodos, blocos e classes, pois todos eles podem ter diferentes variáveis locais, por exemplo.

1 2 3 4 5 6
def bar foo = 1 foo end bar # => 1 

Neste método, nós criamos uma variável local dentro de um método e a imprimimos para o console. A variável está no escopo dentro do método, como ela é criada lá.

1 2 3 4 5 6 7
foo = 1 def bar foo end bar # => NameError (undefined local variable or method `foo' for main:Object) 

Neste exemplo, nós criamos a variável fora do método. Quando chamamos a variável dentro de um método, obtemos um erro, uma vez que a variável está fora do escopo. Variáveis locais são bem delimitadas, significando que um método não pode acessar uma variável fora de si a menos que seja passada como argumento.

1 2 3 4 5 6 7
@foo = 1 def bar @foo end bar # => 1 

Embora variáveis locais estejam disponíveis localmente, variáveis de instância estão disponíveis para todos os métodos de uma instância de classe.

Escopos e Encadernações de Processos Merecidos

Como vimos nos exemplos anteriores, o escopo é baseado na localização no código. Uma variável local definida fora de um método não está no escopo dentro do método, mas pode ser disponibilizada transformando-a em uma variável de instância. Métodos não podem acessar variáveis locais definidas fora deles porque os métodos têm seu próprio escopo, com seus próprios bindings.

Procs (incluindo blocos e lambda’s, por extensão) são diferentes. Sempre que um proc é instanciado, é criado um binding que herda referências às variáveis locais no contexto em que o bloco foi criado.

1 2
foo = 1 Proc.new { foo }.call # => 1 

Neste exemplo, definimos uma variável chamada foo para 1. Internamente, o objeto Proc criado na segunda linha cria uma nova ligação. Ao chamar o proc, podemos pedir o valor da variável.

Desde que o binding é criado quando o proc é inicializado, não podemos criar o proc antes de definir a variável, mesmo que o bloco não seja chamado até que a variável seja definida.

1 2 3
proc = Proc.new { foo } foo = 1 proc.call # => NameError (undefined local variable or method `foo' for main:Object) 

Chamar o proc produzirá um NameError, pois a variável não está definida nas encadernações do proc. Assim, qualquer variável acessada em um proc deve ser definida antes que o proc seja criado ou passado como argumento.

1 2 3 4
foo = 1 proc = Proc.new { foo } foo = 2 proc.call # => 2 

Podemos, no entanto, alterar a variável depois de ter sido definida no contexto principal, uma vez que a encadernação do proc contém uma referência a ela em vez de copiá-la.

1 2 3
foo = 1 Proc.new { foo = 2 }.call foo #=> 2 

Neste exemplo, podemos ver que a variável foo aponta para o mesmo objecto quando está no proc como fora dele. Podemos atualizá-la dentro do proc para que a variável fora dele também seja atualizada.

Bindings

Para acompanhar o escopo atual, Ruby usa bindings, que encapsulam o contexto de execução em cada posição no código. O método binding retorna um objeto Binding que descreve os bindings na posição atual.

1 2
foo = 1 binding.local_variables # => 

O objeto binding tem um método chamado #local_variables que retorna os nomes de todas as variáveis locais disponíveis no escopo atual.

1 2
foo = 1 binding.eval("foo") # => 1 

Código pode ser avaliado no binding usando o método #eval. O exemplo acima não é muito útil, pois simplesmente chamar foo teria o mesmo resultado. No entanto, uma vez que um binding é um objeto que pode ser passado por aí, ele pode ser usado para algumas coisas mais interessantes. Vejamos um exemplo.

A Real-Life Example

Agora aprendemos sobre encadernações na segurança da nossa garagem, como levá-las para as encostas e brincar na neve. Para além do uso interno de encadernações em Ruby em toda a linguagem, existem algumas situações em que os objectos de encadernação são usados explicitamente. Um bom exemplo é o sistema de templating ERB-Ruby.

1 2 3 4 5 6 7 8 9 10
require 'erb' x = 1 def y 2 end template = ERB.new("x is <%= x %>, y() returns <%= y %>, self is `<%= self %>`") template.result(binding) # => "x is 1, y() returns 2, self is `main`" 

Neste exemplo, criamos uma variável chamada x, um método chamado y, e um template ERB que faz referência a ambos. Passamos então o binding atual para ERB#result, que avalia as tags ERB no template e retorna uma string com as variáveis preenchidas.

Atrás do hood, ERB usa Binding#eval para avaliar o conteúdo de cada tag ERB no escopo do binding passado. Uma implementação simplificada que funciona para o exemplo acima poderia ser assim:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
class DiyErb def initialize(template) @template = template end def result(binding) @template.gsub(/<%=(.+?)%>/) do binding.eval() end end end x = 1 def y 2 end template = DiyErb.new("x is <%= x %>, y() returns <%= y %>, self is `<%= self %>`") template.result(binding) # => "x is 1, y() returns 2, self is `main`" 

A classe DiyErb leva uma string de template na inicialização. O seu método #result encontra todas as etiquetas ERB e substitui-as pelo resultado da avaliação do seu conteúdo. Para isso, chama Binding#eval no binding passado, com o conteúdo das tags ERB.

Ao passar o binding atual ao chamar o método #result, as chamadas eval podem acessar as variáveis definidas fora do método, e mesmo fora da classe, sem ter que passá-las explicitamente.

Perdemos você na floresta?

Esperamos que você tenha gostado da nossa viagem de esqui na floresta. Fomos mais fundo em escopos e fechamentos, depois de os teres lustrado. Esperamos não te ter perdido no bosque. Por favor, informe-nos se você gostaria de aprender mais sobre encadernações, ou se tiver qualquer outro tópico Ruby que você gostaria de mergulhar em.

Poisas para nos seguir e, por favor, chute a neve de suas encadernações antes de deixá-las para o próximo esquiador. Se gosta destas viagens mágicas, talvez queira subscrever a Ruby Magic para receber um e-mail quando publicarmos um novo artigo cerca de uma vez por mês.

Deixe uma resposta Cancelar resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *

Artigos recentes

  • Acela está de volta: NYC ou Boston por $99
  • Entrada OMIM – # 608363 – CHROMOSOME 22q11.2 SÍNDROME DE DUPLICAÇÃO
  • Kate Albrecht’s Parents – Learn More About Her Father Chris Albrecht And Mother Annie Albrecht
  • Temple Fork Outfitters
  • Burr (romance)

Arquivo

  • Fevereiro 2022
  • Janeiro 2022
  • Dezembro 2021
  • Novembro 2021
  • Outubro 2021
  • Setembro 2021
  • Agosto 2021
  • Julho 2021
  • Junho 2021
  • Maio 2021
  • Abril 2021
  • DeutschDeutsch
  • NederlandsNederlands
  • SvenskaSvenska
  • DanskDansk
  • EspañolEspañol
  • FrançaisFrançais
  • PortuguêsPortuguês
  • ItalianoItaliano
  • RomânăRomână
  • PolskiPolski
  • ČeštinaČeština
  • MagyarMagyar
  • SuomiSuomi
  • 日本語日本語
©2022 CDhistory | Powered by WordPress & Superb Themes