Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby의 바인딩 및 어휘 범위

새해 복 많이 받으시고 Ruby Magic에 다시 오신 것을 환영합니다! 이번 겨울 에피소드에서는 바인딩과 범위에 대해 자세히 알아보겠습니다. 그러니 스키를 타고 우리를 따라 숲 속으로 깊숙이 들어가십시오.

지난 시간에 우리는 블록, 프로시저 및 람다를 비교하여 Ruby의 클로저를 살펴보았습니다. 세 가지 유형의 차이점 외에도 클로저를 정의하는 항목에 대해 설명했습니다. .

<블록 인용>

클로저는 환경이 있는 일급 함수입니다. 환경은 클로저가 생성될 때 존재했던 변수에 대한 매핑입니다. 클로저는 다른 범위에 정의된 경우에도 이러한 변수에 대한 액세스를 유지합니다.

우리는 Ruby의 일류 함수에 해당하는 기능을 살펴보았지만 환경은 편리하게 건너뛰었습니다. . 이 에피소드에서는 Ruby가 어휘 범위를 처리하는 방법을 조사하여 해당 환경이 클로저, 클래스 및 클래스 인스턴스에 대해 어떻게 작동하는지 살펴보겠습니다. 바인딩을 통해 .

어휘 범위

프로그래밍에서 범위는 바인딩을 나타냅니다. 코드의 특정 부분에서 사용할 수 있습니다. 바인딩 또는 이름 바인딩 , 변수 이름을 값에 바인딩하는 것처럼 이름을 메모리 참조에 바인딩합니다. 범위는 self가 무엇을 정의하는지 정의합니다. 호출할 수 있는 메서드와 사용할 수 있는 변수를 의미합니다.

Ruby는 대부분의 최신 프로그래밍 언어와 마찬가지로 어휘 범위라고 하는 정적 범위를 사용합니다. (동적 범위와 반대). 현재 범위는 코드의 구조를 기반으로 하며 코드의 특정 부분에서 사용할 수 있는 변수를 결정합니다. 이는 코드가 메서드, 블록 및 클래스 사이를 이동할 때 범위가 변경됨을 의미합니다. 예를 들어 모두 다른 로컬 변수를 가질 수 있기 때문입니다.

def bar
  foo = 1
  foo
end
 
bar #  => 1

이 방법에서는 로컬 변수를 생성합니다. 메소드 내에서 콘솔에 인쇄하십시오. 변수가 범위에 있습니다. 메서드 내부에서 생성되므로

foo = 1
 
def bar
  foo
end
 
bar # => NameError (undefined local variable or method `foo' for main:Object)

이 예에서는 메서드 외부에 변수를 만듭니다. 메소드 내에서 변수를 호출하면 변수가 범위를 벗어남기 때문에 오류가 발생합니다. . 지역 변수의 범위는 엄격합니다. 즉, 메서드는 인수로 전달되지 않는 한 외부 변수에 액세스할 수 없습니다.

@foo = 1
 
def bar
  @foo
end
 
bar #  => 1

로컬 변수는 로컬에서 사용할 수 있지만 인스턴스 변수 클래스 인스턴스의 모든 메서드에서 사용할 수 있습니다.

상속된 범위 및 프로시저 바인딩

이전 예제에서 보았듯이 범위는 코드의 위치를 ​​기반으로 합니다. 메소드 외부에 정의된 로컬 변수는 메소드 내부의 범위에 있지 않지만 인스턴스 변수로 변환하여 사용 가능하게 만들 수 있습니다. 메소드는 고유한 바인딩과 함께 고유한 범위를 갖기 때문에 메소드 외부에 정의된 로컬 변수에 액세스할 수 없습니다.

프록(블록 및 람다 포함, 확장)은 다릅니다. proc이 인스턴스화될 때마다 블록이 생성된 컨텍스트에서 로컬 변수에 대한 참조를 상속하는 바인딩이 생성됩니다.

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

이 예에서는 foo라는 변수를 설정합니다. 1로 . 내부적으로 두 번째 줄에 생성된 Proc 개체는 새 바인딩을 생성합니다. proc을 호출할 때 변수의 값을 요청할 수 있습니다.

