एक सरणी में तत्वों को खोजने के लिए रूबी के पास ग्यारह तरीके हैं।
include?
बार-बार पहुंच के लिए पसंदीदा एक है , एक सेट बनाएं और फिर कॉल करें include?
याmember?
।
यहाँ वे सभी हैं:
array.include?(element) # preferred method
array.member?(element)
array.to_set.include?(element)
array.to_set.member?(element)
array.index(element) > 0
array.find_index(element) > 0
array.index { |each| each == element } > 0
array.find_index { |each| each == element } > 0
array.any? { |each| each == element }
array.find { |each| each == element } != nil
array.detect { |each| each == element } != nil
वे सभी एक लौट आए true
यदि तत्व मौजूद है तो ईश मान ।
include?
पसंदीदा तरीका है। यह for
आंतरिक रूप से टूटने वाली सी-भाषा लूप का उपयोग करता है जब कोई तत्व आंतरिक rb_equal_opt/rb_equal
कार्यों से मेल खाता है । जब तक आप बार-बार सदस्यता जाँच के लिए सेट नहीं बनाते हैं तब तक यह अधिक कुशल नहीं हो सकता है।
VALUE
rb_ary_includes(VALUE ary, VALUE item)
{
long i;
VALUE e;
for (i=0; i<RARRAY_LEN(ary); i++) {
e = RARRAY_AREF(ary, i);
switch (rb_equal_opt(e, item)) {
case Qundef:
if (rb_equal(e, item)) return Qtrue;
break;
case Qtrue:
return Qtrue;
}
}
return Qfalse;
}
member?
Array
वर्ग में पुनर्परिभाषित नहीं किया जाता है और Enumerable
मॉड्यूल से किसी भी तरह से अकल्पित कार्यान्वयन का उपयोग करता है जो शाब्दिक रूप से सभी तत्वों के माध्यम से गणना करता है:
static VALUE
member_i(RB_BLOCK_CALL_FUNC_ARGLIST(iter, args))
{
struct MEMO *memo = MEMO_CAST(args);
if (rb_equal(rb_enum_values_pack(argc, argv), memo->v1)) {
MEMO_V2_SET(memo, Qtrue);
rb_iter_break();
}
return Qnil;
}
static VALUE
enum_member(VALUE obj, VALUE val)
{
struct MEMO *memo = MEMO_NEW(val, Qfalse, 0);
rb_block_call(obj, id_each, 0, 0, member_i, (VALUE)memo);
return memo->v2;
}
रूबी कोड में अनुवादित यह निम्न के बारे में करता है:
def member?(value)
memo = [value, false, 0]
each_with_object(memo) do |each, memo|
if each == memo[0]
memo[1] = true
break
end
memo[1]
end
दोनों include?
औरmember?
ओ (एन) समय की जटिलता है क्योंकि दोनों अपेक्षित मूल्य की पहली घटना के लिए सरणी खोजते हैं।
हम सरणी के पहले हैश प्रतिनिधित्व बनाने की लागत पर O (1) एक्सेस समय प्राप्त करने के लिए एक सेट का उपयोग कर सकते हैं। यदि आप एक ही सरणी पर बार-बार सदस्यता की जांच करते हैं तो यह प्रारंभिक निवेश जल्दी भुगतान कर सकता है। Set
सी में लागू नहीं है, लेकिन सादे रूबी वर्ग के रूप में, अभी भी अंतर्निहित का ओ (1) पहुंच समय है@hash
इस लायक बनाता है।
यहाँ सेट वर्ग का कार्यान्वयन है:
module Enumerable
def to_set(klass = Set, *args, &block)
klass.new(self, *args, &block)
end
end
class Set
def initialize(enum = nil, &block) # :yields: o
@hash ||= Hash.new
enum.nil? and return
if block
do_with_enum(enum) { |o| add(block[o]) }
else
merge(enum)
end
end
def merge(enum)
if enum.instance_of?(self.class)
@hash.update(enum.instance_variable_get(:@hash))
else
do_with_enum(enum) { |o| add(o) }
end
self
end
def add(o)
@hash[o] = true
self
end
def include?(o)
@hash.include?(o)
end
alias member? include?
...
end
जैसा कि आप देख सकते हैं सेट क्लास बस एक आंतरिक @hash
उदाहरण बनाता है , सभी वस्तुओं को मैप true
करता है और फिर सदस्यता का उपयोग करके जांच करता हैHash#include?
करता है, जिसे हैश क्लास में ओ (1) एक्सेस समय के साथ कार्यान्वित किया जाता है।
मैं अन्य सात विधियों पर चर्चा नहीं करूंगा क्योंकि वे सभी कम कुशल हैं।
उपरोक्त सूचीबद्ध 11 से परे ओ (एन) जटिलता के साथ वास्तव में और भी अधिक विधियां हैं, लेकिन मैंने उन्हें सूचीबद्ध नहीं करने का फैसला किया क्योंकि वे पहले मैच में तोड़ने के बजाय पूरे सरणी को स्कैन करते हैं।
इनका उपयोग न करें:
# bad examples
array.grep(element).any?
array.select { |each| each == element }.size > 0
...