Ruby에서 map (& : method) 구문에 인수를 제공 할 수 있습니까?


116

다음 Ruby 속기 ( ais an array)에 익숙 할 것입니다 .

a.map(&:method)

예를 들어, irb에서 다음을 시도하십시오.

>> a=[:a, 'a', 1, 1.0]
=> [:a, "a", 1, 1.0]
>> a.map(&:class)
=> [Symbol, String, Fixnum, Float]

구문 a.map(&:class)a.map {|x| x.class}.

" Ruby에서 map (& : name)은 무엇을 의미합니까? " 에서이 구문에 대해 자세히 읽어보십시오 .

구문을 통해 각 배열 요소에 대해 &:class메서드를 호출 class합니다.

내 질문은 : 메서드 호출에 인수를 제공 할 수 있습니까? 그렇다면 어떻게?

예를 들어, 다음 구문을 어떻게 변환합니까?

a = [1,3,5,7,9]
a.map {|x| x + 2}

받는 사람 &:구문?

나는 &:구문이 더 낫다는 것을 제안하지 않습니다 . 나는 &:인수와 함께 구문 을 사용하는 메커니즘에 관심이 있습니다.

+Integer 클래스의 메서드 라는 것을 알고 있다고 가정합니다 . irb에서 다음을 시도 할 수 있습니다.

>> a=1
=> 1
>> a+(1)
=> 2
>> a.send(:+, 1)
=> 2

답변:


139

다음 Symbol과 같이 간단한 패치를 만들 수 있습니다 .

class Symbol
  def with(*args, &block)
    ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
  end
end

이를 통해 다음을 수행 할 수 있습니다.

a = [1,3,5,7,9]
a.map(&:+.with(2))
# => [3, 5, 7, 9, 11] 

그러나 여러 매개 변수를 전달하는 것과 같은 다른 멋진 기능도 많이 있습니다.

arr = ["abc", "babc", "great", "fruit"]
arr.map(&:center.with(20, '*'))
# => ["********abc*********", "********babc********", "*******great********", "*******fruit********"]
arr.map(&:[].with(1, 3))
# => ["bc", "abc", "rea", "rui"]
arr.map(&:[].with(/a(.*)/))
# => ["abc", "abc", "at", nil] 
arr.map(&:[].with(/a(.*)/, 1))
# => ["bc", "bc", "t", nil] 

그리고 inject두 개의 인수를 블록에 전달하는을 사용하여 작업 할 수도 있습니다 .

%w(abecd ab cd).inject(&:gsub.with('cde'))
# => "cdeeecde" 

또는 [속기] 블록 속기 블록 으로 전달하는 것과 같은 멋진 기능 :

