람다 사용시기, Proc.new 사용시기


336

Ruby 1.8에서는 proc / lambda와 다른 한편에 미묘한 차이가 Proc.new있습니다.

  • 그 차이점은 무엇입니까?
  • 어느 것을 선택할지 결정하는 방법에 대한 지침을 줄 수 있습니까?
  • Ruby 1.9에서는 proc과 lambda가 다릅니다. 거래는 무엇입니까?

3
Matz와 Flanagan의 Ruby Programming Language 책도 참조하십시오. proc은 람다가 메소드처럼 동작하는 메소드 콜 시맨틱 인 블록 항복 시맨틱 스처럼 동작합니다. 또한 반환, 휴식 등 모두 procs n lambdas에서 diff 동작
Gishu


당신은 proc과 lambda의 차이점만을 말하는 대답을 받아들였으며, 질문의 제목은 언제 그것들을 사용
Shri

답변:


378

로 만든 발동 사이의 또 다른 중요하지만 미묘한 차이 lambda로 작성 발동은 Proc.new그들이 처리하는 방법입니다 return문 :

  • - 작성된 lambdaproc에서 return명령문은 proc 자체에서만 리턴합니다.
  • A의 Proc.new-created 시저의 return문은 조금 더 놀라운 일이다 : 그것은 단지 PROC에서하지 제어를 반환 뿐만 아니라 시저를 둘러싸는 방법에서!

여기에 lambdaproc이 생성 return되었습니다. 그것은 당신이 아마 기대하는 방식으로 행동합니다 :

def whowouldwin

  mylambda = lambda {return "Freddy"}
  mylambda.call

  # mylambda gets called and returns "Freddy", and execution
  # continues on the next line

  return "Jason"

end


whowouldwin
#=> "Jason"

이제 여기에 Proc.new생성 된 proc의 return동일한 작업이 있습니다. 루비가 가장 자랑스런 서프라이즈 원칙을 위반 한 사례 중 하나를 보려고합니다.

def whowouldwin2

  myproc = Proc.new {return "Freddy"}
  myproc.call

  # myproc gets called and returns "Freddy", 
  # but also returns control from whowhouldwin2!
  # The line below *never* gets executed.

  return "Jason"

end


whowouldwin2         
#=> "Freddy"

이 놀라운 행동 (타이핑이 적음) 덕분에 나는 procs를 만들 때 lambdaover를 사용하는 것을 선호 Proc.new합니다.


12
그런 다음 proc방법도 있습니다. 단지 속기 Proc.new입니까?
panzi


4
@mattdipasquale 내 테스트에서 return 서술문 과 유사하게 proc동작 합니다. 그것은 루비 문서가 부정확하다는 것을 의미합니다. lambdaProc.new
Kelvin

31
@mattdipasquale 죄송합니다. 절반 밖에 안되었습니다. proc같은 역할을 lambda1.8에서,하지만 같은 역할 Proc.new1.9 인치 Peter Wagenet의 답변을 참조하십시오.
Kelvin

55
왜 "놀라운"행동입니까? A lambda는 익명의 방법입니다. 그것은 메소드이기 때문에 값을 반환하고 그것을 호출 한 메소드는 무시하고 다른 값을 반환하는 것을 포함하여 원하는 모든 것을 할 수 있습니다. A Proc는 코드 스 니펫에 붙여 넣는 것과 같습니다. 방법처럼 작동하지 않습니다. 따라서에서 반환이 발생 Proc하면 호출 한 메서드 코드의 일부일뿐입니다.
Arcolye

96

추가 설명을 제공하려면 다음을 수행하십시오.

Joey는의 복귀 행동 Proc.new이 놀랍다 고 말합니다 . 그러나 Proc.new가 블록처럼 동작한다고 생각할 때 이것이 블록이 동작하는 방식과 마찬가지로 놀라운 것은 아닙니다. 반면에 lambas는 메소드와 비슷하게 동작합니다.

이것은 실제로 procs가 arity (논쟁 수)에 관해서 융통성이있는 이유를 설명하지만 람다는 그렇지 않습니다. 블록은 모든 인수를 제공 할 필요는 없지만 메소드가 제공합니다 (기본값이 제공되지 않는 한). 람다 인수 기본값을 제공하는 것은 Ruby 1.8에서 옵션이 아니지만, 이제 Ruby 1.9에서 대체 람다 구문을 사용하여 지원됩니다 (webmat에서 알 수 있음).

concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1)   # => "12"

그리고 Michiel de Mare (OP)는 Ruby 1.9에서 Arity와 동일하게 동작하는 Procs와 lambda에 대해 올바르지 않습니다. 위와 같이 여전히 1.8에서 동작을 유지한다는 것을 확인했습니다.

