Co oznacza map ( & : name) W Rubim?

Znalazłem ten kod w A RailsCast :

def tag_names
  @tag_names || tags.map(&:name).join(' ')
end

Co oznacza (&:name) w map(&:name)?

Author: user22a6db72d7249, 2009-08-01

14 answers

To skrót od tags.map(&:name.to_proc).join(' ')

Jeśli foo jest obiektem z metodą to_proc, możesz przekazać go do metody jako &foo, która wywoła foo.to_proc i użyje go jako bloku metody.

Metoda Symbol#to_proc została pierwotnie dodana przez ActiveSupport, ale została zintegrowana z Rubim 1.8.7. Jest to jego realizacja:

class Symbol
  def to_proc
    Proc.new do |obj, *args|
      obj.send self, *args
    end
  end
end
 478
Author: Josh Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-27 16:43:12

Kolejnym fajnym skrótem, nieznanym wielu, jest

array.each(&method(:foo))

Który jest skrótem

array.each { |element| foo(element) }

Wywołując method(:foo) wzięliśmy obiekt Method z self, który reprezentuje jego metodę foo, i użyliśmy &, aby zaznaczyć, że ma to_proc metoda , która przekształca ją w Proc.

Jest to bardzo przydatne, gdy chcesz robić rzeczy styl bez punktów. Przykładem jest sprawdzenie, czy w tablicy znajduje się łańcuch równy łańcuchowi "foo". Jest sposób konwencjonalny:

["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.

 161
Author: Gerry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-09 18:44:35

Jest odpowiednikiem

def tag_names
  @tag_names || tags.map { |tag| tag.name }.join(' ')
end
 71
Author: Sophie Alpert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-01 17:39:52

Zauważmy również, że Ampersand #to_proc magia może działać z dowolną klasą, nie tylko symbolem. Wielu Rubyistów decyduje się zdefiniować #to_proc w klasie tablicy:

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 wysłanie to_proc wiadomości na swoim operandu, który w powyższym kodzie należy do klasy Array. A ponieważ zdefiniowałem metodę #to_proc na tablicy, linia staje się:

[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
 39
Author: Boris Stitnicky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-05 19:28:18

To skrót od tags.map { |tag| tag.name }.join(' ')

 35
Author: Oliver N.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-01 18:06:00
tags.map(&:name)

Jest tym samym co

tags.map{|tag| tag.name}

&:name po prostu używa symbolu jako nazwy metody do wywołania.

 27
Author: Albert.Qing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-01 03:23:29

Odpowiedź Josha Lee jest prawie poprawna, z wyjątkiem tego, że odpowiedni kod Ruby powinien być następujący.

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 wykonywane, Ruby dzieli pierwsze wejście [1,'a'] na 1 i 'a', aby dać obj 1 i args* 'a', aby spowodować błąd, ponieważ obiekt Fixnum 1 nie ma metody self (która jest :first).


Kiedy [[1,'a'],[2,'b'],[3,'c']].map(&:first) jest wykonywane;

  1. :first jest obiektem symbolicznym, więc gdy &:first jest dana metodzie map jako parametr, Symbol#to_proc jest wywoływany.

  2. Map wysyła wiadomość wywołania do: first.to_proc z parametrem [1,'a'], np. :first.to_proc.call([1,'a']) jest wykonywany.

  3. Procedura To_proc w klasie Symbol wysyła wiadomość do obiektu array ([1,'a']) z parametrem (:first), np. jest wykonywana [1,'a'].send(:first).

  4. Iteracja nad resztą elementów w obiekcie [[1,'a'],[2,'b'],[3,'c']].

Jest to to samo co wykonywanie [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first) wyrażenia.

 14
Author: prosseek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-23 21:14:16

Dzieją się tu dwie rzeczy i ważne jest, aby zrozumieć obie.

Jak opisano w innych odpowiedziach, Metoda Symbol#to_proc jest wywoływana.

Ale powodem wywołania symbolu jest to, że jest on przekazywany do map jako 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 tylko map z symbolami.

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)

Symbol zostaje zamienione na Proc, ponieważ jest przeszedł jako blok. Możemy to pokazać, próbując przekazać proc do .map bez ampersand:

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 przekonwertować, metoda nie będzie wiedziała, jak go użyć, ponieważ oczekuje argumentu blokowego. Podanie go z & daje .map oczekiwany blok.

 8
Author: devpuppy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-09 01:43:55

( & : name) jest skrótem od ( & : name.to_proc) jest taki sam jak tags.map{ |t| t.name }.join(' ')

To_proc jest faktycznie zaimplementowany w C

 5
Author: tessie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-11 05:55:00

Chociaż mamy już świetne odpowiedzi, patrząc z perspektywy początkującego, chciałbym dodać dodatkowe informacje:

Co oznacza map ( & : name) W Rubim?

Oznacza to, że przekazujesz inną metodę jako parametr do funkcji map. (W rzeczywistości przekazujesz symbol, który zostaje przekształcony w proc. Ale to nie jest tak ważne w tym konkretnym przypadku).

Ważne jest to, że masz method o nazwie name, która będzie używana za pomocą metody map jako argumentu zamiast tradycyjnego stylu block.

 2
Author: Jonathan Duarte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-08 15:45:53

Tutaj :name jest symbolem wskazującym na metodę name obiektu tag. Kiedy przechodzimy &:name do map, będzie on traktował {[2] } jako obiekt proc. W skrócie, tags.map(&:name) działa jak:

tags.map do |tag|
  tag.name
end
 1
Author: timlentse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-30 06:30:44

Oznacza

array.each(&:to_sym.to_proc)
 1
Author: mminski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-20 12:25:09

Jest taki sam jak poniżej:

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end
 0
Author: user3078630,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-27 09:32:18

Map ( & : name) pobiera obiekt zliczalny (w Twoim przypadku znaczniki) i uruchamia metodę name dla każdego elementu/znacznika, wyprowadzając każdą zwracaną wartość z metody.

Jest skrótem

array.map { |element| element.name }

Który zwraca tablicę nazw elementów (znaczników)

 0
Author: Sunda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-01 04:48:06