루비에서 map (& : name)은 무엇을 의미합니까?


496

RailsCast 에서이 코드를 찾았 습니다 .

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

무엇은하지 (&:name)map(&:name)평균?


122
그건 그렇고“프레첼 콜론”이라고 들었습니다.
Josh Lee

6
ㅋ. 나는 그것을 앰퍼샌드로 알고 있습니다. 나는 그것이 "프레첼 (pretzel)"이라고 들었다. 그러나 그것은 의미가있다.
DragonFax

74
"pretzel colon"이라고 부르기는 쉽지만, 오해의 소지가 있습니다. 루비에는 "& :"가 없습니다. 앰퍼샌드 (&)는 함께 푸시 된 "단항 앰퍼샌드 연산자"입니다. 무엇이든, 그것은 "프레첼 기호"입니다. 그냥 말하면
fontno

3
tags.map (& : name)은 tags.map {| s | s.name}
kaushal sharma

3
"pretzel colon"은 고통스러운 의학적 상태처럼 들리지만 ... 나는이 상징의 이름을 좋아합니다 :)
zmorris

답변:


517

속기입니다 tags.map(&:name.to_proc).join(' ')

경우 fooA의 객체 인 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

41
이것은 내 것보다 더 나은 대답입니다.
Oliver N.

91
tags.map (: name.to_proc) 자체는 tags.map의 약어입니다. {| tag | tag.name}
Simone Carletti

5
이 유효한 루비 코드가 아닙니다, 당신은 여전히 필요 &, 즉tags.map(&:name.to_proc).join(' ')
horseyguy

5
Symbol # to_proc는 Ruby가 아닌 C로 구현되지만 Ruby에서는 이와 유사합니다.
Andrew Grimm

5
@AndrewGrimm이 코드를 사용하여 Ruby on Rails에 처음 추가되었습니다. 그런 다음 버전 1.8.7에서 기본 루비 기능으로 추가되었습니다.
Cameron Martin

175

많은 사람들에게 알려지지 않은 또 다른 멋진 속기는

array.each(&method(:foo))

이것은 속기입니다

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

호출 하여 메소드 를 나타내는 객체를 method(:foo)가져 와서 Method객체 를로 변환 하는 메소드 가 있음을 나타냅니다 .selffoo&to_proc Proc

포인트없는 스타일 로 작업 할 때 매우 유용합니다 . 예를 들어 배열에 문자열과 같은 문자열이 있는지 확인하는 것입니다 "foo". 일반적인 방법이 있습니다.

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

그리고 포인트없는 방법이 있습니다.

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

가장 좋은 방법은 가장 읽기 쉬운 방법이어야합니다.


25
array.each{|e| foo(e)}어쨌든 :-) +1 어쨌든
Jared Beck

&method?를 사용하여 다른 클래스의 생성자를 매핑 할 수 있습니까?
홀로그램 원리

3
@finishingmove 그래요. 이것을보십시오[1,2,3].map(&Array.method(:new))
Gerry

78

그것은 동등하다

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

45

앰퍼샌드와 #to_proc매직은 Symbol뿐만 아니라 모든 클래스에서 작동 할 수 있습니다. 많은 Rubyist #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!' ) }

이것은 순금입니다!
kubak

38

속기입니다 tags.map { |tag| tag.name }.join(' ')


아니요, Ruby 1.8.7 이상에 있습니다.

맵이나 루비에 대한 간단한 관용구가 항상 특정 방식으로 '&'를 해석합니까?
collimarco

7
@collimarco : jleedev가 그의 답변에서 말했듯이 단항 &연산자 to_proc는 피연산자를 호출 합니다. 따라서 map 메소드에만 국한되지 않으며 실제로 블록을 가져 와서 하나 이상의 인수를 블록에 전달하는 메소드에서 작동합니다.

36
tags.map(&:name)

와 같다

tags.map{|tag| tag.name}

&:name 호출 할 메소드 이름으로 기호를 사용합니다.


1
나는 특별히 procs를위한 것이 아니라 내가 찾던 대답 (그러나 그것은 요청자 질문이었다)
matrim_c

좋은 대답입니다! 나를 위해 명확하게.
apadana

14

