Domanda

Ho trovato questo codice in a RailsCast :

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

Cosa significa (& amp;: name) nella map (& amp;: name) ?

È stato utile?

Soluzione

È una scorciatoia per tags.map (& amp;: name.to_proc) .join ('')

Se foo è un oggetto con un metodo to_proc , puoi passarlo a un metodo come & amp; foo , che chiamerà foo.to_proc e usalo come blocco del metodo.

Il metodo Symbol # to_proc è stato originariamente aggiunto da ActiveSupport ma è stato integrato in Ruby 1.8.7. Questa è la sua implementazione:

class Symbol
  def to_proc
    Proc.new do |obj, *args|
      obj.send self, *args
    end
  end
end

Altri suggerimenti

Un'altra bella scorciatoia, sconosciuta a molti, è

array.each(&method(:foo))

che è una scorciatoia per

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

Chiamando il metodo (: foo) abbiamo preso un oggetto Method da self che rappresenta il suo metodo foo e ha utilizzato & amp; per indicare che ha un to_proc metodo che lo converte in Proc .

Questo è molto utile quando vuoi fare cose senza punto . Un esempio è verificare se esiste una stringa in un array uguale alla stringa " foo " . C'è il modo convenzionale:

["bar", "baz", "foo"].any? { |str| str == "foo" }

E c'è il modo senza punti:

["bar", "baz", "foo"].any?(&"foo".method(:==))

Il modo preferito dovrebbe essere quello più leggibile.

È equivalente a

def tag_names
  @tag_names || tags.map { |tag| tag.name }.join(' ')
end

Mentre notiamo anche che la e commerciale #to_proc può funzionare con qualsiasi classe, non solo con Symbol. Molti rubyisti scelgono di definire #to_proc sulla classe Array:

class Array
  def to_proc
    proc { |receiver| receiver.send *self }
  end
end

# And then...

[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]

La e commerciale & amp; funziona inviando il messaggio to_proc sul suo operando, che, nel codice sopra, è di classe Array. E poiché ho definito il metodo #to_proc su Array, la linea diventa:

[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }

È una scorciatoia per tags.map {| tag | tag.name} .join ('')

tags.map(&:name)

è uguale a

tags.map{|tag| tag.name}

& amp;: name utilizza semplicemente il simbolo come nome del metodo da chiamare.

La risposta di Josh Lee è quasi corretta, tranne per il fatto che il codice Ruby equivalente avrebbe dovuto essere il seguente.

class Symbol
  def to_proc
    Proc.new do |receiver|
      receiver.send self
    end
  end
end

non

class Symbol
  def to_proc
    Proc.new do |obj, *args|
      obj.send self, *args
    end
  end
end

Con questo codice, quando print [[1, 'a'], [2, 'b'], [3, 'c']]. map (& amp;: first) è eseguito, Ruby divide il primo input [1, 'a'] in 1 e 'a' per dare obj 1 e args * 'a 'per causare un errore poiché l'oggetto Fixnum 1 non ha il metodo self (che è: primo).


Quando viene eseguito [[1, 'a'], [2, 'b'], [3, 'c']]. map (& amp;: first) ;

  1. : first è un oggetto Symbol, quindi quando & amp;: first viene assegnato a un metodo map come parametro, viene richiamato Symbol # to_proc.

  2. la mappa invia il messaggio di chiamata a: first.to_proc con il parametro [1, 'a'] , ad es. : first.to_proc.call ([1, 'a' ]) viene eseguito.

  3. La procedura
  4. to_proc nella classe Symbol invia un messaggio di invio a un oggetto array ( [1, 'a'] ) con il parametro (: primo), ad es. [1, 'a']. send (: first) viene eseguito.

  5. scorre il resto degli elementi nell'oggetto [[1, 'a'], [2, 'b'], [3, 'c']] .

È lo stesso dell'esecuzione di [[1, 'a'], [2, 'b'], [3, 'c']]. map (| e | e.first) espressione.

Qui stanno accadendo due cose ed è importante capirle entrambe.

Come descritto in altre risposte, viene chiamato il metodo Symbol # to_proc .

Ma il motivo per cui to_proc viene chiamato sul simbolo è perché viene passato a map come argomento di blocco. Posizionare & amp; davanti a un argomento in una chiamata di metodo fa sì che venga passato in questo modo. Questo vale per qualsiasi metodo Ruby, non solo per map con simboli.

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)

Il Symbol viene convertito in Proc perché è passato come blocco. Possiamo mostrarlo provando a passare un proc a .map senza la e commerciale:

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"]

Anche se non ha bisogno di essere convertito, il metodo non saprà come usarlo perché si aspetta un argomento a blocchi. Passandolo con & amp; dà a .map il blocco che si aspetta.

(& amp;: name) è l'abbreviazione di (& amp;: name.to_proc) è uguale a tags.map {| t | t.name} .join ('')

to_proc è effettivamente implementato in C

Anche se abbiamo già ottime risposte, guardando nella prospettiva di un principiante vorrei aggiungere ulteriori informazioni:

  

Cosa significa map (& amp;: name) in Ruby?

Ciò significa che stai passando un altro metodo come parametro alla funzione mappa. (In realtà stai passando un simbolo che viene convertito in un proc. Ma questo non è così importante in questo caso particolare).

L'importante è che tu abbia un metodo chiamato name che sarà usato dal metodo map come argomento invece del tradizionale blocco stile.

map (& amp;: name) prende un oggetto enumerabile (tag nel tuo caso) ed esegue il metodo name per ogni elemento / tag, producendo ogni valore restituito dal metodo.

È una scorciatoia per

array.map { |element| element.name }

che restituisce la matrice dei nomi degli elementi (tag)

Qui : name è il simbolo che punta al metodo name dell'oggetto tag. Quando passiamo & amp;: name a map , tratteremo name come un oggetto proc. In breve, tags.map (& amp;: name) si comporta come:

tags.map do |tag|
  tag.name
end

significa

array.each(&:to_sym.to_proc)

Esegue sostanzialmente la chiamata del metodo tag.name su ogni tag dell'array.

È una scorciatoia rubino semplificata.

È lo stesso di seguito:

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top