break진술은 실제로 Procs 또는 람다에서별로 의미가 없습니다. Procs에서는 휴식이 이미 완료된 Proc.new에서 돌아옵니다. 람다가 본질적으로 방법이기 때문에 람다에서 벗어나는 것은 의미가 없으며, 최상위 수준의 방법에서 결코 벗어나지 않을 것입니다.

next, redoraiseProcs와 lambdas에서 동일하게 작동합니다. 반면 retry어느 쪽도 허용되지 않으며 예외가 발생합니다.

마지막으로이 proc방법은 일관성이없고 예상치 못한 동작이 있으므로 사용해서는 안됩니다. Ruby 1.8에서는 실제로 람다를 반환합니다! Ruby 1.9에서는이 문제가 해결되었으며 Proc을 반환합니다. Proc을 만들려면을 사용하십시오 Proc.new.

자세한 내용 은이 정보의 대부분을 제공하는 O'Reilly의 The Ruby Programming Language 를 적극 권장 합니다.


1
"" "단, Proc.new가 블록처럼 동작한다고 생각할 때 이것은 블록이 동작하는 방식과 똑같이 놀라운 일이 아닙니다." ""<-블록은 객체의 일부이지만 Proc.new는 객체를 생성합니다. lambda와 Proc.new는 모두 클래스가 Proc 인 객체를 만듭니다. 왜 diff입니까?
16:29에

1
루비 2.5로, break프로세서 수의 인상에서 LocalJumpError, 반면 break람다의 동작합니다에서 불과 같은 return( , return nil).
Masa Sakano

43

차이점 과 차이점을 보여주는 이 페이지 를 찾았습니다 . 페이지에 따르면 유일한 차이점은 람다는 허용되는 인수 수에 대해 엄격한 반면 누락 된 인수는로 변환한다는 것 입니다. 차이점을 설명하는 IRB 세션의 예는 다음과 같습니다.Proc.newlambdaProc.newnil

