Znalazłem ten kod w RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Co robi (&:name)w map(&:name)średniej?
Znalazłem ten kod w RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Co robi (&:name)w map(&:name)średniej?
Odpowiedzi:
To jest skrót od tags.map(&:name.to_proc).join(' ')
Jeśli foojest to obiekt z to_procmetodą, możesz przekazać ją do metody as &foo, która wywoła ją foo.to_proci użyje jako bloku metody.
Symbol#to_procMetoda został dodany przez ActiveSupport ale został zintegrowany Ruby 1.8.7. To jest jego wdrożenie:
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
&, tj.tags.map(&:name.to_proc).join(' ')
Kolejny fajny skrót, nieznany wielu, to
array.each(&method(:foo))
co jest skrótem od
array.each { |element| foo(element) }
Dzwoniąc method(:foo), wzięliśmy Methodobiekt, selfktóry reprezentuje jego foometodę, i użyliśmy &do oznaczenia, że ma to_proc metodę, która konwertuje go na Proc.
Jest to bardzo przydatne, gdy chcesz robić rzeczy bez punktów . Przykładem jest sprawdzenie, czy w tablicy jest jakikolwiek ciąg znaków, który jest taki sam "foo". Istnieje konwencjonalny sposób:
["bar", "baz", "foo"].any? { |str| str == "foo" }
I jest sposób bez punktów:
["bar", "baz", "foo"].any?(&"foo".method(:==))
Preferowany sposób powinien być najbardziej czytelny.
array.each{|e| foo(e)}jest jeszcze krótszy :-) +1 tak czy inaczej
&method?
[1,2,3].map(&Array.method(:new))
Jest to równoważne z
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
Zwróćmy też uwagę, że znaki handlowe i #to_procmagiczne mogą współpracować z dowolną klasą, nie tylko Symbolem. Wielu rubyistów decyduje się zdefiniować #to_procw klasie Array:
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
Ampersand &działa poprzez wysyłanie to_procwiadomości na swoim operandzie, który w powyższym kodzie jest klasy Array. A ponieważ zdefiniowałem #to_procmetodę na tablicy, linia staje się:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
To jest skrót od tags.map { |tag| tag.name }.join(' ')
&operator woła to_procswój operand. Więc nie jest to specyficzne dla metody map i faktycznie działa na każdej metodzie, która pobiera blok i przekazuje jeden lub więcej argumentów do bloku.
Odpowiedź Josha Lee jest prawie poprawna, tyle że równoważny kod Ruby powinien wyglądać następująco.
class Symbol
def to_proc
Proc.new do |receiver|
receiver.send self
end
end
end
nie
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
Z tym kodem, gdy print [[1,'a'],[2,'b'],[3,'c']].map(&:first)jest wykonywany, Ruby dzieli pierwsze wejście [1,'a']na 1 i „a”, aby dać obj1 i args*„a”, aby spowodować błąd, ponieważ obiekt Fixnum 1 nie ma metody self (która jest: pierwsza).
Kiedy [[1,'a'],[2,'b'],[3,'c']].map(&:first)jest wykonywany;
:firstjest obiektem Symbol, więc po &:firstpodaniu metody mapy jako parametru wywoływany jest Symbol # to_proc.
map wysyła komunikat wywołania do: first.to_proc z parametrem [1,'a'], np. :first.to_proc.call([1,'a'])jest wykonywany.
Procedura to_proc w klasie Symbol wysyła komunikat wysyłania do obiektu tablicy ( [1,'a']) z parametrem (: first), np. [1,'a'].send(:first)jest wykonywany.
wykonuje iterację nad resztą elementów w [[1,'a'],[2,'b'],[3,'c']]obiekcie.
Jest to to samo, co wykonywanie [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)wyrażenia.
[1,2,3,4,5,6].inject(&:+)- zastrzyk oczekuje lambdy z dwoma parametrami (notka i przedmiot) i :+.to_procdostarcza ją - Proc.new |obj, *args| { obj.send(self, *args) }lub{ |m, o| m.+(o) }
Działają się tutaj dwie rzeczy i ważne jest, aby zrozumieć oba.
Jak opisano w innych odpowiedziach, Symbol#to_procmetoda jest wywoływana.
Ale powodem to_procwywołania tego symbolu jest to, że jest przekazywany mapjako argument blokowy. Umieszczenie &przed argumentem w wywołaniu metody powoduje, że jest on przekazywany w ten sposób. Dotyczy to każdej metody Ruby, nie tylkomap symboli.
def some_method(*args, &block)
puts "args: #{args.inspect}"
puts "block: #{block.inspect}"
end
some_method(:whatever)
# args: [:whatever]
# block: nil
some_method(&:whatever)
# args: []
# block: #<Proc:0x007fd23d010da8>
some_method(&"whatever")
# TypeError: wrong argument type String (expected Proc)
# (String doesn't respond to #to_proc)
SymbolZostaje zamieniana na Procponieważ jest przekazywana w postaci bloku. Możemy to pokazać, próbując przekazać procesor .mapbez znaku handlowego:
arr = %w(apple banana)
reverse_upcase = proc { |i| i.reverse.upcase }
reverse_upcase.is_a?(Proc)
=> true
arr.map(reverse_upcase)
# ArgumentError: wrong number of arguments (1 for 0)
# (map expects 0 positional arguments and one block argument)
arr.map(&reverse_upcase)
=> ["ELPPA", "ANANAB"]
Mimo że nie trzeba go konwertować, metoda nie będzie wiedziała, jak go użyć, ponieważ oczekuje argumentu blokowego. Podanie go &daje oczekiwany .mapblok.
Zasadniczo wykonuje wywołanie metody tag.namedla każdego znacznika w tablicy.
Jest to uproszczony rubinowy skrót.
Chociaż mamy już świetne odpowiedzi, patrząc z perspektywy początkującego chciałbym dodać dodatkowe informacje:
Co oznacza mapa (i: nazwa) w Ruby?
Oznacza to, że przekazujesz inną metodę jako parametr do funkcji mapy. (W rzeczywistości podajesz symbol, który zamienia się w proc. Ale to nie jest tak ważne w tym konkretnym przypadku).
Ważne jest to, że masz methodnazwę, namektóra będzie używana przez metodę map jako argument zamiast tradycyjnego blockstylu.
Po pierwsze, &:namejest skrótem do &:name.to_proc, w którym :name.to_proczwraca Proc(coś, co jest podobne, ale nie identyczne jak lambda), które po wywołaniu z obiektem jako (pierwszy) argument wywołuje namemetodę na tym obiekcie.
Po drugie, podczas gdy &w def foo(&block) ... endkonwertuje blok przekazany na fooa Proc, robi się odwrotnie, gdy jest stosowany do a Proc.
Zatem &:name.to_procjest blok, który przyjmuje obiekt jako argument i wywołuje na nim namemetodę, tj { |o| o.name }.
To jest tak samo jak poniżej:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end