그게 너무 가져

RubyTapas 아카이브를 다시 살펴볼 시간입니다! 이제 모두에게 무료로 제공되는 Ruby#fetch 메서드 계열에 대한 이 세 번째이자 마지막 에피소드에서는 몇 가지 고급#fetch 사용법에 대해 알아봅니다. 포함: 깊은 가져오기, 폴백 코드에서 누락된 키 이름 사용, #fetch 의 두 인수 형식을 사용하지 않는 이유.


Director's commentary: 이 글은 2012년 10월 26일 RubyTapas에 처음 게시되었습니다. 그 이후로 #dig 메서드는 폴백 블록 인수가 부족하지만 내 딥 페칭 요구 사항 중 일부를 대체했습니다.

비디오 아래에서 원래 에피소드 스크립트와 코드를 찾으십시오.




소개



이것은 세 번째이며 거의 확실하게 마지막입니다episode on the #fetch method. 오늘 저는 #fetch 사용의 몇 가지 고급 측면을 살펴보고자 합니다.

#해시 너머 가져오기



우선, 지금까지 해시에 대해 it in the context of Hash 객체, #fetch isn’t limited을 시연했다는 점에 주목할 필요가 있습니다. Arrays 에서도 사용할 수 있으며 키가 누락되고 기본 블록이 제공되지 않은 경우 IndexError 대신 KeyError 를 발생시킨다는 점을 제외하면 해시 버전과 매우 유사하게 작동합니다.

a = [:x, :y, :z]
a.fetch(3)
# ~> -:2:in `fetch': index 3 outside of array bounds: -3...3 (IndexError)
# ~> from -:2:in `<main>'


#fetch 에서 ENV pseudo-hash 을 찾을 수도 있습니다. 예를 들어 기본값을 계속 제공하면서 환경 변수를 통해 포트 번호를 사용자 정의할 수 있도록 하는 것과 같이 선택적 구성 값에 매우 유용합니다.

port = ENV.fetch('PORT'){ 8080 }.to_i
port # => 8080



중첩 해시의 기본값



경우에 따라 중첩된 해시에서 선택적 값을 가져오고 싶을 수 있습니다. 값이 있는지 여부를 알 수 없을 뿐만 아니라 중첩된 하위 트리가 있는지 여부도 알 수 없습니다. 예를 들어 다음은 몇 가지 구성 데이터입니다.

config1 = {
  database: {
    type: 'mysql',
    host: 'localhost'
  }
}

config2 = {} # empty!  



누락된 하위 트리에 대한 기본값으로 빈 해시를 사용하여 가져오기 문을 함께 연결하여 이와 같은 데이터를 처리하는 것을 좋아합니다.

config2.fetch(:database){{}}.fetch(:type){'sqlite'}
# => "sqlite"  



일반화된 기본 블록



아직 표시하지 않은 한 가지#fetch는 전달된 누락된 키를 생성한다는 것입니다. 다음은 제가 의미하는 바를 보여주는 몇 가지 코드입니다.

{}.fetch(:foo) do |key|
  puts "Missing key: #{key}"
end
# >> Missing key: foo



이것이 유용할 수 있는 한 가지 시나리오는 모두 동일한 방식으로 누락된 키를 처리해야 하는 가져오기 호출이 많은 경우입니다. 기본 블록을 하나의 인수를 취하는 lambda으로 정의하고 fetch에 대한 각 호출에 람다를 전달할 수 있습니다. 이 코드는 사용자에게 누락된 값을 묻는 메시지를 표시합니다.

default = ->(key) do
  puts "#{key} not found, please enter it: "
  gets
end

h = {}
name = h.fetch(:name, &default)
email = h.fetch(:email, &default)



인수가 두 개인 #fetch 형식


#fetch 방법에 이미 익숙하다면 내가 이 비디오에서 두 인수 형식을 사용하지 않은 이유가 궁금할 것입니다. 익숙하지 않은 분들을 위해 블록을 기본값으로 #fetch에 전달하는 대신 두 번째 인수를 전달할 수 있습니다.

{}.fetch(:threads, 4) # => 4



이렇게 하면 필요한지 여부에 관계없이 기본값을 평가하는 비용으로 블록을 실행하는 약간의 오버헤드를 피할 수 있습니다. 개인적으로 저는 두 인수 형식을 사용하지 않습니다. 나는 항상 블록 형식을 사용하는 것을 선호합니다. 그 이유는 다음과 같습니다. 우리가 프로그램을 작성 중이고 블록 오버헤드를 피하기 위해 인수가 두 개인 가져오기 형식을 사용한다고 가정해 보겠습니다. 기본값은 여러 곳에서 사용되기 때문에 메서드로 추출합니다.

def default
  42 # the ultimate answer
end

answers = {}
answers.fetch("How many roads must a man walk down?", default)
# => 42



나중에 #default의 구현을 훨씬 더 비싼 계산으로 변경하기로 결정합니다. 반환하기 전에 원격 서비스와 통신해야 하는 것일 수 있습니다.

def default
  # ...some expensive computation
end

answers = {}
answers.fetch("How many roads must a man walk down?", default)



기본값이 #fetch 에 인수로 전달되면 항상 필요한지 여부가 평가됩니다. 이제 우리의 값비싼#default 코드는 값이 존재하더라도 우리가#fetch 값을 가질 때마다 실행됩니다. 너무 이른 최적화로 인해 #default 방법이 인수로 사용되는 모든 곳에서 잠재적으로 훨씬 더 큰 성능 회귀를 도입했습니다. 블록 형식을 사용했다면 비용이 많이 드는 계산은 실제로 필요할 때만 트리거되었을 것입니다.

결론



좋아, #fetch에 대해 충분히! 행복한 해킹!

좋은 웹페이지 즐겨찾기