irb (main) : 001 : 0> l = 람다 {| x, y | x + y}
=> # <Proc : 0x00007fc605ec0748 @ (irb) : 1>
irb (main) : 002 : 0> p = Proc.new {| x, y | x + y}
=> # <Proc : 0x00007fc605ea8698 @ (irb) : 2>
irb (main) : 003 : 0> l.call "hello", "world"
=> "helloworld"
irb (main) : 004 : 0> p.call "hello", "world"
=> "helloworld"
irb (main) : 005 : 0> l. "hello"전화
ArgumentError : 잘못된 개수의 인수 (1/2)
    부터 (irb) : 1
    (irb) : 5 :`call '에서
    에서 (irb) : 5
    : 0에서
irb (main) : 006 : 0> p.call "hello"
TypeError : nil을 문자열로 변환 할 수 없습니다
    from (irb) : 2 : in`+ '에서
    에서 (irb) : 2
    (irb) : 6 :`call '에서
    에서 (irb) : 6
    : 0에서

오류 허용 동작을 구체적으로 원하지 않는 한이 페이지에서는 람다 사용을 권장합니다. 이 정서에 동의합니다. 람다를 사용하는 것은 조금 더 간결한 것처럼 보이며, 그와 같은 의미없는 차이로 인해 평균 상황에서 더 나은 선택 인 것 같습니다.

루비 1.9에 관해서는, 미안하지만, 아직 1.9를 보지 않았지만 그들이 그렇게 많이 바꿀 것이라고는 생각하지 않습니다 (그러나 내 말을하지 마십시오. 나는 아마도 틀렸다.)


2
procs도 람다와 다르게 반환됩니다.
Cam

"" "Proc.new는 누락 된 인수를 nil로 변환합니다" "" "Proc.new는 추가 인수도 무시합니다 (물론 람다는이 오류를 오류로 표시합니다).
약화

16

Proc는 나이가 많지만 리턴의 의미는 (최소한 언어를 배우고있을 때) 나에게 반 직관적입니다.

  1. proc을 사용한다면, 아마도 일종의 기능적 패러다임을 사용할 것입니다.
  2. Proc는 기본적으로 진행되며 기능이 거의없는 엔 클로징 범위 (이전 응답 참조)에서 벗어날 수 있습니다.

Lambda는 기능적으로 안전하고 추론하기 쉽습니다. 저는 항상 proc 대신 사용합니다.


11

미묘한 차이점에 대해서는 많이 말할 수 없습니다. 그러나 Ruby 1.9는 이제 람다 및 블록에 대한 선택적 매개 변수를 허용합니다.

1.9 이하의 스테이 비 람다에 대한 새로운 구문은 다음과 같습니다.

stabby = ->(msg='inside the stabby lambda') { puts msg }

루비 1.8에는 그런 문법이 없었습니다. 블록 / 람다를 선언하는 기존의 방법은 선택적 인수를 지원하지 않았습니다.

# under 1.8
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }
SyntaxError: compile error
(irb):1: syntax error, unexpected '=', expecting tCOLON2 or '[' or '.'
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }

그러나 Ruby 1.9는 이전 구문에서도 선택적 인수를 지원합니다.

l = lambda { |msg = 'inside the regular lambda'|  puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez

Leopard 또는 Linux 용 Ruby1.9를 빌드하려면 이 기사 (shameless self promotion)를 확인하십시오.


람다 내의 선택적 매개 변수가 많이 필요했습니다 .1.9에 추가 한 것이 기쁩니다. 블록도 선택적 매개 변수를 가질 수 있다고 가정합니다 (1.9에서)?
mpd

블록에서 기본 매개 변수를 보여주지 않고 람다 만
iconoclast

11

짧은 대답 : 중요한 것은 무엇입니까? return람다는 자체를 반환하고 proc은 자체를 반환하고 함수를 호출합니다.

덜 명확한 것은 각각을 사용하려는 이유입니다. 람다는 기능적 프로그래밍 의미에서해야 할 일입니다. 기본적으로 현재 범위가 자동으로 바인딩되는 익명의 방법입니다. 둘 중 람다는 아마도 사용해야 할 것입니다.

반면 Proc는 언어 자체를 구현하는 데 실제로 유용합니다. 예를 들어 "if"문 또는 "for"루프를 구현할 수 있습니다. proc에서 발견 된 모든 리턴은 단지 "if"문이 아니라 호출 한 메소드에서 리턴됩니다. 이것은 언어가 작동하는 방식, "if"문이 작동하는 방식입니다. 그래서 루비는 이것을 커버 아래에서 사용하고 강력 해 보였기 때문에 그냥 노출했습니다.

루프, if-else 구문 등과 같은 새로운 언어 구문을 생성하는 경우에만 필요합니다.


1
"lambda 자체가 반환되고 proc 자체가 반환됩니다. 그리고 그 기능을 호출했습니다"는 명백한 잘못이며 매우 일반적인 오해입니다. proc은 클로저이며이를 만든 메서드에서 반환됩니다. 페이지의 다른 곳에서 내 전체 답변을 참조하십시오.
ComDubh

10

그것을 보는 좋은 방법은 람다는 자신의 범위에서 (메소드 호출 인 것처럼) 실행되는 반면 Procs는 호출 메소드와 함께 인라인으로 실행되는 것으로 볼 수 있습니다. 적어도 하나를 사용할 것을 결정하는 좋은 방법입니다 각각의 경우에.


8

나는 queston의 세 번째 방법 인 "proc"에 대해 아무런 언급도하지 않았지만 더 이상 사용되지 않지만 1.8과 1.9에서 다르게 처리되었습니다.

다음은 세 가지 유사한 호출의 차이점을 쉽게 확인할 수있는 상당히 자세한 예입니다.

def meth1
  puts "method start"

  pr = lambda { return }
  pr.call

  puts "method end"  
end

def meth2
  puts "method start"

  pr = Proc.new { return }
  pr.call

  puts "method end"  
end

def meth3
  puts "method start"

  pr = proc { return }
  pr.call

  puts "method end"  
end

puts "Using lambda"
meth1
puts "--------"
puts "using Proc.new"
meth2
puts "--------"
puts "using proc"
meth3

1
Matz는 proc과 Proc.new가 다른 결과를 반환하는 것이 혼란스러워서 더 이상 사용하지 않을 계획이라고 말했습니다. 1.9에서 그들은 동일하게 작동합니다 (proc는 Proc.new의 별칭입니다). eigenclass.org/hiki/Changes+in+Ruby+1.9#l47
Dave Rapin

@banister : proc1.8에서 람다를 반환했습니다. 1.9에서 proc을 반환하도록 수정되었습니다. 그러나 이것은 중대한 변화입니다. 따라서 더 이상 사용하지 않는 것이 좋습니다
Gishu

곡괭이가 어딘가에 발췌 부분에서 proc이 효과적으로 폐기 될 것 같은 것이라고 생각합니다. 정확한 페이지 번호가 없습니다.
dertoni

7

Ruby의 Closures는 Ruby 와 함께 Ruby에서 블록, 람다 및 프로세스가 작동하는 방법에 대한 훌륭한 개요입니다.


"함수는 여러 블록을 받아 들일 수 없습니다. 클로저를 자유롭게 값으로 전달할 수 있다는 원칙을 위반합니다." 블록은 클로저가 아닙니다. 프로세서는 여러 함수를 수용 할 수 있습니다.
ComDubh

5

람다는 다른 언어와 마찬가지로 예상대로 작동합니다.

유선 Proc.new은 놀랍고 혼란 스럽다.

return의해 생성 된 proc 의 문장 Proc.new은 그 자체뿐만 아니라 그것을 둘러싸는 메소드에서도 제어를 반환 합니다 .

def some_method
  myproc = Proc.new {return "End."}
  myproc.call

  # Any code below will not get executed!
  # ...
end

Proc.new블록과 마찬가지로 코드를 둘러싸는 메서드에 삽입 한다고 주장 할 수 있습니다 . 그러나 Proc.new블록은 개체의 일부인 반면 개체를 만듭니다 .

그리고 lambda와와의 또 다른 차이점 Proc.new은 (잘못된) 인수를 처리하는 것입니다. 람다는 그것에 대해 불평하면서 Proc.new추가 인수 를 무시하거나 인수가 없음을 nil로 간주합니다.

irb(main):021:0> l = -> (x) { x.to_s }
=> #<Proc:0x8b63750@(irb):21 (lambda)>
irb(main):022:0> p = Proc.new { |x| x.to_s}
=> #<Proc:0x8b59494@(irb):22>
irb(main):025:0> l.call
ArgumentError: wrong number of arguments (0 for 1)
        from (irb):21:in `block in irb_binding'
        from (irb):25:in `call'
        from (irb):25
        from /usr/bin/irb:11:in `<main>'
