Ruby memiliki sebelas metode untuk menemukan elemen dalam array.
Yang disukai adalah include?
atau, untuk akses berulang, membuat Set dan kemudian memanggil include?
atau member?
.
Inilah semuanya:
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
Mereka semua mengembalikan true
nilai ish jika elemen ada.
include?
adalah metode yang disukai. Ini menggunakan for
loop bahasa-C secara internal yang rusak ketika elemen cocok dengan rb_equal_opt/rb_equal
fungsi internal . Itu tidak bisa menjadi jauh lebih efisien kecuali Anda membuat Set untuk cek keanggotaan berulang.
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?
tidak didefinisikan ulang di Array
kelas dan menggunakan implementasi yang tidak dioptimalkan dari Enumerable
modul yang secara harfiah menyebutkan semua elemen:
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;
}
Diterjemahkan ke kode Ruby hal ini tentang hal berikut:
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
Keduanya include?
dan member?
memiliki kompleksitas waktu O (n) karena keduanya mencari array untuk kemunculan pertama dari nilai yang diharapkan.
Kita dapat menggunakan Set untuk mendapatkan O (1) waktu akses dengan biaya harus membuat representasi Hash dari array terlebih dahulu. Jika Anda berulang kali memeriksa keanggotaan pada larik yang sama, investasi awal ini dapat melunasi dengan cepat. Set
tidak diimplementasikan dalam C tetapi sebagai kelas Ruby biasa, masih waktu akses O (1) yang mendasarinya @hash
membuat ini berharga.
Berikut ini adalah implementasi dari kelas Set:
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
Seperti yang Anda lihat kelas Set hanya membuat @hash
instance internal , memetakan semua objek ke true
dan kemudian memeriksa keanggotaan menggunakan Hash#include?
yang diimplementasikan dengan O (1) waktu akses di kelas Hash.
Saya tidak akan membahas tujuh metode lainnya karena semuanya kurang efisien.
Sebenarnya ada lebih banyak metode dengan kompleksitas O (n) di luar 11 yang tercantum di atas, tetapi saya memutuskan untuk tidak mencantumkannya karena mereka memindai seluruh array daripada merusak pada pertandingan pertama.
Jangan gunakan ini:
# bad examples
array.grep(element).any?
array.select { |each| each == element }.size > 0
...