Ruby에서 동적 메서드 호출
내가 아는 한 Ruby에서 동적으로 메서드를 호출하는 세 가지 방법이 있습니다.
방법 1 :
s = SomeObject.new
method = s.method(:dynamic_method)
method.call
방법 2 :
s = SomeObject.new
s.send(:dynamic_method)
방법 3 :
s = SomeObject.new
eval "s.dynamic_method"
이를 벤치마킹하여 방법 1이 가장 빠르고 방법 2가 더 느리며 방법 3이 가장 느리다는 것을 확인했습니다.
나는 또한 것을 발견 .call
하고 .send
있는 동안 모두는 개인 메소드를 호출 할 수 eval
하지 않습니다.
그래서 내 질문은 : .send
또는 사용할 이유가 eval
있습니까? 항상 가장 빠른 방법을 사용하지 않는 이유는 무엇입니까? 이러한 동적 메서드 호출 방법의 다른 차이점은 무엇입니까?
사용할 이유가
send
있습니까?
call
메소드 객체가 send
필요하지만 다음을 수행하지 않습니다.
class Foo
def method_missing(name)
"#{name} called"
end
end
Foo.new.send(:bar) #=> "bar called"
Foo.new.method(:bar).call #=> undefined method `bar' for class `Foo' (NameError)
사용할 이유가
eval
있습니까?
eval
메서드를 호출하기위한 것이 아닙니다.
벤치 마크와 관련하여 + send
보다 빠른 것 같습니다 .method
call
require 'benchmark'
class Foo
def bar; end
end
Benchmark.bm(4) do |b|
b.report("send") { 1_000_000.times { Foo.new.send(:bar) } }
b.report("call") { 1_000_000.times { Foo.new.method(:bar).call } }
end
결과:
user system total real
send 0.210000 0.000000 0.210000 ( 0.215181)
call 0.740000 0.000000 0.740000 ( 0.739262)
다음과 같이 생각하십시오.
방법 1 (method.call) : 단일 런타임
프로그램에서 Ruby를 한 번만 실행하면 전체 시스템을 제어하고 "method.call"접근 방식을 통해 "메서드에 대한 포인터"를 유지할 수 있습니다. 당신이하는 일은 당신이 원할 때마다 실행할 수있는 "라이브 코드"에 대한 핸들을 잡는 것뿐입니다. 이것은 기본적으로 객체 내에서 직접 메서드를 호출하는 것만 큼 빠릅니다 (그러나 object.send를 사용하는 것만 큼 빠르지는 않습니다-다른 답변의 벤치 마크 참조).
방법 2 (object.send) : 데이터베이스에 메서드 이름 유지
그러나 호출하려는 메소드의 이름을 데이터베이스에 저장하고 향후 애플리케이션에서 데이터베이스에서 검색하여 해당 메소드 이름을 호출하려면 어떻게해야할까요? 그런 다음 두 번째 접근 방식을 사용하면 루비가 두 번째 "s.send (: dynamic_method)"접근 방식을 사용하여 임의의 메서드 이름을 호출하게됩니다.
방법 3 (평가) :자가 수정 방법 코드
메서드를 새로운 코드로 실행하는 방식으로 데이터베이스에 코드를 작성 / 수정 / 지속하려면 어떻게해야합니까? 데이터베이스에 기록 된 코드를 주기적으로 수정하고 매번 새 코드로 실행되도록 할 수 있습니다. 이 (매우 드문 경우) 세 번째 접근 방식을 사용하여 메서드 코드를 문자열로 작성하고 나중에 다시로드하고 전체를 실행할 수 있습니다.
가치가 있기 때문에 일반적으로 루비 세계에서는 매우 난해하고 드문 경우를 제외하고 Eval (방법 3)을 사용하는 것이 나쁜 형태로 간주됩니다. 따라서 발생하는 거의 모든 문제에 대해 방법 1과 2를 고수해야합니다.
가능한 모든 메서드 호출은 다음과 같습니다.
require 'benchmark/ips'
class FooBar
def name; end
end
el = FooBar.new
Benchmark.ips do |x|
x.report('plain') { el.name }
x.report('eval') { eval('el.name') }
x.report('method call') { el.method(:name).call }
x.report('send sym') { el.send(:name) }
x.report('send str') { el.send('name') }
x.compare!
end
결과는 다음과 같습니다.
Warming up --------------------------------------
plain 236.448k i/100ms
eval 20.743k i/100ms
method call 131.408k i/100ms
send sym 205.491k i/100ms
send str 168.137k i/100ms
Calculating -------------------------------------
plain 9.150M (± 6.5%) i/s - 45.634M in 5.009566s
eval 232.303k (± 5.4%) i/s - 1.162M in 5.015430s
method call 2.602M (± 4.5%) i/s - 13.009M in 5.010535s
send sym 6.729M (± 8.6%) i/s - 33.495M in 5.016481s
send str 4.027M (± 5.7%) i/s - 20.176M in 5.027409s
Comparison:
plain: 9149514.0 i/s
send sym: 6729490.1 i/s - 1.36x slower
send str: 4026672.4 i/s - 2.27x slower
method call: 2601777.5 i/s - 3.52x slower
eval: 232302.6 i/s - 39.39x slower
일반 호출이 가장 빠르며 추가 할당, 기호 조회, 메소드 조회 및 평가 만 필요합니다.
에 관해서는 send
기호를 통해, 더 빨리 심볼에 대한 훨씬 더 easer 할당 할 메모리와 같은 문자열을 통해보다. 정의되면 메모리에 장기간 저장되며 재 할당이 없습니다.
같은 이유는 method(:name)
(1) Proc
객체에 대한 메모리를 할당해야 합니다. (2) 우리는 클래스에서 메서드를 호출하여 시간이 걸리는 추가 메서드 조회를 유도합니다.
eval
is runs interpreter so it's the heaviest.
I updated the benchmark from @Stefan to check if there are some speed improvements when saving reference to method. But again – send
is much faster than call
require 'benchmark'
class Foo
def bar; end
end
foo = Foo.new
foo_bar = foo.method(:bar)
Benchmark.bm(4) do |b|
b.report("send") { 1_000_000.times { foo.send(:bar) } }
b.report("call") { 1_000_000.times { foo_bar.call } }
end
These are the results:
user system total real
send 0.080000 0.000000 0.080000 ( 0.088685)
call 0.110000 0.000000 0.110000 ( 0.108249)
So send
seems to be the one to take.
The whole point of send
and eval
is that you can change the command dynamically. If the method you want to execute is fixed, then you can hard-wire that method without using send
or eval
.
receiver.fixed_method(argument)
But when you want to invoke a method that varies or you do not know in advance, then you cannot write that directly. Hence the use of send
or eval
.
receiver.send(method_that_changes_dynamically, argument)
eval "#{code_to_evaluate_that_changes_more_dramatically}"
Additional use of send
is that, as you noticed, you can call a method with explicit receiver using send
.
참고URL : https://stackoverflow.com/questions/17454992/dynamic-method-calling-in-ruby
'IT Share you' 카테고리의 다른 글
새 DbContext ()는 언제 만들어야합니까? (0) | 2020.11.08 |
---|---|
java.net.SocketTimeoutException 가져 오기 : Android에서 연결 시간이 초과되었습니다. (0) | 2020.11.08 |
C # 엔티티 프레임 워크 : 저장소 클래스 내에서 DBContext 클래스의 올바른 사용 (0) | 2020.11.08 |
탭을 사용하지 않고 jupyter 노트북에서 자동 완성을 얻는 방법은 무엇입니까? (0) | 2020.11.08 |
인덱스가 범위를 벗어난 부분 문자열 슬라이싱이 작동하는 이유는 무엇입니까? (0) | 2020.11.08 |