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

Ruby 해시 키로 객체

Ruby 3x3 작업을 따라오셨다면 Optcarrot에 대해 들어보셨을 것입니다. 순수한 Ruby로 작성된 NES 에뮬레이터입니다.

나는 최근에 Optcarrot 소스를 살펴보고 있었고 흥미로운 세부 사항이 하나 눈에 띄었습니다. 종종 간과되지만 매우 유용한 Ruby 해시의 기능을 광범위하게 사용합니다. 그것은 모든 객체를 해시 키로 사용할 수 있는 기능입니다.

컨텍스트:NES 메모리 매핑

고급 프로그래머로서 우리는 메모리를 RAM으로 생각하는 경향이 있습니다. 그러나 하위 수준에서 "메모리"는 다른 많은 용도로 사용됩니다.

"메모리"에 대한 읽기 및 쓰기는 NES의 CPU가 GPU, 제어 패드 및 카트리지의 특수 전자 장치와 통신하는 방법입니다. 사용된 주소에 따라 write_to_memory 메소드 호출은 조이스틱을 재설정하거나 VRAM을 교체하거나 사운드를 재생할 수 있습니다.

이것을 Ruby에서 어떻게 구현하시겠습니까?

Optcarrot은 두 개의 Method를 저장하여 이를 수행합니다. 65536 주소 각각에 대한 개체. 하나는 getter이고 하나는 setter입니다. 다음과 같이 보입니다.

@getter_methods[0x0001] = @ram.method(:[])
@setter_methods[0x0001] = @ram.method(:[]=)

문제:중복 개체

Object#method 사용 시 문제 이런 식으로 개별 Method 동일한 개체입니다.

object_id를 보면 알 수 있습니다. :

> a = []
> a.method(:[]=).object_id
=> 70142391223600
> a.method(:[]=).object_id
=> 70142391912420

두 가지 Method 개체에 다른 object_id가 있습니다. 값이 같으므로 동일한 작업을 수행하더라도 서로 다른 개체입니다.

일반적으로 몇 가지 추가 Method 하지만 이 경우에는 수천 개의 개체를 다루고 있습니다.

해결책:해시를 통한 메모 작성

Optcarrot은 중복 Method를 방지합니다. 너무 간단해서 간과하기 쉬운 트릭으로 개체 문제를 해결합니다.

해시를 사용하여 메모를 작성하고 중복 제거합니다. 아래의 단순화된 코드는 기술을 보여줍니다.

def initialize
  @setter_methods = []
  @setter_cache = {}
  ...
end

def add_setter(address, setter)
  # Doesn't store duplicates
  @setter_cache[setter] ||= setter

  # Use the deduped version
  @setter_methods[address] = @setter_cache[setter]  
end

이것은 Hash 때문에 작동합니다. 어떤 종류의 개체를 키로 제공하든 상관하지 않습니다.

이것이 혼란스럽다면 IRB에서 문자열을 사용해 보십시오:

> cache = {}
> cache["foo"] ||= "bar" 
=> "bar"
cache["foo"] ||= "baz"
=> "bar"

이제 Ruby에서 문자열이 String 클래스의 인스턴스임을 고려하십시오. . Ruby에서 문자열을 해시 키로 사용하는 메커니즘은 기본적으로 Method를 저장하는 데 사용되는 메커니즘과 동일합니다. 물체.

해시가 평등을 계산하는 방법

문자열이 아닌 객체를 해시 키로 사용할 때 Hash가 어떻게 두 개체가 같은지 알고 계십니까?

대답은 Object#hash를 사용한다는 것입니다. 방법. 이 메서드는 개체를 통과하고 재귀적으로 해시를 생성합니다. 다음과 같습니다.

> a.method(:[]=).hash
=> 929915641391564853

동일한 객체는 동일한 해시 값을 생성하므로 동등성 테스트로 사용할 수 있습니다.

a.hash == b.hash

흥미롭게도 이것은 eql?에서 사용하는 것과 동일한 접근 방식입니다. 방법:

a.eql?(b)

이것은 Method와 함께 작동합니다. 이 예의 개체:

> a.method(:[]=).hash == a.method(:[]=).hash
=> true

결론

Ruby 웹 개발 패턴에 익숙해지면서 optcarrot 소스를 보고 웹이 아닌 실시간 앱이 다른 패턴을 어떻게 사용하는지 보는 것이 정말 흥미로웠습니다. 웹 앱에서 65536개의 요소로 배열을 만들 수 있을지는 의문이지만 여기서는 "데스크톱" 앱 설정의 일부로 의미가 있습니다.

질문이나 의견이 있으면 starr@honeybadger.io 또는 Twitter의 @StarrHorne으로 연락하십시오.