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

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

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

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

456
задан collimarco 01 авг. '09 в 20:35
источник поделиться
14 ответов

Это сокращение для 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
478
ответ дан Josh Lee 01 авг. '09 в 20:50
источник поделиться

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

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(:==))

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

161
ответ дан Gerry 08 марта '12 в 21:07
источник поделиться

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

def tag_names
  @tag_names || tags.map { |tag| tag.name }.join(' ')
end
71
ответ дан Sophie Alpert 01 авг. '09 в 20:39
источник поделиться

Покажем также, что магизация 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!' ) }
39
ответ дан Boris Stitnicky 20 нояб. '12 в 15:38
источник поделиться

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

35
ответ дан Oliver N. 01 авг. '09 в 20:37
источник поделиться
tags.map(&:name)

То же, что и

tags.map{|tag| tag.name}

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

27
ответ дан Albert.Qing 01 нояб. '16 в 6:23
источник поделиться

Ответ Джоша Ли почти правильный, за исключением того, что эквивалентный код 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).

14
ответ дан prosseek 24 янв. '14 в 0:08
источник поделиться

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

Как описано в других ответах, вызывается метод 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 ожидаемый блок.

8
ответ дан devpuppy 09 апр. '16 в 4:43
источник поделиться

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

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

5
ответ дан tessie 25 авг. '16 в 8:50
источник поделиться

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

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

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

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

2
ответ дан Jonathan Duarte 08 дек. '17 в 18:23
источник поделиться

это означает

array.each(&:to_sym.to_proc)
1
ответ дан mminski 20 дек. '16 в 15:25
источник поделиться

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

tags.map do |tag|
  tag.name
end
1
ответ дан timlentse 30 июня '16 в 9:30
источник поделиться

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

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

array.map { |element| element.name }

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

0
ответ дан Sunda 01 сент. '18 в 7:48
источник поделиться

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

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end
0
ответ дан user3078630 27 июня '16 в 12:32
источник поделиться

Другие вопросы по меткам