Что означает map (&: name) в Ruby?

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

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

Что означает (&:name) in map(&:name)?

Ответ 1

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

Если foo - это объект с методом to_proc, вы можете передать его методу как &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

Ответ 2

Еще одно замечательное сокращение, неизвестное многим,

array.each(&method(:foo))

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

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

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

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

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

И есть беспутный путь:

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

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

Ответ 3

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

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

Ответ 4

Покажем также, что магизация amperand #to_proc может работать с любым классом, а не только с символом. Многие рубисты предпочитают определять #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!"]

Амперсанд & работает, отправив сообщение to_proc в свой операнд, который в приведенном выше коде имеет класс Array. И так как я определил метод #to_proc в массиве, строка будет выглядеть следующим образом:

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

Ответ 5

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

Ответ 6

tags.map(&:name)

То же, что и

tags.map{|tag| tag.name}

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

Ответ 7

Ответ Джоша Ли почти правильный, за исключением того, что эквивалентный код 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 и 'a', чтобы дать obj 1 и args* 'a', чтобы вызвать ошибку, поскольку объект Fixnum 1 делает не имеет метода self (который есть: первый).


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

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

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

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

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

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

Ответ 8

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

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

Но причина to_proc вызывается в символе, потому что она передается map в качестве аргумента блока. Размещение & перед аргументом в вызове метода заставляет его передавать этот путь. Это справедливо для любого метода 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, поскольку он передается как блок. Мы можем показать это, пытаясь передать 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"]

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

Ответ 9

(&: name) сокращен для (&: name.to_proc), он аналогичен tags.map{ |t| t.name }.join(' ')

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

Ответ 10

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

Что означает map (&: name) в Ruby?

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

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

Ответ 11

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

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

array.map { |element| element.name }

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

Ответ 12

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

tags.map do |tag|
  tag.name
end

Ответ 13

это означает

array.each(&:to_sym.to_proc)

Ответ 14

Это то же самое, что и ниже:

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end

Ответ 15

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

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