irb(main):026:0> p.call
=> ""
irb(main):049:0> l.call 1, 2
ArgumentError: wrong number of arguments (2 for 1)
        from (irb):47:in `block in irb_binding'
        from (irb):49:in `call'
        from (irb):49
        from /usr/bin/irb:11:in `<main>'
irb(main):050:0> p.call 1, 2
=> "1"

procRuby 1.8에서 BTW 는 람다를 생성하는 반면 Ruby 1.9+에서는처럼 Proc.new혼동되어 실제로 혼동됩니다.


3

아코디언 가이의 답변을 자세히 설명하려면 :

Proc.new블록을 전달하여 proc out 을 만듭니다. lambda {...}블록을 전달하는 메서드 호출이 아니라 일종의 리터럴로 구문 분석 되었다고 생각합니다 . return메소드 호출에 첨부 된 블록 내부에서을 호출하면 블록이 아닌 메소드에서 리턴되며,이 Proc.new경우가 이에 해당하는 예입니다.

(이것은 1.8입니다. 어떻게 이것이 1.9로 해석되는지 모르겠습니다.)


3

나는 이것에 대해 조금 늦었지만 Proc.new주석에 언급되지 않은 것에 대해 하나의 위대한 것이 거의 알려지지 않은 것이 있습니다. 에 의해으로 문서 :

Proc::new첨부 된 블록이있는 메소드 내에서만 블록없이 호출 될 수 있으며,이 경우 해당 블록은Proc 객체 로 변환됩니다 .

즉, Proc.new항복 방법을 체인으로 묶을 수 있습니다.

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!

흥미롭게도,에서 &block인수 를 선언하는 것과 같은 일을 def하지만 def arg 목록에서 인수 를 선언 하지 않아도됩니다.
jrochkind

2

강조 그것의 가치가 그 return어휘 둘러싸는 방법에서 PROC 반환, 즉에서 proc 디렉토리가 생성 된 방법 , 하지 proc 디렉토리를 호출하는 방법. 이것은 procs의 클로저 속성의 결과입니다. 따라서 다음 코드는 아무것도 출력하지 않습니다.

def foo
  proc = Proc.new{return}
  foobar(proc)
  puts 'foo'
end

def foobar(proc)
  proc.call
  puts 'foobar'
end

foo

proc 디렉토리가에서 실행하지만 foobar, 그것은에서 생성 foo하고 그래서 return종료 foo뿐만 아니라, foobar. Charles Caldwell이 위에서 썼 듯이 GOTO 느낌이 있습니다. 제 생각에는 return어휘 문맥에서 실행되는 블록에서는 문제가 없지만 다른 문맥에서 실행되는 proc에서 사용될 때 훨씬 덜 직관적입니다.


1

동작의 차이점 return은 IMHO 와 2 사이의 가장 중요한 차이점입니다. Proc.new :-)보다 타이핑이 적기 때문에 lambda를 선호합니다.


2
업데이트하려면 :을 사용하여 procs를 만들 수 있습니다 proc {}. 이것이 언제 적용되는지 확실하지 않지만 Proc.new를 입력하는 것보다 (약간) 쉽습니다.
aceofbassgreg
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.