Josh Lee의 대답은 동등한 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)실행되고, 루비 제의 입력을 분리 [1,'a']한 내로 'A'에 환원 obj1 args*'A'(입니다 제)에있어서 자기가없는 Fixnum이라는 오브젝트 1과 같은 에러를 야기.


[[1,'a'],[2,'b'],[3,'c']].map(&:first)실행될 때 ;

  1. :first는 Symbol 객체이므로 &:firstmap 메소드에 매개 변수로 제공되면 Symbol # to_proc이 호출됩니다.

  2. map은 매개 변수를 사용하여 호출 메시지를 : first.to_proc로 보냅니다 . [1,'a']예를 들어, :first.to_proc.call([1,'a'])실행됩니다.

  3. Symbol 클래스의 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)표현식 실행과 동일 합니다.


1
조쉬 리의 대답은 절대적 대한 당신의 생각에 의해 볼 수 있듯이, 올바른 [1,2,3,4,5,6].inject(&:+)분사는 두 개의 매개 변수 (메모 및 항목)와 람다 기대하고 - :+.to_proc을 제공 - Proc.new |obj, *args| { obj.send(self, *args) }또는{ |m, o| m.+(o) }
열린 아가시

11

여기서 두 가지 일이 일어나고 있으며 두 가지를 모두 이해하는 것이 중요합니다.

다른 답변에서 설명한 것처럼 Symbol#to_proc메소드가 호출됩니다.

그러나 to_proc심볼에 대한 이유 는 map블록 인수로 전달되기 때문 입니다. &메소드 호출에서 인수 앞에 배치 하면 이러한 방식으로 전달됩니다. 이것은 map심볼 뿐만 아니라 모든 Ruby 메소드에도 적용 됩니다.

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)

SymbolA를 변환됩니다 Proc이 블록으로 전달 있기 때문에. .map앰퍼샌드없이 proc을 전달하여이를 표시 할 수 있습니다 .

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됩니다.


이것은 정직하게 주어진 최고의 답변입니다. 당신은 앰퍼샌드의 메커니즘과 우리가 왜 당신의 대답까지 얻지 못한 proc로 끝나는지를 설명합니다. 감사합니다.
Fralcon

5

(& : name)은 (& : name.to_proc)의 약자입니다. tags.map{ |t| t.name }.join(' ')

to_proc는 실제로 C로 구현됩니다.


5

map (& : name) 은 열거 가능한 객체 (귀하의 경우 태그 )를 가져 와서 각 요소 / 태그에 대해 name 메서드를 실행하여 메서드에서 반환 된 각 값을 출력합니다.

속기입니다

array.map { |element| element.name }

요소 (태그) 이름의 배열을 반환합니다.


3

기본적으로 tag.name배열의 각 태그에서 메소드 호출 을 실행합니다 .

단순화 된 루비 속기입니다.


2

우리는 이미 훌륭한 답변을 얻었지만 초보자의 관점을 통해 추가 정보를 추가하고 싶습니다.

루비에서 map (& : name)은 무엇을 의미합니까?

이는 다른 함수를 매개 변수로 맵 함수에 전달 함을 의미합니다. (실제로 당신은 proc로 변환되는 심볼을 전달하고 있습니다. 그러나 이것은 특별한 경우에는 중요하지 않습니다).

중요한 것은 map 메소드가 전통적인 스타일 대신 인수로 사용할 method이름이 name있다는 것 block입니다.


2

먼저, &:name에 대한 바로 가기입니다 &:name.to_proc. 여기서 (첫 번째) 인수로 객체와 함께 호출 할 때 해당 객체 의 메서드를 호출하는 람다와 동일하지만 람다와 동일하지 않은 것을 :name.to_proc반환 합니다.Procname

반면 둘째 &def foo(&block) ... end변환하는 블록에 전달 foo(A)에 Proc, 그것이 적용 반대 않는다 Proc.

따라서 &:name.to_proc객체를 인수로 사용하여 name메소드를 호출하는 블록입니다 { |o| o.name }.


1

태그 객체 :name의 메소드를 가리키는 기호는 다음과 같습니다 name. 우리가 통과하는 경우 &:namemap, 그것은 취급 name발동 개체로. 간단히 말해 다음과 같이 tags.map(&:name)작동합니다.

tags.map do |tag|
  tag.name
end


0

아래와 같습니다 :

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.