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

고급 루비 해시 기법

Ruby 개발자가 Hashes를 사용하는 것만큼 무언가를 사용하면 모두 봤다고 생각하기 쉽습니다.

그러나 나는 겸손한 루비 해시가 소매에 몇 가지 트릭을 가지고 있다는 것을 말하려고 왔습니다. 멍청한 키-값 시스템과는 거리가 먼 Hash 개체는 매우 흥미롭고 정교한 작업을 수행할 수 있는 능력을 제공합니다.

모든 개체는 해시 키가 될 수 있습니다.

더 진행하기 전에 명확하지 않을 수 있는 한 가지를 지적하고 싶습니다. 문자열과 기호를 해시 키로 사용하는 경향이 있지만 다른 종류의 객체도 사용할 수 없다는 의미는 아닙니다. 사실 거의 모든 것을 해시 키로 사용할 수 있습니다.

# Numbers can be hash keys
{1 => "one"}[1] # "one"

# So can the Ruby kernel
{Kernel => 1}[Kernel] # 1

# You can store values for specific classes
{Kernel => 1, String => 2}["hello world".class] # 2

# You can store values for booleans
{true => "verdad"}[1==1] # "verdad"

# You can even use complex arrays and even other hashes as hash keys
{[[1,0],[0,1]] => "identity matrix"}[[[1,0], [0,1]]] # "identity matrix"

이러한 옵션 중 일부는 다른 옵션보다 더 유용하지만 모두 사용할 수 있습니다.

기본값을 제어할 수 있습니다.

해시 h={ a: 1 }가 있다고 가정합니다. . 존재하지 않는 값에 액세스하려고 하는 경우(예:   h[:x]) - 당신은 0을 얻을. 달리 지정하지 않는 한 nil이 모든 해시의 기본값이기 때문입니다.

생성자에 인수를 전달하여 새 해시의 기본값을 설정할 수 있습니다.

h = Hash.new("This attribute intentionally left blank")
h[:a] = 1
h[:a] # 1
h[:x] # "This attribute intentionally left blank"

동적 기본값

이 트릭이 뒤따르는 모든 것의 기초이기 때문에 주의하십시오.

블록을 생성자에 전달하면 프로그래밍 방식으로 기본값을 생성할 수 있습니다. 아래 예에서는 타임스탬프를 기본값에 추가하여 동적으로 생성되는 것을 볼 수 있습니다.

h = Hash.new { |hash, key| "#{key}: #{ Time.now.to_i }" }
h[:a] # "a: 1435682937"
h[:a] # "a: 1435682941"
h[:b] # "b: 1435682943"

이는 "기본값" 블록이 기본값을 반환하는 것 외에 다른 작업을 수행할 수 있기 때문에 중요합니다.

해시 키가 없으면 예외 발생

해시의 주요 문제 중 하나는 해시가 자동으로 실패한다는 것입니다. 실수로 user[:phnoe]를 입력했습니다. user[:phone] 대신 , 그리고 예외를 발생시키는 대신 해시는 nil을 반환합니다. 하지만 이 동작을 변경할 수 있습니다.

h = Hash.new { |hash, key| raise ArgumentError.new("No hash key: #{ key }") }
h[:a]=1
h[:a] # 1
h[:x] # raises ArgumentError: No hash key: x

이 기술은 특정 해시에 적용되기 때문에 디버깅 및 리팩토링에 유용할 수 있습니다. 이 동작을 추가하는 것은 Hash 클래스를 원숭이 패치하는 것과 같은 것보다 훨씬 덜 방해가 되는 방법입니다.

참고:나는 누군가가 새 코드에서 Hash.fetch 대신 이것을 사용하도록 제안하지 않습니다. 디버깅과 리팩토링을 위한 준비를 하는 것은 흥미로운 트릭일 뿐입니다.

지연 생성 조회 테이블

이 기술은 계산 결과를 캐싱하는 데 유용합니다. 많은 제곱근을 계산해야 한다고 상상해 보십시오. 아래 예와 같이 느리게 채워진 조회 테이블을 만들 수 있습니다.

sqrt_lookup = Hash.new { |hash, key| hash[key] = Math.sqrt(key) }
sqrt_lookup[9] # 3.0
sqrt_lookup[7] # 2.6457513110645907
sqrt_lookup    # {9=>3.0, 7=>2.6457513110645907}

재귀적 지연 조회 테이블

재귀 함수가 있고 각 재귀의 결과를 캐시하려고 한다고 가정합니다. 팩토리얼 계산을 예로 들어보겠습니다. "4 계승", 일명 "4!" "4x3x2x1"을 말하는 또 다른 방법입니다. 해시를 사용하여 이를 재귀적으로 구현할 수 있습니다. 이 블로그 게시물에서 가져온 아래 예는 이를 잘 보여줍니다.

factorial = Hash.new do |h,k| 
  if k > 1
    h[k] = h[k-1] * k
  else
    h[k] = 1
  end
end

factorial[4] # 24
factorial    # {1=>1, 2=>2, 3=>6, 4=>24}

초기화 후 기본값 수정

해시가 생성된 후 기본값을 제어할 수도 있습니다. 이렇게 하려면 default를 사용하세요. 및 default_proc 세터.

h={}
h[:a] # nil
h.default = "new default"
h[:a] # "new default"

h.default_proc = Proc.new { Time.now.to_i }
h[:a] # 1435684014

루비 찾기:게으른 무한 중첩 해시 게임

재미를 위해 이 모든 유용한 기술을 하나의 매우 쓸모없는 예제로 요약해 보겠습니다. 오래된 텍스트 기반 게임인 Adventure를 기억하십니까? 가장 멍청한 버전을 만들어 봅시다.

당신이 동굴에 있다고 상상해보십시오. 북쪽, 남쪽, 동쪽 또는 서쪽으로 갈 수 있습니다. 이 중 세 가지를 선택하면 계속 탐색하는 동굴의 새로운 "방"으로 이동합니다. 그러나 하나의 선택은 당신을 "루비"로 이끕니다. 따라서 게임 이름은 "루비를 찾아라."입니다.

동굴의 각 방은 해시에 해당합니다. 해시에는 항목이 하나만 있습니다. 무작위로 선택된 ["n", "", "e", "w"] 중 하나는 "당신이 루비를 찾았습니다."라는 값을 가집니다. 잘못 선택하면 새 해시가 생성되어 트리에 추가됩니다.

generator = Proc.new do |hash, key| 
  hash[key] = Hash.new(&generator).merge(["n", "s", "e", "w"][rand(4)] => "You found the ruby!")
end
dungeon = Hash.new(&generator)
dungeon["n"] # <Hash ...
dungeon["n"]["s"] # <Hash ...
dungeon["n"]["s"]["w"] # "You found the ruby!"