Вопрос

Я нашел этот код в RailsCast:

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

Что это (&:name) в map(&:name) иметь в виду?

Это было полезно?

Решение

Это сокращение от tags.map (& amp;: name.to_proc) .join ('')

Если foo является объектом с методом to_proc , вы можете передать его в метод как & amp; foo , который вызовет foo.to_proc и используйте его как блок метода.

Метод Symbol # to_proc был первоначально добавлен ActiveSupport, но был интегрирован в Ruby 1.8.7. Это его реализация:

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

Другие советы

Еще одна классная стенография, неизвестная многим, это

array.each(&method(:foo))

который является сокращением для

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

При вызове метода (: foo) мы взяли объект Method из self , который представляет его метод foo и использовал & amp; , чтобы указать, что у него есть to_proc метод , который преобразует его в Proc .

Это очень полезно, когда вы хотите использовать стиль без точек . Пример - проверить, есть ли какая-либо строка в массиве, равная строке " foo " . Есть общепринятый способ:

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

И есть бессмысленный способ:

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

Предпочтительный способ должен быть наиболее читаемым.

Это эквивалентно

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

Отметим также, что магия амперсанда #to_proc может работать с любым классом, а не только с Symbol. Многие Rubyists предпочитают определять #to_proc в классе Array:

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

# And then...

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

Ampersand & amp; работает, отправляя сообщение to_proc о своем операнде, который в приведенном выше коде относится к классу Array. И так как я определил метод #to_proc в массиве, строка становится такой:

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

Это сокращение от tags.map {| tag | tag.name} .join ('')

tags.map(&:name)

- это то же самое, что и

tags.map{|tag| tag.name}

& amp;: name просто использует символ в качестве имени метода для вызова.

Ответ Джоша Ли почти правильный, за исключением того, что эквивалентный код Ruby должен был быть следующим.

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

нет

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

С помощью этого кода, когда print [[1,'a'],[2,'b'],[3,'c']].map(&:first) выполняется, Ruby разделяет первый ввод [1,'a'] на 1 и «а», чтобы дать obj 1 и args* 'a', чтобы вызвать ошибку, поскольку объект Fixnum 1 не имеет метода self (то есть :first).


Когда [[1,'a'],[2,'b'],[3,'c']].map(&:first) выполняется;

  1. :first является объектом символа, поэтому, когда &:first передается методу карты в качестве параметра, вызывается символ #to_proc.

  2. карта отправляет сообщение о вызове в :first.to_proc с параметром [1,'a'], например, :first.to_proc.call([1,'a']) выполняется.

  3. Процедура to_proc в классе Символ отправляет сообщение об отправке объекту массива ([1,'a']) с параметром (:first), например, [1,'a'].send(:first) выполняется.

  4. перебирает остальные элементы в [[1,'a'],[2,'b'],[3,'c']] объект.

Это то же самое, что выполнить [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first) выражение.

Здесь происходят две вещи, и важно понимать обе.

Как описано в других ответах, вызывается метод Symbol # to_proc .

Но причина того, что to_proc вызывается для символа, заключается в том, что он передается в map в качестве аргумента блока. Помещение & amp; перед аргументом в вызове метода приводит к его передаче таким способом. Это верно для любого метода Ruby, а не только для map с символами.

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 преобразуется в Proc , поскольку он передается как блок. Мы можем показать это, попытавшись передать процедуру в .map без амперсанда:

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

Несмотря на то, что его не нужно конвертировать, метод не будет знать, как его использовать, потому что он ожидает аргумент блока. Передав его с помощью & amp; , вы получите .map ожидаемый блок.

(& amp;: name) - это сокращение от (& amp;: name.to_proc), оно совпадает с tags.map {| t | t.name} .join ('')

to_proc фактически реализован на C

Хотя у нас уже есть отличные ответы, глядя на новичка, я хотел бы добавить дополнительную информацию:

  

Что означает карта (& amp;: имя) в Ruby?

Это означает, что вы передаете другой метод в качестве параметра функции map. (На самом деле вы передаете символ, который превращается в процесс. Но это не так важно в данном конкретном случае).

Важно то, что у вас есть метод с именем name , который будет использоваться методом карты в качестве аргумента вместо традиционного block стиль.

map (& amp;: name) берет перечисляемый объект (тэги в вашем случае) и запускает метод name для каждого элемента / тэга, выводя каждое возвращаемое значение из метода.

Это сокращение для

array.map { |element| element.name }

, который возвращает массив имен элементов (тегов)

Здесь : name - это символ, который указывает на метод name объекта тега. Когда мы передаем & amp:: name в map , он будет обрабатывать name как объект proc. Для краткости tags.map (& amp;: name) действует как:

tags.map do |tag|
  tag.name
end

это значит

array.each(&:to_sym.to_proc)

Он в основном выполняет вызов метода tag.name для каждого тега в массиве.

Это упрощенная рубиновая стенография.

Это так же, как показано ниже:

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top