[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Patrzę na ten kod, ale mój mózg nie rejestruje, jak liczba 10 może stać się wynikiem. Czy ktoś mógłby wyjaśnić, co się tutaj dzieje?
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Patrzę na ten kod, ale mój mózg nie rejestruje, jak liczba 10 może stać się wynikiem. Czy ktoś mógłby wyjaśnić, co się tutaj dzieje?
Odpowiedzi:
Możesz myśleć o pierwszym argumencie bloku jako o akumulatorze: wynik każdego uruchomienia bloku jest przechowywany w akumulatorze, a następnie przekazywany do następnego wykonania bloku. W przypadku kodu pokazanego powyżej, domyślnie ustawiasz akumulator wynik na 0. Każde uruchomienie bloku dodaje podaną liczbę do bieżącej sumy, a następnie zapisuje wynik z powrotem w akumulatorze. Następne wywołanie bloku ma tę nową wartość, dodaje ją, zapisuje ponownie i powtarza.
Pod koniec procesu inject zwraca akumulator, który w tym przypadku jest sumą wszystkich wartości w tablicy, czyli 10.
Oto kolejny prosty przykład tworzenia skrótu z tablicy obiektów, kluczowanych przez ich reprezentację w postaci ciągu:
[1,"a",Object.new,:hi].inject({}) do |hash, item|
hash[item.to_s] = item
hash
end
W tym przypadku domyślnie ustawiamy nasz akumulator na pusty hash, a następnie wypełniamy go za każdym razem, gdy blok jest wykonywany. Zauważ, że musimy zwrócić hash jako ostatnią linię bloku, ponieważ wynik bloku zostanie ponownie zapisany w akumulatorze.
result + explanation
jest zarówno transformacją do akumulatora, jak i wartością zwracaną. Jest to ostatnia linia w bloku, która jest niejawnym zwrotem.
inject
przyjmuje wartość, od której zaczyna się (w naszym 0
przykładzie) oraz blok i uruchamia ten blok raz dla każdego elementu listy.
result + element
).Najłatwiejszym sposobem wyjaśnienia tego może być pokazanie, jak działa każdy krok, na przykład; jest to wyimaginowany zestaw kroków pokazujących, jak można ocenić ten wynik:
[1, 2, 3, 4].inject(0) { |result, element| result + element }
[2, 3, 4].inject(0 + 1) { |result, element| result + element }
[3, 4].inject((0 + 1) + 2) { |result, element| result + element }
[4].inject(((0 + 1) + 2) + 3) { |result, element| result + element }
[].inject((((0 + 1) + 2) + 3) + 4) { |result, element| result + element }
(((0 + 1) + 2) + 3) + 4
10
Składnia metody wstrzykiwania jest następująca:
inject (value_initial) { |result_memo, object| block }
Rozwiążmy powyższy przykład tj
[1, 2, 3, 4].inject(0) { |result, element| result + element }
co daje 10 jako wyjście.
Tak więc, zanim zaczniemy, zobaczmy, jakie wartości są przechowywane w każdej zmiennej:
wynik = 0 Zero pochodzi z wstrzyknięcia (wartość), która wynosi 0
element = 1 Jest to pierwszy element tablicy.
Okey !!! Zacznijmy więc rozumieć powyższy przykład
Krok 1 [1, 2, 3, 4].inject(0) { |0, 1| 0 + 1 }
Krok 2 [1, 2, 3, 4].inject(0) { |1, 2| 1 + 2 }
Krok 3 [1, 2, 3, 4].inject(0) { |3, 3| 3 + 3 }
Krok 4 [1, 2, 3, 4].inject(0) { |6, 4| 6 + 4 }
Krok: 5 [1, 2, 3, 4].inject(0) { |10, Now no elements left in the array, so it'll return 10 from this step| }
Tutaj wartości pogrubione-kursywa to elementy pobrane z tablicy, a wartości po prostu pogrubione są wartościami wynikowymi.
Mam nadzieję, że rozumiesz działanie #inject
metody #ruby
.
Kod iteruje po czterech elementach w tablicy i dodaje poprzedni wynik do bieżącego elementu:
Co powiedzieli, ale pamiętaj również, że nie zawsze musisz podawać „wartość początkową”:
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
jest taki sam jak
[1, 2, 3, 4].inject { |result, element| result + element } # => 10
Spróbuj, poczekam.
Gdy żaden argument nie jest przekazywany do wstrzyknięcia, pierwsze dwa elementy są przekazywane do pierwszej iteracji. W powyższym przykładzie wynik to 1, a element to 2 za pierwszym razem, więc do bloku zostanie wysłane jedno wywołanie mniej.
Liczba, którą umieścisz w swojej () in inject, reprezentuje miejsce początkowe, może wynosić 0 lub 1000. Wewnątrz rur masz dwa miejsca na pozycje | x, y |. x = jaka kiedykolwiek liczba miałaś wewnątrz .inject ('x'), a druga reprezentuje każdą iterację twojego obiektu.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
1 + 5 = 6 2 + 6 = 8 3 + 8 = 11 11 + 4 = 15
Wstrzyknąć stosuje blok
result + element
do każdego elementu w tablicy. Dla następnego elementu („element”) wartością zwracaną z bloku jest „wynik”. Sposób, w jaki to nazwałeś (z parametrem), „wynik” zaczyna się od wartości tego parametru. Efektem jest więc sumowanie elementów.
tldr; inject
różni się od map
w jeden ważny sposób: inject
zwraca wartość ostatniego wykonania bloku, a map
zwraca tablicę, po której był iterowany.
Co więcej, wartość każdego wykonania bloku przekazana do następnego wykonania za pośrednictwem pierwszego parametru ( result
w tym przypadku) i można zainicjować tę wartość ((0)
część).
Twój powyższy przykład można zapisać w map
następujący sposób:
result = 0 # initialize result
[1, 2, 3, 4].map { |element| result += element }
# result => 10
Ten sam efekt, ale inject
jest bardziej zwięzły.
Często zdarza się map
, że przypisanie odbywa się w bloku, podczas gdy ocena odbywa się winject
bloku.
Wybór metody zależy od wybranego zakresu result
. Kiedy nie używać tego byłoby coś takiego:
result = [1, 2, 3, 4].inject(0) { |x, element| x + element }
Możesz być taki jak wszyscy: „Słuchaj, właśnie połączyłem to wszystko w jedną linię”, ale tymczasowo przydzieliłeś pamięć x
jako zmienną podstawową, która nie była konieczna, ponieważ już musiałeś result
z nią pracować.
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
jest równoważne z następującym:
def my_function(r, e)
r+e
end
a = [1, 2, 3, 4]
result = 0
a.each do |value|
result = my_function(result, value)
end
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Mówiąc prostym językiem, przechodzisz przez tę tablicę ( [1,2,3,4]
). Będziesz iterować tę tablicę 4 razy, ponieważ są 4 elementy (1, 2, 3 i 4). Metoda iniekcji ma 1 argument (liczbę 0) i dodasz ten argument do pierwszego elementu (0 + 1. To równa się 1). 1 jest zapisywany w „wyniku”. Następnie dodajesz ten wynik (czyli 1) do następnego elementu (1 + 2. To jest 3). Ten zostanie zapisany jako wynik. Kontynuuj: 3 + 3 równa się 6. I na koniec 6 + 4 równa się 10.
Ten kod nie dopuszcza możliwości nieprzekazywania wartości początkowej, ale może pomóc wyjaśnić, co się dzieje.
def incomplete_inject(enumerable, result)
enumerable.each do |item|
result = yield(result, item)
end
result
end
incomplete_inject([1,2,3,4], 0) {|result, item| result + item} # => 10
Zacznij tutaj, a następnie przejrzyj wszystkie metody, które pobierają bloki. http://ruby-doc.org/core-2.3.3/Enumerable.html#method-i-inject
Czy to blok, który cię dezorientuje, czy też dlaczego masz wartość w metodzie? Dobre pytanie. Jaka jest tam metoda operatora?
result.+
Od czego to się zaczyna?
#inject(0)
Możemy to zrobić?
[1, 2, 3, 4].inject(0) { |result, element| result.+ element }
czy to działa?
[1, 2, 3, 4].inject() { |result = 0, element| result.+ element }
Widzisz, opieram się na idei, że po prostu sumuje wszystkie elementy tablicy i zwraca liczbę w notatce, którą widzisz w dokumentach.
Zawsze możesz to zrobić
[1, 2, 3, 4].each { |element| p element }
aby zobaczyć wyliczalną tablicę, przez którą przechodzi iteracja. To podstawowa idea.
Po prostu wstrzykuj lub zmniejszaj, aby otrzymać notatkę lub akumulator, który zostanie wysłany.
Moglibyśmy spróbować uzyskać wynik
[1, 2, 3, 4].each { |result = 0, element| result + element }
ale nic nie wraca, więc działa tak samo jak wcześniej
[1, 2, 3, 4].each { |result = 0, element| p result + element }
w bloku inspektora elementów.
To jest proste i dość łatwe do zrozumienia wyjaśnienie:
Zapomnij o „wartości początkowej”, ponieważ na początku jest ona nieco myląca.
> [1,2,3,4].inject{|a,b| a+b}
=> 10
Możesz to rozumieć jako: wstrzykuję „maszynę dodającą” pomiędzy 1,2,3,4. Oznacza to, że jest to 1 ♫ 2 ♫ 3 ♫ 4, a ♫ jest maszyną sumującą, więc jest to to samo, co 1 + 2 + 3 + 4, a to jest 10.
Możesz faktycznie wstrzyknąć +
między nimi:
> [1,2,3,4].inject(:+)
=> 10
i to jest tak, jakby wstawić a +
pomiędzy 1, 2, 3, 4, co daje 1 + 2 + 3 + 4 i jest to 10. Jest :+
to sposób określania przez Rubiego +
w formie symbolu.
Jest to dość łatwe do zrozumienia i intuicyjne. A jeśli chcesz przeanalizować krok po kroku, jak to działa, to jest tak: biorąc 1 i 2, a teraz dodaj je, a kiedy masz wynik, najpierw zapisz go (czyli 3), a teraz następny jest zapisany wartość 3 i element tablicy 3 przechodzą przez proces a + b, czyli 6 i teraz przechowują tę wartość, a teraz 6 i 4 przechodzą przez proces a + b i wynosi 10. Zasadniczo robisz
((1 + 2) + 3) + 4
i wynosi 10. „Wartość początkowa” 0
jest po prostu „podstawą”. W wielu przypadkach nie jest to potrzebne. Wyobraź sobie, że potrzebujesz 1 * 2 * 3 * 4 i tak jest
[1,2,3,4].inject(:*)
=> 24
i gotowe. Nie potrzebujesz „wartości początkowej”, 1
aby pomnożyć całość 1
.
Istnieje inna forma metody .inject (), która jest bardzo pomocna [4,5] .inject (&: +) Która zsumuje cały element obszaru
Jest taki sam jak ten:
[1,2,3,4].inject(:+)
=> 10