użyć .include? metoda . Zwraca wartość logiczną, która jest tym, czego chcesz. W twoim przypadku po prostu wpisz: [„Cat”, „Dog”, „Bird”]. Include („Dog”), a to powinno zwrócić wartość logiczną true.
Jwan622
nie używać obejmują? Metoda, jeśli chcesz sprawdzić wiele razy, czy inna wartość ma być obecna w tablicy, czy nie, ponieważ zawiera? za każdym razem iteruje tablicę wykonującą operację O (n) w celu wyszukiwania za każdym razem, Zamiast tworzyć skrót hash = arr.map {|x| [x,true]}.to_h, teraz sprawdź, czy hash.has_key? 'Dog' zwraca true, czy nie
aqfaridi 28.04.17
3
Naprawdę nie możesz tego zrobić całkowicie „bez zapętlania się”. Jest to logicznie niemożliwe, komputer po prostu nie może mieć pewności, czy tablica zawiera element bez zapętlania elementów, aby sprawdzić, czy którykolwiek z nich jest tym, którego szuka. Oczywiście, chyba że jest pusty. Więc myślę, że nie potrzebujesz pętli.
Tim M.
Zobacz testy porównawcze poniżej, aby sprawdzić różnicę różnych sposobów znajdowania elementu w tablicy i zestawie. stackoverflow.com/a/60404934/128421
Alternatywna składnia:%w(Cat Dog Bird).include? 'Dog'
scarver2
184
Czasami żałuję, że nie zawiera „zawiera”. Zawsze mylę to z dołączeniami.
Henley Chiu
19
Pragnę zauważyć, że wewnętrznie #include?nadal wykonuje pętlę. Koder jest jednak zapisywany przed jawnym zapisaniem pętli. Dodałem odpowiedź, która naprawdę wykonuje to zadanie bez zapętlania.
Boris Stitnicky,
8
@HenleyChiu I, jak to się nazywało[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'
4
@AlfonsoVergara Tak, każde rozwiązanie dla tablicy musi wykonać jakieś wewnętrzne zapętlenie; nie ma możliwości przetestowania członkostwa w tablicy bez zapętlenia. Jeśli nie chcesz wykonywać żadnych pętli, nawet wewnętrznie, musisz użyć innej struktury danych, na przykład idealnej tabeli skrótów z kluczami o stałej wielkości. Biorąc pod uwagę, że nie ma możliwości przetestowania członkostwa w tablicy bez zapętlenia wewnętrznego, zinterpretowałem to pytanie jako „bez konieczności pisania pętli osobiście”
Brian Campbell
250
Jest in?metoda w ActiveSupport(część Rails) od v3.1, jak podkreślił @campaterson. Więc w Railsach lub jeśli możesz require 'active_support', możesz napisać:
'Unicorn'.in?(['Cat','Dog','Bird'])# => false
OTOH, w samym Rubimie nie ma inoperatora ani #in?metody, mimo że był on wcześniej proponowany, w szczególności przez Yusuke Endoh, czołowego członka rubinowego rdzenia.
Jak wskazano przez innych, sposobem odwróconej include?występuje dla wszystkich EnumerableS tym Array, Hash, Set, Range:
Zauważ, że jeśli masz wiele wartości w tablicy, wszystkie zostaną sprawdzone jedna po drugiej (tj. O(n)), Podczas gdy wyszukiwanie skrótu będzie miało stały czas (tj O(1).). Jeśli więc tablica jest stała, na przykład dobrym pomysłem jest użycie zestawu . Na przykład:
Szybkie badanie ujawnia, że dzwoniąc include?na 10 elementu Setwynosi około 3,5x szybciej niż nazywając ją na ekwiwalentArray (jeśli element nie zostanie znaleziony).
Ostatnia uwaga końcowa: bądź ostrożny przy użyciu include?na Range, są subtelności, więc zapoznaj się z dokumentem i porównaj z cover?...
@jahrichie, co dokładnie uważacie za „starszą składnię” w tej odpowiedzi, opcjonalne nawiasy?
Dennis
3
zgadzam się z @Dennis, to nie jest starsze, nawiasy są opcjonalne, aw większości przypadków DOBRA PRAKTYKA ... spróbuj użyć opcji bez nawiasów w jednym wierszu, jeśli na przykład zdanie, co miałem na myśli to, że w zależności od twojego przypadku ty powinien lub musi używać nawiasów klamrowych lub nie (nie związany w ogóle ze „starymi” składniami ruby)
d1jhoni1b
Jest to jedyna składnia, jaką mogę dostać do pracy w ramach operacji trójskładnikowej.
Michael Cameron
49
Użyj Enumerable#include:
a =%w/CatDogBird/
a.include?'Dog'
Lub, jeśli wykonano wiele testów, 1 możesz pozbyć się pętli (która nawet include?ma) i przejść od O (n) do O (1) za pomocą:
h =Hash[[a, a].transpose]
h['Dog']
1. Mam nadzieję, że jest to oczywiste, ale odrzucam zastrzeżenia: tak, tylko dla kilku odnośników Hash [] i transponuj operacje dominują w profilu i każdy z nich jest O (n) .
Bardzo przydatne, jeśli chcesz sprawdzić, czy którykolwiek / wszystkie te ciągi są zawarte w innym ciągu / stałej
thanikkal
43
Ruby ma jedenaście metod wyszukiwania elementów w tablicy.
Preferowanym jest include?lub, w celu wielokrotnego dostępu, utwórz Zestaw, a następnie zadzwoń include?lubmember? .
Oto wszystkie z nich:
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
Wszystkie zwracają a true wartość ish, jeśli element jest obecny.
include?jest preferowaną metodą. Wykorzystuje forwewnętrznie pętlę języka C, która pęka, gdy element pasuje do rb_equal_opt/rb_equalfunkcji wewnętrznych . Nie może stać się o wiele bardziej wydajny, jeśli nie utworzysz zestawu do wielokrotnych kontroli członkostwa.
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)){caseQundef:if(rb_equal(e, item))returnQtrue;break;caseQtrue:returnQtrue;}}returnQfalse;}
member?nie jest przedefiniowany w Arrayklasie i używa niezoptymalizowanej implementacji z Enumerablemodułu, która dosłownie wylicza wszystkie elementy:
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();}returnQnil;}
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;}
Zarówno include?imember? mają O (n) od złożoności przeszukaniu tablicę do pierwszego wystąpienia wartości oczekiwanej.
Możemy użyć Zestawu, aby uzyskać czas dostępu O (1) kosztem konieczności najpierw utworzenia reprezentacji Hash tablicy. Jeśli wielokrotnie sprawdzasz członkostwo w tej samej tablicy, ta początkowa inwestycja może się szybko zwrócić. Setnie jest zaimplementowany w C, ale jako zwykła klasa Ruby, wciąż czas dostępu O (1) instrumentu bazowego@hash sprawia, że jest to opłacalne.
Jak widać klasa Set po prostu tworzy @hashinstancję wewnętrzną , mapuje wszystkie obiekty, truea następnie sprawdza członkostwo za pomocąHash#include? którego jest implementowany czas dostępu O (1) w klasie Hash.
Nie będę omawiać pozostałych siedmiu metod, ponieważ wszystkie są mniej wydajne.
W rzeczywistości istnieje jeszcze więcej metod o złożoności O (n) powyżej 11 wymienionych powyżej, ale postanowiłem ich nie wymieniać, ponieważ skanują one całą tablicę, a nie psują się przy pierwszym dopasowaniu.
Nie używaj tych:
# bad examples
array.grep(element).any?
array.select {|each| each == element }.size >0...
Jak bezczelnie jest mówić, że Ruby ma dokładnie 11 sposobów na zrobienie czegokolwiek! Nie wcześniej niż mówisz, że ktoś wskaże, że przegapiłeś numer 12, następnie numer 13 i tak dalej. Aby wyrazić swoje zdanie, zasugeruję inne sposoby, ale najpierw pozwól mi zakwestionować wymienione przez 11ciebie sposoby. Po pierwsze, trudno policzyć indexi find_index(lub findi detect) jako osobne metody, ponieważ są to po prostu różne nazwy dla tej samej metody. Po drugie, wszystkie wyrażenia, które kończą się, > 0są niepoprawne, co z pewnością było przeoczeniem. (cd.)
Cary Swoveland,
... arr.index(e)na przykład zwraca 0if arr[0] == e. Przypomnisz arr.index(e)zwroty, niljeśli enie są obecne. indexnie można jednak użyć, jeśli ktoś szuka nilw arr. (Ten sam problem z rindex, którego nie ma na liście.) Przekształcenie tablicy w zbiór, a następnie zastosowanie metod ustawiania, jest nieco rozciągnięte. Dlaczego więc nie przekonwertować na skrót (z kluczami z tablicy i dowolnych wartości), a następnie użyć metod skrótu? Nawet jeśli konwersja na zestaw jest OK, istnieją inne metody zestawu, których można użyć, np !arr.to_set.add?(e). (cd.)
Cary Swoveland,
... Zgodnie z obietnicą, oto jeszcze kilka metod, które mogłyby zostać wykorzystane: arr.count(e) > 0, arr != arr.dup.delete(e) , arr != arr - [e]i arr & [e] == [e]. Można również zatrudnić selecti reject.
Cary Swoveland,
Zdecydowanie polecam użycie zestawu, jeśli kryterium jest takie, że żadne duplikaty nie są dozwolone, i wiedząc, czy dany element istnieje na liście. Zestaw jest O wiele szybszy. Tablice naprawdę nie powinny być używane, gdy potrzebne jest wyszukiwanie; Lepiej są używane jako kolejka, w której rzeczy są tymczasowo przechowywane do przetworzenia w kolejności. Hash i Set lepiej sprawdzają, czy coś istnieje.
Tin Man
30
Sugeruje kilka odpowiedzi Array#include?, ale jest jedna ważna uwaga: patrząc na źródło, nawet Array#include?wykonuje pętlę:
rb_ary_includes(VALUE ary, VALUE item){
long i;for(i=0; i<RARRAY_LEN(ary); i++){if(rb_equal(RARRAY_AREF(ary, i), item)){returnQtrue;}}returnQfalse;}
Sposobem na sprawdzenie obecności słowa bez zapętlania jest zbudowanie trie dla tablicy. Istnieje wiele implementacji Trie (Google „Ruby Trie”). Użyję rambling-triew tym przykładzie:
a =%w/cat dog bird/
require 'rambling-trie'# if necessary, gem install rambling-trie
trie =Rambling::Trie.create {|trie| a.each do|e| trie << e end}
A teraz jesteśmy gotowi przetestować obecność różnych słów w tablicy bez zapętlania jej w O(log n)czasie, z tą samą prostotą składniową, jak Array#include?przy użyciu sublinear Trie#include?:
a.each do ... endUmm ... nie jestem pewien, jak to nie jest pętla
Klamka
27
Zauważ, że tak naprawdę obejmuje to pętlę; wszystko, co nie jest O (1), zawiera jakąś pętlę. Zdarza się, że jest to pętla nad znakami ciągu wejściowego. Zwróć też uwagę na wspomnianą już odpowiedź Set#include?dla osób, którym zależy na wydajności; w połączeniu z używaniem symboli zamiast ciągów, może to być średnia wielkość liter O (1) (jeśli używasz ciągów, to po prostu obliczenie skrótu to O (n), gdzie n jest długością łańcucha). Lub jeśli chcesz korzystać z bibliotek stron trzecich, możesz użyć idealnego skrótu, który jest najgorszym przypadkiem O (1).
Brian Campbell
4
AFAIK, Set używa skrótów do indeksowania swoich elementów, więc w rzeczywistości Set#include?powinien mieć złożoność O (1) dla dobrze rozłożonego Set(dokładniej O (rozmiar wejściowy) dla skrótu i O (log (n / numer-wiadra)) dla poszukiwanie)
Uri Agassi
11
Koszt stworzenia i utrzymania trie jest taki sam. Jeśli wykonujesz wiele operacji wyszukiwania na tablicy, to warto poświęcić pamięć i czas wypełniania trie i utrzymywania jej, ale dla pojedynczych, a nawet setek lub tysięcy kontroli, O (n) jest idealnie odpowiednie. Inną opcją, która nie wymaga dodawania zależności, byłoby posortowanie tablicy lub utrzymanie jej w posortowanej kolejności, w którym to przypadku można użyć operacji wyszukiwania binarnego O (lg n) w celu sprawdzenia włączenia.
mówi
1
@speakingcode, możesz mieć rację z pragmatycznego punktu widzenia. Ale OP prosi o „sprawdzenie, czy wartość istnieje, nic więcej, bez zapętlenia”. Kiedy napisałem tę odpowiedź, było tu wiele pragmatycznych rozwiązań, ale żadne z nich nie spełniłoby dosłownie wymagań pytającego. Twoja obserwacja, że BST są powiązane z próbami, jest poprawna, ale w przypadku ciągów trie jest właściwym narzędziem do pracy, nawet Wikipedia wie tyle . Złożoność konstruowania i utrzymywania dobrze wdrożonego trie jest zaskakująco korzystna.
Boris Stitnicky
18
Jeśli nie chcesz zapętlać, nie możesz tego zrobić za pomocą tablic. Zamiast tego powinieneś użyć zestawu.
require 'set'
s =Set.new
100.times{|i| s <<"foo#{i}"}
s.include?("foo99")=>true[1,2,3,4,5,6,7,8].to_set.include?(4)=>true
Zestawy działają wewnętrznie jak Hashes, więc Ruby nie musi przeglądać kolekcji w celu znalezienia elementów, ponieważ jak sama nazwa wskazuje, generuje skróty kluczy i tworzy mapę pamięci, dzięki czemu każdy skrót wskazuje na określony punkt w pamięci. Poprzedni przykład wykonany za pomocą skrótu:
Minusem jest to, że zestawy i klucze skrótu mogą zawierać tylko unikalne przedmioty, a jeśli dodasz dużo przedmiotów, Ruby będzie musiała przerobić całą rzecz po określonej liczbie przedmiotów, aby zbudować nową mapę, która pasuje do większej przestrzeni na klucze. Aby dowiedzieć się więcej na ten temat, polecam obejrzeć „ MountainWest RubyConf 2014 - Big O in a Homemade Hash” autorstwa Nathana Longa .
Jeśli użyjesz funkcji wykrywania, możesz przynajmniej zmniejszyć zapętlenie. Wykrywanie zatrzyma się przy pierwszym „wykrytym” elemencie (blok przekazany dla elementu zostanie oceniony jako prawdziwy). Ponadto możesz określić, co należy zrobić, jeśli nic nie zostanie wykryte (możesz przekazać lambda).
aenw
1
@aenw nie include?kończy się przy pierwszym trafieniu?
Kimmo Lehto,
masz całkowitą rację. Jestem tak przyzwyczajony do używania funkcji wykrywania, że zapomniałem o włączeniu. dziękuję za komentarz - zapewniłem odświeżenie mojej wiedzy.
dniu
include?zatrzymuje się przy pierwszym trafieniu, ale jeśli trafienie znajduje się na końcu listy ... Każde rozwiązanie, które opiera się na tablicy do przechowywania, będzie miało obniżającą się wydajność w miarę powiększania się listy, szczególnie gdy trzeba znaleźć element na końcu lista. Hash i Set nie mają tego problemu, podobnie jak uporządkowana lista i wyszukiwanie binarne.
Tin Man
Na tym właśnie polegała ta odpowiedź :)
Kimmo Lehto
17
Jest to inny sposób, aby to zrobić: użyj Array#index metody.
Zwraca indeks pierwszego wystąpienia elementu w tablicy.
Na przykład:
a =['cat','dog','horse']if a.index('dog')
puts "dog exists in the array"end
index() może również wziąć blok:
Na przykład:
a =['cat','dog','horse']
puts a.index {|x| x.match /o/}
Zwraca indeks pierwszego słowa w tablicy zawierającej literę „o”.
indexwciąż iteruje tablicę, po prostu zwraca wartość elementu.
Tin Man
13
Śmieszny fakt,
Możesz użyć, *aby sprawdzić członkostwo w tablicy w casewyrażeniach.
case element
when*array
...else...end
Zwróć uwagę na trochę * w klauzuli when, która sprawdza członkostwo w tablicy.
Obowiązuje wszystkie zwykłe magiczne zachowanie operatora splat, więc na przykład, jeśli arraytak naprawdę nie jest tablicą, ale pojedynczym elementem, dopasuje ten element.
Byłoby to również powolne pierwsze sprawdzenie w oświadczeniu przypadku, więc użyłbym go w ostatnim whenmożliwym, aby inne, szybsze kontrole szybko zostały wyeliminowane.
Tin Man
9
Istnieje wiele sposobów osiągnięcia tego celu. Kilka z nich to:
a =[1,2,3,4,5]2.in? a #=> true8.in? a #=> false
a.member?1#=> true
a.member?8#=> false
Parametr Hash # has_key? Tablica # include
Złożoność czasowa Operacja O (1) Operacja O (n)
Typ dostępu Uzyskuje dostęp do skrótu [klawisz], jeśli przechodzi przez każdy element
zwraca dowolną wartość z tablicy do niej
true jest zwracane do znalezionych wartości w tablicy
Hash # has_key? połączenie
połączenie
W przypadku jednorazowego sprawdzenia korzystanie include?jest w porządku
Czy nadal nie zapętlasz tablicy, aby przekonwertować ją na skrót?
chrisjacob
2
tak, ale teraz mam wstępnie obliczoną odpowiedź, aby sprawdzić, czy jakiś element istnieje w tablicy, czy nie. Teraz następnym razem, gdy szukam innego słowa, zajmie O (1) dla zapytania, prawda, chociaż wstępne obliczenia zajmą O (n). Właściwie użyłem to? w mojej aplikacji wewnętrznej pętli do sprawdzania, czy dany element istnieje w tablicy, czy nie, psuje to wydajność, sprawdzało w ruby-prof, to było wąskie gardło
aqfaridi
1
.... ale przestrzeń O (n), a także nie, to czas O (n), ponieważ, jak słusznie wskazuje @ chris72205, musisz najpierw wykonać iterację po tablicy. O (1) + O (n) = O (n). Czy to w rzeczywistości jest gorsze niż włączenie?
Rambatino
Koleś, o którym mówiłem, nie używaj pętli wewnętrznej, ponieważ sprawdzanie za jednym razem przy użyciu opcji włączania jest w porządku. Przeczytaj więc najpierw przypadek użycia. Lepiej, jeśli chcesz sprawdzić wiele razy w pętli.
aqfaridi
Konwersja tablicy na skrót jest kosztowna, ale użycie tablicy do wyszukiwania jest niewłaściwą strukturą. Tablice są lepsze niż kolejki, w których kolejność może być ważna; Skróty i zestawy są lepsze, gdy ważne jest włączenie / istnienie / unikalność. Zacznij od odpowiedniego pojemnika, a problem jest sporny.
Tin Man
4
Dzięki temu dowiesz się nie tylko, że istnieje, ale także ile razy się pojawia:
Nie ma sensu z tego korzystać, chyba że chcesz wiedzieć, ile razy się pojawia, jednak, gdy .any?powróci, gdy tylko znajdzie pierwszy pasujący element, .countzawsze przetworzy całą tablicę.
Zaz
Chociaż technicznie to powie, czy coś istnieje, NIE jest to również właściwy sposób, jeśli zależy Ci na szybkości.
Tin Man
4
Za to, co jest warte, dokumenty Ruby są niesamowitym źródłem tego rodzaju pytań.
Zwróciłbym również uwagę na długość przeszukiwanej tablicy. Theinclude?Metoda będzie uruchomić przeszukiwanie liniowe z O (N) złożoności, które można uzyskać bardzo brzydki zależności od rozmiaru tablicy.
Jeśli pracujesz z dużą (posortowaną) tablicą, rozważę napisanie algorytmu wyszukiwania binarnego, który nie powinien być zbyt trudny i ma najgorszy przypadek O (log n).
Lub jeśli używasz Ruby 2.0, możesz skorzystać z bsearch.
Wyszukiwanie binarne zakłada, że tablica jest posortowana (lub uporządkowana w jakiejś formie), co może być kosztowne w przypadku dużych tablic, często negując przewagę.
Tin Man
Wyszukiwanie binarne wymaga również porównywalności wszystkich par elementów <=>, co nie zawsze jest prawdą. Załóżmy na przykład, że elementy tablicy były skrótami.
Cary Swoveland,
1
@CarySwoveland powinno być mniej więcej sugerowane, że elementy w posortowanej tablicy są porównywalne.
davissp14,
4
Możesz spróbować:
Przykład: jeśli Cat i Dog istnieją w tablicy:
(['Cat','Dog','Bird']&['Cat','Dog']).size ==2#or replace 2 with ['Cat','Dog].size
Twoje wyrażenie regularne /[,|.| |]#{v.to_s}[,|.| |]/sprawia, że myślę, że chciałeś znaleźć „nazwę akcji otoczoną jednym z: przecinek, kropka, spacja lub nic”, ale są pewne subtelne błędy. "|update|"wróci [:update]i "update"wróci []. Klasy znaków ( [...]) nie używają potoków ( |) do rozdzielania znaków. Nawet jeśli zmienimy je na group ( (...)), nie możesz dopasować pustego znaku. Więc regex, który prawdopodobnie chciałeś, to/(,|\.| |^)#{v.to_s}(,|\.| |$)/
bkDJ
wyrażenie regularne jest w porządku (sprawdzone rubular.com) - i @Rambatino: dlaczego miałoby być na przykład Amazon Echo;) Można powiedzieć: „dodaj kurczaka do mojej listy zakupów” (i - no cóż - wtedy będziesz musiał dodaj: dodaj do tablicy, ale myślę, że rozumiesz o co chodzi;)
walt_die
1
„Wyrażenie regularne jest w porządku (zaznaczone rubular.com)”. Nie jest ok Twoje wyrażenie regularne nie będzie pasowało do słowa kluczowego na początku lub na końcu łańcucha (np. „Zaktualizuj profil mojego brata”). Jeśli nie chcesz dopasowywać początku ani końca, to wyrażenie regularne nadal nie jest w porządku, ponieważ klasa znaków po obu stronach słowa kluczowego powinna mieć wartość/[,. ]/
Jest to strasznie nieefektywny sposób na zrobienie tego! Nie oddaję głosu, ponieważ nie jest to technicznie niepoprawne i ktoś może dowiedzieć się czegoś o Ruby, czytając go, ale istnieje wiele lepszych odpowiedzi powyżej.
jibberia
Conehead, możesz uprościć arr != arr - [e]. arr & [e] == [e]jest inną drogą wzdłuż tych samych linii.
Cary Swoveland,
@CarySwoveland Nie śmiej się z kapelusza czarodzieja ;-)
Wand Maker
Odniosłem się do głowy czarodzieja, a nie do kapelusza, z największym szacunkiem.
jeśli nie chcesz używać include?, możesz najpierw owinąć element w tablicy, a następnie sprawdzić, czy zawinięty element jest równy przecięciu tablicy i owiniętego elementu. Zwróci wartość logiczną opartą na równości.
Zawsze uważam za interesujące przeprowadzenie testów porównawczych, aby zobaczyć względną szybkość różnych sposobów robienia czegoś.
Znalezienie elementu tablicy na początku, w środku lub na końcu wpłynie na wszelkie wyszukiwania liniowe, ale prawie nie wpłynie na wyszukiwanie względem zbioru.
Konwersja tablicy na zestaw spowoduje trafienie w czas przetwarzania, więc utwórz zestaw z tablicy raz lub zacznij od zestawu od samego początku.
Co po uruchomieniu na moim laptopie z systemem Mac OS powoduje:
Ruby v.2.7.0--------------------First--------------------Running each test 65536 times.Test will take about 5 seconds.
_array_include? is similar to _array_index
_array_index is similar to _array_find_index
_array_find_index is faster than _array_any_each by 2x±1.0
_array_any_each is similar to _array_index_each
_array_index_each is similar to _array_find_index_each
_array_find_index_each is faster than _array_member? by 4x±1.0
_array_member? is faster than _array_detect_each by 2x±1.0
_array_detect_each is similar to _array_find_each
--------------------Middle--------------------Running each test 32 times.Test will take about 2 seconds.
_array_include? is similar to _array_find_index
_array_find_index is similar to _array_index
_array_index is faster than _array_member? by 2x±0.1
_array_member? is faster than _array_index_each by 2x±0.1
_array_index_each is similar to _array_find_index_each
_array_find_index_each is similar to _array_any_each
_array_any_each is faster than _array_detect_each by 30.000000000000004%±10.0%
_array_detect_each is similar to _array_find_each
--------------------Last--------------------Running each test 16 times.Test will take about 2 seconds.
_array_include? is faster than _array_find_index by 10.000000000000009%±10.0%
_array_find_index is similar to _array_index
_array_index is faster than _array_member? by 3x±0.1
_array_member? is faster than _array_find_index_each by 2x±0.1
_array_find_index_each is similar to _array_index_each
_array_index_each is similar to _array_any_each
_array_any_each is faster than _array_detect_each by 30.000000000000004%±10.0%
_array_detect_each is similar to _array_find_each
--------------------Sets vs.Array.include?----------------------------------------First--------------------Running each test 65536 times.Test will take about 1 second.
_array_include? is similar to _set_include?
_set_include? is similar to _set_member?--------------------Middle--------------------Running each test 65536 times.Test will take about 2 minutes.
_set_member? is similar to _set_include?
_set_include? is faster than _array_include? by 1400x±1000.0--------------------Last--------------------Running each test 65536 times.Test will take about 4 minutes.
_set_member? is similar to _set_include?
_set_include? is faster than _array_include? by 3000x±1000.0
Zasadniczo wyniki mówią mi, aby użyć zestawu do wszystkiego, jeśli mam szukać włączenia, chyba że mogę zagwarantować, że pierwszy element jest tym, którego chcę, co jest mało prawdopodobne. Wstawianie elementów do skrótu jest nieco obciążone, ale czasy wyszukiwania są o wiele szybsze, nie sądzę, że powinno to być kiedykolwiek brane pod uwagę. Ponownie, jeśli musisz go przeszukać, nie używaj tablicy, użyj zestawu. (Lub hash.)
Im mniejsza tablica, tym szybciej będą działać metody tablic, ale nadal nie nadążą, choć w małych tablicach różnica może być niewielka.
„First”, „Middle” i „Ostatni” odzwierciedlać użycie first, size / 2a lastza ARRAYza element poszukiwany. Ten element będzie używany podczas wyszukiwania zmiennych ARRAYi SET.
Niewielkie zmiany zostały wprowadzone do metod, które w porównaniu do > 0ponieważ badanie powinno być >= 0dla indexprób typu.
Więcej informacji na temat Fruity i jego metodologii można znaleźć w README .
hash = arr.map {|x| [x,true]}.to_h
, teraz sprawdź, czyhash.has_key? 'Dog'
zwraca true, czy nieOdpowiedzi:
Poszukujesz
include?
:źródło
%w(Cat Dog Bird).include? 'Dog'
#include?
nadal wykonuje pętlę. Koder jest jednak zapisywany przed jawnym zapisaniem pętli. Dodałem odpowiedź, która naprawdę wykonuje to zadanie bez zapętlania.[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'
Jest
in?
metoda wActiveSupport
(część Rails) od v3.1, jak podkreślił @campaterson. Więc w Railsach lub jeśli możeszrequire 'active_support'
, możesz napisać:OTOH, w samym Rubimie nie ma
in
operatora ani#in?
metody, mimo że był on wcześniej proponowany, w szczególności przez Yusuke Endoh, czołowego członka rubinowego rdzenia.Jak wskazano przez innych, sposobem odwróconej
include?
występuje dla wszystkichEnumerable
S tymArray
,Hash
,Set
,Range
:Zauważ, że jeśli masz wiele wartości w tablicy, wszystkie zostaną sprawdzone jedna po drugiej (tj.
O(n)
), Podczas gdy wyszukiwanie skrótu będzie miało stały czas (tjO(1)
.). Jeśli więc tablica jest stała, na przykład dobrym pomysłem jest użycie zestawu . Na przykład:Szybkie badanie ujawnia, że dzwoniąc
include?
na 10 elementuSet
wynosi około 3,5x szybciej niż nazywając ją na ekwiwalentArray
(jeśli element nie zostanie znaleziony).Ostatnia uwaga końcowa: bądź ostrożny przy użyciu
include?
naRange
, są subtelności, więc zapoznaj się z dokumentem i porównaj zcover?
...źródło
#in?
w swoim rdzeniu, jeśli używasz Railsów, jest on dostępny. api.rubyonrails.org/classes/Object.html#method-i-in-3F (Wiem, że jest to pytanie Ruby, a nie Rails, ale może pomóc każdemu, kto chce użyć#in?
w Railsach. Wygląda na to, że zostało dodane w Railsach 3.1 apidock.com/rails/Object/in%3FPróbować
źródło
Użyj
Enumerable#include
:Lub, jeśli wykonano wiele testów, 1 możesz pozbyć się pętli (która nawet
include?
ma) i przejść od O (n) do O (1) za pomocą:1. Mam nadzieję, że jest to oczywiste, ale odrzucam zastrzeżenia: tak, tylko dla kilku odnośników Hash [] i transponuj operacje dominują w profilu i każdy z nich jest O (n) .
źródło
Jeśli chcesz sprawdzić przez blok, możesz spróbować
any?
luball?
.Aby uzyskać więcej informacji, zobacz Wyliczalny .
Inspiracją dla mnie było „ oceń, czy tablica zawiera jakieś rubiny ”
źródło
Ruby ma jedenaście metod wyszukiwania elementów w tablicy.
Preferowanym jest
include?
lub, w celu wielokrotnego dostępu, utwórz Zestaw, a następnie zadzwońinclude?
lubmember?
.Oto wszystkie z nich:
Wszystkie zwracają a
true
wartość ish, jeśli element jest obecny.include?
jest preferowaną metodą. Wykorzystujefor
wewnętrznie pętlę języka C, która pęka, gdy element pasuje dorb_equal_opt/rb_equal
funkcji wewnętrznych . Nie może stać się o wiele bardziej wydajny, jeśli nie utworzysz zestawu do wielokrotnych kontroli członkostwa.member?
nie jest przedefiniowany wArray
klasie i używa niezoptymalizowanej implementacji zEnumerable
modułu, która dosłownie wylicza wszystkie elementy:Przetłumaczone na kod Ruby robi to o:
Zarówno
include?
imember?
mają O (n) od złożoności przeszukaniu tablicę do pierwszego wystąpienia wartości oczekiwanej.Możemy użyć Zestawu, aby uzyskać czas dostępu O (1) kosztem konieczności najpierw utworzenia reprezentacji Hash tablicy. Jeśli wielokrotnie sprawdzasz członkostwo w tej samej tablicy, ta początkowa inwestycja może się szybko zwrócić.
Set
nie jest zaimplementowany w C, ale jako zwykła klasa Ruby, wciąż czas dostępu O (1) instrumentu bazowego@hash
sprawia, że jest to opłacalne.Oto implementacja klasy Set:
Jak widać klasa Set po prostu tworzy
@hash
instancję wewnętrzną , mapuje wszystkie obiekty,true
a następnie sprawdza członkostwo za pomocąHash#include?
którego jest implementowany czas dostępu O (1) w klasie Hash.Nie będę omawiać pozostałych siedmiu metod, ponieważ wszystkie są mniej wydajne.
W rzeczywistości istnieje jeszcze więcej metod o złożoności O (n) powyżej 11 wymienionych powyżej, ale postanowiłem ich nie wymieniać, ponieważ skanują one całą tablicę, a nie psują się przy pierwszym dopasowaniu.
Nie używaj tych:
źródło
11
ciebie sposoby. Po pierwsze, trudno policzyćindex
ifind_index
(lubfind
idetect
) jako osobne metody, ponieważ są to po prostu różne nazwy dla tej samej metody. Po drugie, wszystkie wyrażenia, które kończą się,> 0
są niepoprawne, co z pewnością było przeoczeniem. (cd.)arr.index(e)
na przykład zwraca0
ifarr[0] == e
. Przypomniszarr.index(e)
zwroty,nil
jeślie
nie są obecne.index
nie można jednak użyć, jeśli ktoś szukanil
warr
. (Ten sam problem zrindex
, którego nie ma na liście.) Przekształcenie tablicy w zbiór, a następnie zastosowanie metod ustawiania, jest nieco rozciągnięte. Dlaczego więc nie przekonwertować na skrót (z kluczami z tablicy i dowolnych wartości), a następnie użyć metod skrótu? Nawet jeśli konwersja na zestaw jest OK, istnieją inne metody zestawu, których można użyć, np!arr.to_set.add?(e)
. (cd.)arr.count(e) > 0
,arr != arr.dup.delete(e)
,arr != arr - [e]
iarr & [e] == [e]
. Można również zatrudnićselect
ireject
.Sugeruje kilka odpowiedzi
Array#include?
, ale jest jedna ważna uwaga: patrząc na źródło, nawetArray#include?
wykonuje pętlę:Sposobem na sprawdzenie obecności słowa bez zapętlania jest zbudowanie trie dla tablicy. Istnieje wiele implementacji Trie (Google „Ruby Trie”). Użyję
rambling-trie
w tym przykładzie:A teraz jesteśmy gotowi przetestować obecność różnych słów w tablicy bez zapętlania jej w
O(log n)
czasie, z tą samą prostotą składniową, jakArray#include?
przy użyciu sublinearTrie#include?
:źródło
a.each do ... end
Umm ... nie jestem pewien, jak to nie jest pętlaSet#include?
dla osób, którym zależy na wydajności; w połączeniu z używaniem symboli zamiast ciągów, może to być średnia wielkość liter O (1) (jeśli używasz ciągów, to po prostu obliczenie skrótu to O (n), gdzie n jest długością łańcucha). Lub jeśli chcesz korzystać z bibliotek stron trzecich, możesz użyć idealnego skrótu, który jest najgorszym przypadkiem O (1).Set
używa skrótów do indeksowania swoich elementów, więc w rzeczywistościSet#include?
powinien mieć złożoność O (1) dla dobrze rozłożonegoSet
(dokładniej O (rozmiar wejściowy) dla skrótu i O (log (n / numer-wiadra)) dla poszukiwanie)Jeśli nie chcesz zapętlać, nie możesz tego zrobić za pomocą tablic. Zamiast tego powinieneś użyć zestawu.
Zestawy działają wewnętrznie jak Hashes, więc Ruby nie musi przeglądać kolekcji w celu znalezienia elementów, ponieważ jak sama nazwa wskazuje, generuje skróty kluczy i tworzy mapę pamięci, dzięki czemu każdy skrót wskazuje na określony punkt w pamięci. Poprzedni przykład wykonany za pomocą skrótu:
Minusem jest to, że zestawy i klucze skrótu mogą zawierać tylko unikalne przedmioty, a jeśli dodasz dużo przedmiotów, Ruby będzie musiała przerobić całą rzecz po określonej liczbie przedmiotów, aby zbudować nową mapę, która pasuje do większej przestrzeni na klucze. Aby dowiedzieć się więcej na ten temat, polecam obejrzeć „ MountainWest RubyConf 2014 - Big O in a Homemade Hash” autorstwa Nathana Longa .
Oto punkt odniesienia:
A wyniki:
źródło
include?
kończy się przy pierwszym trafieniu?include?
zatrzymuje się przy pierwszym trafieniu, ale jeśli trafienie znajduje się na końcu listy ... Każde rozwiązanie, które opiera się na tablicy do przechowywania, będzie miało obniżającą się wydajność w miarę powiększania się listy, szczególnie gdy trzeba znaleźć element na końcu lista. Hash i Set nie mają tego problemu, podobnie jak uporządkowana lista i wyszukiwanie binarne.Jest to inny sposób, aby to zrobić: użyj
Array#index
metody.Zwraca indeks pierwszego wystąpienia elementu w tablicy.
Na przykład:
index()
może również wziąć blok:Na przykład:
Zwraca indeks pierwszego słowa w tablicy zawierającej literę „o”.
źródło
index
wciąż iteruje tablicę, po prostu zwraca wartość elementu.Śmieszny fakt,
Możesz użyć,
*
aby sprawdzić członkostwo w tablicy wcase
wyrażeniach.Zwróć uwagę na trochę
*
w klauzuli when, która sprawdza członkostwo w tablicy.Obowiązuje wszystkie zwykłe magiczne zachowanie operatora splat, więc na przykład, jeśli
array
tak naprawdę nie jest tablicą, ale pojedynczym elementem, dopasuje ten element.źródło
when
możliwym, aby inne, szybsze kontrole szybko zostały wyeliminowane.Istnieje wiele sposobów osiągnięcia tego celu. Kilka z nich to:
źródło
Object#in?
został dodany tylko do Railsów (tj.ActiveSupport
) V3.1 +. Nie jest dostępny w podstawowym Ruby.Jeśli chcesz sprawdzić wielokrotność razy dla dowolnego klucza, przekonwertuj
arr
nahash
, a teraz zaznacz O (1)Wydajność skrótu # has_key? w porównaniu z tablicą # include?
W przypadku jednorazowego sprawdzenia korzystanie
include?
jest w porządkuźródło
Dzięki temu dowiesz się nie tylko, że istnieje, ale także ile razy się pojawia:
źródło
.any?
powróci, gdy tylko znajdzie pierwszy pasujący element,.count
zawsze przetworzy całą tablicę.Za to, co jest warte, dokumenty Ruby są niesamowitym źródłem tego rodzaju pytań.
Zwróciłbym również uwagę na długość przeszukiwanej tablicy. The
include?
Metoda będzie uruchomić przeszukiwanie liniowe z O (N) złożoności, które można uzyskać bardzo brzydki zależności od rozmiaru tablicy.Jeśli pracujesz z dużą (posortowaną) tablicą, rozważę napisanie algorytmu wyszukiwania binarnego, który nie powinien być zbyt trudny i ma najgorszy przypadek O (log n).
Lub jeśli używasz Ruby 2.0, możesz skorzystać z
bsearch
.źródło
<=>
, co nie zawsze jest prawdą. Załóżmy na przykład, że elementy tablicy były skrótami.Możesz spróbować:
Przykład: jeśli Cat i Dog istnieją w tablicy:
Zamiast:
Uwaga:
member?
iinclude?
są takie same.To może wykonać pracę w jednej linii!
źródło
Jeśli nie chcemy tego używać,
include?
działa to również:źródło
źródło
['Cat', nil, 'Dog'].detect { |x| x == nil } #=> nil
. Zostałnil
znaleziony?Co powiesz na ten sposób?
źródło
Jeśli próbujesz to zrobić w teście jednostkowym MiniTest , możesz użyć
assert_includes
. Przykład:źródło
Jest na to odwrócenie.
Załóżmy, że tablica jest
[ :edit, :update, :create, :show ]
, być może, całymi siedmioma grzechami śmiertelnymi / uspokajającymi .I dalej zabawka z pomysłem wyciągnięcia prawidłowej akcji z jakiegoś sznurka:
Następnie:
źródło
/[,|.| |]#{v.to_s}[,|.| |]/
sprawia, że myślę, że chciałeś znaleźć „nazwę akcji otoczoną jednym z: przecinek, kropka, spacja lub nic”, ale są pewne subtelne błędy."|update|"
wróci[:update]
i"update"
wróci[]
. Klasy znaków ([...]
) nie używają potoków (|
) do rozdzielania znaków. Nawet jeśli zmienimy je na group ((...)
), nie możesz dopasować pustego znaku. Więc regex, który prawdopodobnie chciałeś, to/(,|\.| |^)#{v.to_s}(,|\.| |$)/
/[,. ]/
Jeśli chcesz zwrócić wartość nie tylko prawda lub fałsz, użyj
Zwróci „Pies”, jeśli istnieje na liście, w przeciwnym razie zero.
źródło
array.any?{|x| x == 'Dog'}
jeśli nie chcesz, prawda / fałsz (nie wartości), ale także chcą porównać przeciwko bloku jak ten.Oto jeszcze jeden sposób, aby to zrobić:
źródło
arr != arr - [e]
.arr & [e] == [e]
jest inną drogą wzdłuż tych samych linii.źródło
źródło
in?
wymagaActiveSupport
, aby być importowane:require active_support
.jeśli nie chcesz używać
include?
, możesz najpierw owinąć element w tablicy, a następnie sprawdzić, czy zawinięty element jest równy przecięciu tablicy i owiniętego elementu. Zwróci wartość logiczną opartą na równości.źródło
Zawsze uważam za interesujące przeprowadzenie testów porównawczych, aby zobaczyć względną szybkość różnych sposobów robienia czegoś.
Znalezienie elementu tablicy na początku, w środku lub na końcu wpłynie na wszelkie wyszukiwania liniowe, ale prawie nie wpłynie na wyszukiwanie względem zbioru.
Konwersja tablicy na zestaw spowoduje trafienie w czas przetwarzania, więc utwórz zestaw z tablicy raz lub zacznij od zestawu od samego początku.
Oto kod testu porównawczego:
Co po uruchomieniu na moim laptopie z systemem Mac OS powoduje:
Zasadniczo wyniki mówią mi, aby użyć zestawu do wszystkiego, jeśli mam szukać włączenia, chyba że mogę zagwarantować, że pierwszy element jest tym, którego chcę, co jest mało prawdopodobne. Wstawianie elementów do skrótu jest nieco obciążone, ale czasy wyszukiwania są o wiele szybsze, nie sądzę, że powinno to być kiedykolwiek brane pod uwagę. Ponownie, jeśli musisz go przeszukać, nie używaj tablicy, użyj zestawu. (Lub hash.)
Im mniejsza tablica, tym szybciej będą działać metody tablic, ale nadal nie nadążą, choć w małych tablicach różnica może być niewielka.
„First”, „Middle” i „Ostatni” odzwierciedlać użycie
first
,size / 2
alast
zaARRAY
za element poszukiwany. Ten element będzie używany podczas wyszukiwania zmiennychARRAY
iSET
.Niewielkie zmiany zostały wprowadzone do metod, które w porównaniu do
> 0
ponieważ badanie powinno być>= 0
dlaindex
prób typu.Więcej informacji na temat Fruity i jego metodologii można znaleźć w README .
źródło