proc이 초기화될 때 바인딩이 생성되기 때문에 변수가 정의될 ​​때까지 블록이 호출되지 않더라도 변수를 정의하기 전에는 proc을 생성할 수 없습니다.

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

프로시저를 호출하면 NameError가 생성됩니다. 변수가 proc의 바인딩에 정의되어 있지 않기 때문입니다. 따라서 proc에서 액세스하는 모든 변수는 proc이 생성되거나 인수로 전달되기 전에 정의되어야 합니다.

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

그러나 proc의 바인딩이 복사하는 대신 참조를 보유하고 있기 때문에 기본 컨텍스트에서 정의된 변수를 변경할 수 있습니다.

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

이 예에서 foo 변수는 proc에 있을 때와 외부에 있을 때 동일한 개체를 가리킵니다. proc 내부의 변수를 업데이트하여 외부의 변수도 업데이트할 수 있습니다.

바인딩

현재 범위를 추적하기 위해 Ruby는 바인딩을 사용합니다. , 코드의 각 위치에서 실행 컨텍스트를 캡슐화합니다. binding 메서드는 binding을 반환합니다. 현재 위치의 바인딩을 설명하는 개체입니다.

foo = 1
binding.local_variables # => [:foo]

바인딩 개체에는 #local_variables라는 메서드가 있습니다. 현재 범위에서 사용 가능한 모든 지역 변수의 이름을 반환합니다.

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

#eval을 사용하여 바인딩에서 코드를 평가할 수 있습니다. 방법. 위의 예는 단순히 foo를 호출하기 때문에 그다지 유용하지 않습니다. 같은 결과를 얻었을 것입니다. 그러나 바인딩은 전달할 수 있는 개체이기 때문에 좀 더 흥미로운 작업에 사용할 수 있습니다. 예를 들어 보겠습니다.

실제 사례

이제 우리는 차고의 안전을 위한 바인딩에 대해 배웠습니다. 예를 들어 슬로프에 가지고 가서 눈 속에서 노는 것과 같은 것입니다. 언어 전체에 걸쳐 Ruby의 내부 바인딩 사용 외에도 바인딩 개체가 명시적으로 사용되는 몇 가지 상황이 있습니다. 좋은 예는 ERB(Ruby의 템플릿 시스템)입니다.

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`"

이 예에서는 x라는 변수를 생성합니다. , y라는 메서드 및 둘 다 참조하는 ERB 템플릿이 있습니다. 그런 다음 현재 바인딩을 ERB#result에 전달합니다. , 템플릿의 ERB 태그를 평가하고 변수가 채워진 문자열을 반환합니다.

내부적으로 ERB는 Binding#eval을 사용합니다. 전달된 바인딩 범위에서 각 ERB 태그의 내용을 평가합니다. 위의 예에서 작동하는 단순화된 구현은 다음과 같습니다.

class DiyErb
  def initialize(template)
    @template = template
  end
 
  def result(binding)
    @template.gsub(/<%=(.+?)%>/) do
      binding.eval($1)
    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`"

DiyErb 클래스는 초기화 시 템플릿 문자열을 사용합니다. #result 메소드는 모든 ERB 태그를 찾아 내용을 평가한 결과로 대체합니다. 이를 위해 Binding#eval을 호출합니다. ERB 태그의 내용과 함께 전달된 바인딩에서.

#result 호출 시 현재 바인딩 전달 메소드, eval 호출은 명시적으로 전달하지 않고도 메서드 외부 및 클래스 외부에서 정의된 변수에 액세스할 수 있습니다.

숲에서 당신을 잃어버렸나요?

숲 속으로 스키 여행을 즐기셨기를 바랍니다. 우리는 범위와 클로저에 대해 설명을 들은 후 더 깊이 파고 들었습니다. 숲에서 당신을 잃지 않기를 바랍니다. 바인딩에 대해 자세히 알고 싶거나 다른 Ruby 주제에 대해 자세히 알아보려면 알려주세요.

팔로우해주셔서 감사합니다. 다음 개발자를 위해 바인딩을 떠나기 전에 바인딩을 치워주세요. 이 마법 같은 여행이 마음에 드시면 Ruby Magic을 구독하면 한 달에 한 번 정도 새 기사가 게시될 때 이메일을 받을 수 있습니다.