[['0', '1'], ['2', '3']].map(&:map.with(&:to_i))
# => [[0, 1], [2, 3]]
[%w(a b), %w(c d)].map(&:inject.with(&:+))
# => ["ab", "cd"] 
[(1..5), (6..10)].map(&:map.with(&:*.with(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]] 

다음은 @ArupRakshit과 더 자세히 설명하는 대화
입니다. Ruby에서 map (& : method) 구문에 인수를 제공 할 수 있습니까?


@amcaplan이에 제안으로 아래의 코멘트 는 이름을 바꿀 경우, 당신은 짧은 구문을 만들 수 with에 방법을 call. 이 경우, 루비에는이 특별한 방법에 대한 단축키가 내장되어 .()있습니다.

따라서 위와 같이 사용할 수 있습니다.

class Symbol
  def call(*args, &block)
    ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
  end
end

a = [1,3,5,7,9]
a.map(&:+.(2))
# => [3, 5, 7, 9, 11] 

[(1..5), (6..10)].map(&:map.(&:*.(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]] 

5
좋아, 이것이 Ruby 코어의 일부 였으면 좋겠다!
Jikku Jose

6
@UriAgassi 많은 라이브러리가 이것을 수행하기 때문에 좋은 습관이 아닙니다. Symbol#with코어 라이브러리에 존재하지 않을 수 있고 그 메소드를 정의하는 것은 기존 메소드를 재정의하는 것보다 덜 파괴적 이기는하지만 여전히 루비 라이브러리의 코어 클래스 구현을 변경 (즉, 덮어 쓰기)하고 있습니다. 연습은 매우 드물게 그리고 매우 조심스럽게 수행되어야합니다. \ n \ n 기존 클래스에서 상속하고 새로 생성 된 클래스를 수정하십시오. 이것은 일반적으로 핵심 루비 클래스를 변경하는 부정적인 부작용없이 비슷한 결과를 얻습니다.
rudolph9

2
@ rudolph9 - I BEG 차이 - "덮어 쓰기"의 정의를 작성하는 것입니다 이상 기록 된 코드를 사용할 수없는 이상하다는 것을 의미 무언가를, 이것은 분명히 사실이 아니다. Symbol클래스 상속에 대한 귀하의 제안과 관련하여 -그것은 (가능하다면) 사소한 것이 아닙니다 new. 개선의 목적 ...이를 사용하는 구현을 보여주고 비슷한 결과를 얻을 수 있다면 공유 해주세요!
Uri Agassi

3
이 솔루션은 마음에 들지만 더 재미있게 즐길 수있을 것 같습니다. with메서드 를 정의하는 대신 call. 그런 다음 방법을 사용하기 a.map(&:+.(2))때문에 같은 일을 할 수 있습니다 . 그리고 당신이 그것에있는 동안, 당신은 재미있는 것들을 쓸 수 있습니다 .-일종의 LISPy 같은 느낌, 아니? object.()#call:+.(2).(3) #=> 5
amcaplan

2
이것을 핵심으로보고 싶어요-약간의 설탕을 사용할 수있는 일반적인 패턴입니다 .map (& : foo)
Stephen

48

귀하의 예를 들어 할 수 있습니다 a.map(&2.method(:+)).

Arup-iMac:$ pry
[1] pry(main)> a = [1,3,5,7,9]
=> [1, 3, 5, 7, 9]
[2] pry(main)> a.map(&2.method(:+))
=> [3, 5, 7, 9, 11]
[3] pry(main)> 

작동 방식은 다음과 같습니다.

[3] pry(main)> 2.method(:+)
=> #<Method: Fixnum#+>
[4] pry(main)> 2.method(:+).to_proc
=> #<Proc:0x000001030cb990 (lambda)>
[5] pry(main)> 2.method(:+).to_proc.call(1)
=> 3

2.method(:+)Method개체를 제공 합니다. 그런 다음 &, 2.method(:+)실제로 #to_procProc객체를 만드는 호출 메서드 입니다. 그런 다음 Ruby에서 & : 연산자를 무엇이라고 부르나요? .


영리한 사용법! 메서드 호출이 두 가지 방식으로 적용될 수 있다고 가정합니까 (예 : arr [element] .method (param) === param.method (arr [element])) 아니면 혼란 스럽습니까?
Kostas Rousis 2014 년

@rkon 나도 귀하의 질문을받지 못했습니다. 그러나 Pry위 의 출력 을 보면 어떻게 작동하는지 알 수 있습니다.
Arup Rakshit 2014 년

5
@rkon 두 가지 방식으로 작동하지 않습니다. 이것은 +교환 적이기 때문에이 특별한 경우에 작동합니다 .
sawa

여러 인수를 어떻게 제공 할 수 있습니까? 이 경우 : a.map {| x | x.method (1,2,3)}
Zack Xu

1
그게 내 요점입니다 @sawa :) +로 이해가되지만 다른 방법에는 적합하지 않습니다. 또는 각 숫자를 X로 나누고 싶을 때 가정 해 봅시다.
Kostas Rousis

11

링크 한 게시물이 확인되었으므로 a.map(&:class)는의 약자가 a.map {|x| x.class}아니라 a.map(&:class.to_proc).

이것은 연산자를 to_proc따르는 모든 것에 대해 호출 된다는 것을 의미합니다 &.

따라서 Proc대신 직접 줄 수 있습니다 .

a.map(&(Proc.new {|x| x+2}))

아마도 이것이 귀하의 질문의 목적을 무너 뜨리는 것을 알고 있지만 다른 방법은 볼 수 없습니다. 호출 할 메서드를 지정하는 것이 아니라 응답하는 무언가를 전달합니다 to_proc.


1
또한 procs를 지역 변수로 설정하고 매핑에 전달할 수 있다는 점을 기억하십시오. my_proc = Proc.new{|i| i + 1},[1,2,3,4].map(&my_proc) => [2,3,4,5]
rudolph9

10

짧은 대답 : 아니요.

@rkon의 대답에 따라 다음과 같이 할 수도 있습니다.

a = [1,3,5,7,9]
a.map &->(_) { _ + 2 } # => [3, 5, 7, 9, 11]

9
당신이 바로,하지만 난 생각하지 않습니다 &->(_){_ + 2}보다 짧다 {|x| x + 2}.
sawa

1
그것은 @rkon이 그의 대답에서 말하는 것이므로 반복하지 않았습니다.
Agis

2
@Agis 귀하의 답변이 짧지는 않지만 더 좋아 보입니다.
Jikku Jose

1
그것은 굉장한 해결책입니다.
BenMorganIO

5

받아 들여진 답변에서와 같이 코어 클래스를 직접 패치하는 대신 Facets gem 의 기능을 사용하는 것이 더 짧고 깔끔합니다 .

require 'facets'
a = [1,3,5,7,9]
a.map &:+.(2)

5

내 의견으로는 두 가지 인수에 대해서만 예쁜 열거 형에 대한 또 다른 기본 옵션이 있습니다. 클래스 Enumerable에는 with_object다른 것을 반환 하는 메서드 가 있습니다.Enumerable 있습니다.

그래서 당신은 & 각 항목과 개체를 인수로 사용하여 메서드에 연산자를 .

예:

a = [1,3,5,7,9]
a.to_enum.with_object(2).map(&:+) # => [3, 5, 7, 9, 11]

더 많은 인수를 원할 경우 절차를 반복해야하지만 내 의견으로는 추악합니다.

a = [1,3,5,7,9]
a.to_enum.with_object(2).map(&:+).to_enum.with_object(5).map(&:+) # => [8, 10, 12, 14, 16]

0

Symbol#with이미 게시 된 내용에 대해 잘 모르겠습니다. 상당히 단순화했으며 잘 작동합니다.

class Symbol
  def with(*args, &block)
    lambda { |object| object.public_send(self, *args, &block) }
  end
end

(또한 개인 메서드 호출을 방지하는 public_send대신 사용하며 이미 루비에서 사용하므로 혼란 스럽습니다)sendcaller

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.