스칼라의 방법과 기능의 차이


254

Scala Functions ( Scala또 다른 여행의 일부)를 읽었습니다 . 그 게시물에서 그는 이렇게 말했습니다.

방법과 기능은 동일하지 않습니다

그러나 그는 그것에 대해 아무 것도 설명하지 않았습니다. 그는 무엇을 말하려고 했습니까?



3
나는 당신이 무언가를 얻을 수 있다고 생각합니다 . 메소드와 함수의 차이점
jinglining

좋은 답변과 함께 후속 질문 : 스칼라의 함수 대 방법
Josiah Yoder

답변:


238

Jim은 자신의 블로그 게시물 에서이 내용을 거의 다루었 지만 참고를 위해 여기에 브리핑을 게시하고 있습니다.

먼저, 스칼라 사양이 우리에게 말하는 것을 보자. 3 장 (유형)에서는 함수 유형 (3.2.9) 및 메소드 유형 (3.3.1)에 대해 설명합니다. 4 장 (기본 선언)에서는 가치 선언 및 정의 (4.1), 변수 선언 및 정의 (4.2) 및 함수 선언 및 정의 (4.6)에 대해 설명합니다. 6 장 (표현)에서는 익명 함수 (6.23) 및 방법 값 (6.7)에 대해 설명합니다. 흥미롭게도 함수 값은 3.2.9에서 한 번만 말하지만 다른 곳에서는 언급되지 않습니다.

함수 타입 형태의 (대략) 타입이다 (T1, ..., TN) => U 형질 속기이고, FunctionN표준 라이브러리이다. 익명 함수메소드 값 에는 함수 유형이 있으며 함수 유형은 값, 변수 및 함수 선언 및 정의의 일부로 사용할 수 있습니다. 실제로 메소드 유형의 일부일 수 있습니다.

방법 유형 A는 비 값 유형 . 즉 , 메소드 유형 에는 값이없고 객체도없고 인스턴스도 없습니다. 위에서 언급했듯이 Method Value 에는 실제로 Function Type이 있습니다. 메소드 유형은 def선언입니다- def본문을 제외한 모든 것 .

값 선언과 정의변수 선언과 정의가 있는 valvar모두를 포함 선언, 유형과 값 -하는 수 있으며, 각각 기능 유형익명 함수 또는 메소드의 값 . JVM에서 이러한 (메소드 값)은 Java가 "메소드"라고 부르는 것으로 구현됩니다.

함수 선언 A는 def포함 선언 유형신체 . 유형 부분은 메소드 유형이고 본문은 표현식 또는 블록 입니다. 이것은 Java가 "메소드"라고 부르는 JVM에서도 구현됩니다.

마지막으로 Anonymous FunctionFunction Type 의 인스턴스 (즉, trait의 인스턴스 FunctionN)이며 Method Value 는 동일합니다! 구별 방법은 메소드 값이 밑줄 ( m _"함수 선언"( def)에 해당하는 메소드 값임) 을 접두어 붙이 m거나 메소드에서 자동으로 캐스트되는 eta-expansion 프로세스에 의해 메소드에서 작성된다는 것입니다. 작동합니다.

그것이 스펙이 말한 것이므로, 이것을 먼저 설명하겠습니다 . 우리는 그 용어를 사용하지 않습니다! 프로그램의 일부인 소위 "함수 선언" (제 4 장-기본 선언)과 "익명 함수"( 식), "함수 유형" 사이에 너무 많은 혼동 이 발생합니다. 잘 유형-특성.

숙련 된 스칼라 프로그래머가 사용하는 아래의 용어는 사양의 용어에서 하나의 변경 사항을 만듭니다. 함수 선언 대신에 method 라고 합니다 . 또는 메소드 선언. 또한 가치 선언변수 선언 도 실용적인 목적을위한 방법입니다.

따라서 위의 용어 변경을 감안할 때 차이점에 대한 실제 설명이 있습니다.

기능 의 하나를 포함하는 목적 FunctionX과 같은 특성, Function0, Function1, Function2, 등이 포함 할 수 PartialFunction실제로 연장뿐만 아니라이 Function1.

다음 특성 중 하나에 대한 유형 서명을 보자.

trait Function2[-T1, -T2, +R] extends AnyRef

이 특성에는 하나의 추상적 인 방법이 있습니다 (몇 가지 구체적인 방법도 있습니다).

def apply(v1: T1, v2: T2): R

그리고 그것은 그것에 대해 알아야 할 모든 것을 말해줍니다. 함수 갖는 apply수신 방법 N의 유형 파라미터 T1 , T2 , ..., TN 타입의 복귀 무언가 R. 수신되는 매개 변수에 대해 편차가 있고 결과에 대해 공변량입니다.

이 분산 Function1[Seq[T], String]은 a가의 하위 유형 임을 의미합니다 Function1[List[T], AnyRef]. 부속 유형이된다는 것은 부속 유형 대신 사용될 수 있음을 의미 합니다. 하나는 쉽게 내가 전화 할거야 경우 볼 수 f(List(1, 2, 3))와 기대 AnyRef중 하나 이상이 작동 것 두 가지 유형, 다시.

자, 메소드와 함수 의 유사점 은 무엇 입니까? 글쎄, f함수이고 m스코프에 로컬 인 메소드라면, 둘 다 다음과 같이 호출 될 수 있습니다.

val o1 = f(List(1, 2, 3))
val o2 = m(List(1, 2, 3))

첫 번째 통화는 단지 구문 설탕이기 때문에 이러한 호출은 실제로 다릅니다. 스칼라는 그것을 확장합니다 :

val o1 = f.apply(List(1, 2, 3))

물론 object에 대한 메소드 호출입니다 f. 함수에는 다른 구문 설탕도 있습니다 : 함수 리터럴 (실제로 두 개)과 (T1, T2) => R타입 시그니처. 예를 들면 다음과 같습니다.

val f = (l: List[Int]) => l mkString ""
val g: (AnyVal) => String = {
  case i: Int => "Int"
  case d: Double => "Double"
  case o => "Other"
}

메소드와 함수의 또 다른 유사점은 전자가 후자로 쉽게 변환 될 수 있다는 것입니다.

val f = m _

Scala는 유형이 (Scala 2.7)에 있다고 가정 하여 확장 합니다 .m(List[Int])AnyRef

val f = new AnyRef with Function1[List[Int], AnyRef] {
  def apply(x$1: List[Int]) = this.m(x$1)
}

Scala 2.8에서는 실제로 AbstractFunction1클래스를 사용하여 클래스 크기를 줄입니다.

함수에서 메소드로 다른 방법으로 변환 할 수는 없습니다.

그러나 메소드는 하나의 큰 장점이 있습니다 (둘 중 두 가지가 약간 빠를 수 있음). 유형 매개 변수를 수신 할 수 있습니다 . 예를 들어, f위에서는 List수신 유형을 반드시 지정할 수 있지만 ( List[Int]예제에서) m이를 매개 변수화 할 수 있습니다.

def m[T](l: List[T]): String = l mkString ""

나는 이것이 거의 모든 것을 다루고 있다고 생각하지만, 남아있을 수있는 질문에 대한 답변으로 이것을 보완 해 드리겠습니다.


26
이 설명은 매우 분명합니다. 잘 했어. 불행히도 Odersky / Venners / Spoon 책과 Scala 사양은 "function"과 "method"라는 단어를 어느 정도 상호 교환 적으로 사용합니다. (그들은 "메소드"가 더 분명한 "함수"라고 말할 것 같지만 때로는 다른 방법으로도 발생합니다. 예를 들어, 메소드를 함수로 변환하는 것을 다루는 스펙의 섹션 6.7은 "메소드 값"입니다. .)이 단어를 느슨하게 사용하면 사람들이 언어를 배우려고 할 때 많은 혼란을 초래했다고 생각합니다.
Seth Tisue

4
@Seth 알고 있습니다. 저는 PinS가 Scala를 가르쳐 준 책이었습니다. 나는 어려운 길을 잘 배웠다. 즉, paulp은 나를 똑바로 세웠다.
Daniel C. Sobral

4
좋은 설명! 당신의 확장을 인용하는 경우 : 내가 추가 한 것은이 val f = m같은 컴파일러에 의해을 val f = new AnyRef with Function1[List[Int], AnyRef] { def apply(x$1: List[Int]) = this.m(x$1) }당신이 것을 지적한다 this내부 apply방법은 참조하지 않는 AnyRef개체 만이 그 방법으로 개체를 val f = m _평가합니다 ( 외부 this , 그래서 말을 )이므로 this클로저에 의해 캡처 된 값 중 하나입니다 (예 : return아래에서 지적한 것처럼).
Holger Peine

1
@ DanielC.Sobral, 당신이 언급 한 PinS 책은 무엇입니까? 나는 또한 스칼라 학습에 관심이 있고 그 이름을 가진 책을 찾지 못했습니다
tldr

5
스칼라에서 @tldr 프로그래밍 , Odersky et al. 그것은 일반적인 약어입니다 (그들은 어떤 이유로 PiS를 좋아하지 않는다고 말했습니다! :)
Daniel C. Sobral

67

방법과 함수의 실질적인 차이점 중 하나는 return의미하는 것입니다. return메소드에서만 리턴됩니다. 예를 들면 다음과 같습니다.

scala> val f = () => { return "test" }
<console>:4: error: return outside method definition
       val f = () => { return "test" }
                       ^

메소드에 정의 된 함수에서 리턴하면 로컬이 아닌 리턴이 수행됩니다.

scala> def f: String = {                 
     |    val g = () => { return "test" }
     | g()                               
     | "not this"
     | }
f: String

scala> f
res4: String = test

로컬 메소드에서 리턴하는 것은 해당 메소드에서만 리턴합니다.

scala> def f2: String = {         
     | def g(): String = { return "test" }
     | g()
     | "is this"
     | }
f2: String

scala> f2
res5: String = is this

9
폐쇄로 인해 수익이 포착되기 때문입니다.
Daniel C. Sobral

4
함수에서 로컬이 아닌 범위로 '반환'하려는 단일 시간을 생각할 수 없습니다. 사실, 함수가 스택을 더 멀리 돌아가고 싶어한다고 결정하면 심각한 보안 문제로 간주됩니다. longjmp와 같은 느낌, 실수로 잘못하기 쉬운 방법. 스 칼락이 함수에서 돌아올 수 없다는 것을 알았습니다. 이것이이 가증이 언어에서 강탈 당했음을 의미합니까?
root

2
@root-내부에서 돌아 오는 것은 for (a <- List(1, 2, 3)) { return ... }어떻습니까? 그것은 폐쇄로 설탕을 제거합니다.
Ben Lings

흠 ... 글쎄, 그건 합리적인 사용 사례입니다. 여전히 디버그하기 어려운 끔찍한 문제로 이어질 가능성이 있지만 더 합리적인 상황에 처하게됩니다.
root

1
솔직히 다른 구문을 사용합니다. 이 return함수로부터 값을 반환하고, 어떤 형태 escapebreak또는 continue방법에서 복귀.
Ryan The Leach

38

function 인수 목록과 함께 함수를 호출하여 결과를 생성 할 수 있습니다. 함수에는 매개 변수 목록, 본문 및 결과 유형이 있습니다. 클래스, 특성 또는 단일 객체의 멤버 인 함수를 메소드 라고 합니다 . 다른 함수 내에 정의 된 함수를 로컬 함수라고합니다. 결과 유형이 Unit 인 함수를 프로 시저라고합니다. 소스 코드의 익명 함수를 함수 리터럴이라고합니다. 런타임시 함수 리터럴은 함수 값이라는 객체로 인스턴스화됩니다.

스칼라 2 판에서의 프로그래밍. 마틴 오더 스키-Lex Spoon-Bill Venners


1
함수는 클래스에 def 또는 val / var로 속할 수 있습니다. def 만 메소드입니다.
Josiah Yoder

29

당신이 목록을 가지고 있다고하자

scala> val x =List.range(10,20)
x: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19)

방법 정의

scala> def m1(i:Int)=i+2
m1: (i: Int)Int

함수 정의

scala> (i:Int)=>i+2
res0: Int => Int = <function1>

scala> x.map((x)=>x+2)
res2: List[Int] = List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21)

인수를 받아들이는 방법

scala> m1(2)
res3: Int = 4

val을 사용하여 함수 정의

scala> val p =(i:Int)=>i+2
p: Int => Int = <function1>

기능에 대한 인수는 선택 사항입니다

 scala> p(2)
    res4: Int = 4

scala> p
res5: Int => Int = <function1>

방법론은 필수적이다

scala> m1
<console>:9: error: missing arguments for method m1;
follow this method with `_' if you want to treat it as a partially applied function

Method Vs 함수, function을 변수로 사용, 함수를 반환하는 함수 만들기를 사용하는 diff의 다른 예제와 같은 예제로 다른 차이점을 전달하는 방법을 설명하는 다음 자습서 를 확인하십시오.


13

함수는 매개 변수 기본값을 지원하지 않습니다. 방법이 있습니다. 메소드에서 함수로 변환하면 매개 변수 기본값이 유실됩니다. (스칼라 2.8.1)


5
이것에 대한 이유가 있습니까?
corazza

7

여기 에 내 설명의 대부분을 취하는 멋진 기사가 있습니다 . 내 이해에 관한 기능과 방법의 짧은 비교. 그것이 도움이되기를 바랍니다.

기능 : 기본적으로 객체입니다. 보다 정확하게 함수는 apply 메소드가있는 객체입니다. 따라서 오버 헤드로 인해 메소드보다 약간 느립니다. 호출되는 객체와 독립적이라는 점에서 정적 메서드와 비슷합니다. 함수의 간단한 예는 다음과 같습니다.

val f1 = (x: Int) => x + x
f1(2)  // 4

위의 줄은 object1 = object2와 같이 한 객체를 다른 객체에 할당하는 것 외에는 아무것도 아닙니다. 실제로이 예제에서 object2는 익명 함수이며 왼쪽은 그 때문에 개체의 유형을 가져옵니다. 따라서 이제 f1은 객체 (함수)입니다. 익명 함수는 실제로 Function1 [Int, Int]의 인스턴스이며, 이는 Int 유형의 매개 변수 1 개와 Int 유형의 반환 값을 가진 함수를 의미합니다. 인수없이 f1을 호출하면 익명 함수 (Int => Int =)의 서명이 제공됩니다.

메소드 : 객체가 아니라 클래스의 인스턴스, 즉 객체에 할당됩니다. Java의 메소드 또는 c ++의 멤버 함수와 동일합니다 ( Raffi Khatchadourian 가이 질문 에 대한 주석에서 지적했듯이 ). 간단한 메소드 예제는 다음과 같습니다.

def m1(x: Int) = x + x
m1(2)  // 4

위의 줄은 단순한 값 할당이 아니라 메소드의 정의입니다. 두 번째 행과 같이 값 2로이 메소드를 호출하면 x가 2로 대체되고 결과가 계산되고 4가 출력됩니다. 여기서는 메소드이므로 입력 값이 필요하기 때문에 단순히 m1을 쓰면 오류가 발생합니다. _를 사용하면 다음과 같은 함수에 메소드를 지정할 수 있습니다.

val f2 = m1 _  // Int => Int = <function1>

"함수에 메소드를 할당"한다는 것은 무엇을 의미합니까? 방금 메서드와 동일한 방식으로 동작하는 개체가 있다는 의미입니까?
K. M

@KM : val f2 = m1 _는 val f2 = new와 같습니다. Function1 [Int, Int] {def m1 (x : Int) = x + x};
사스케

3

차이점을 설명하는 Rob Norris 의 훌륭한 게시물 이 있습니다. 여기는 TL입니다.

스칼라의 메소드는 값이 아니라 함수입니다. η- 확장을 통해 메소드에 위임하는 함수를 구성 할 수 있습니다 (마지막 밑줄로 트리거 됨).

다음과 같은 정의로 :

방법 으로 정의 뭔가 데프값은 당신이에 할당 할 수있는 뭔가가

간단히 말해서 ( 블로그에서 추출 ) :

메소드를 정의 할 때 메소드를에 할당 할 수 없음을 알 수 있습니다 val.

scala> def add1(n: Int): Int = n + 1
add1: (n: Int)Int

scala> val f = add1
<console>:8: error: missing arguments for method add1;
follow this method with `_' if you want to treat it as a partially applied function
       val f = add1

또한 주 유형add1정상적인 보이지 않는다; 유형의 변수를 선언 할 수 없습니다 (n: Int)Int. 방법은 값이 아닙니다.

그러나 η 확장 후위 연산자 (η는 "eta"로 발음)를 추가하여 메서드를 함수 값으로 바꿀 수 있습니다. 의 유형에 유의하십시오 f.

scala> val f = add1 _
f: Int => Int = <function1>

scala> f(3)
res0: Int = 4

그 결과는 _다음과 같은 기능을 수행하는 Function1것입니다. 메소드에 위임 하는 인스턴스를 생성합니다 .

scala> val g = new Function1[Int, Int] { def apply(n: Int): Int = add1(n) }
g: Int => Int = <function1>

scala> g(3)
res18: Int = 4

1

스칼라 2.13에서는 함수와 달리 메소드가 걸릴 수 있습니다

  • 유형 매개 변수 (다형성 메소드)
  • 암시 적 매개 변수
  • 종속 유형

그러나 이러한 제한은 다형성 함수 유형 # 4672에 의해 dotty (Scala 3)에서 해제됩니다. 예를 들어, dotty 버전 0.23.0-RC1다음 구문을 사용합니다.

유형 매개 변수

def fmet[T](x: List[T]) = x.map(e => (e, e))
val ffun = [T] => (x: List[T]) => x.map(e => (e, e))

암시 적 매개 변수 ( 컨텍스트 매개 변수)

def gmet[T](implicit num: Numeric[T]): T = num.zero
val gfun: [T] => Numeric[T] ?=> T = [T] => (using num: Numeric[T]) => num.zero

종속 유형

class A { class B }
def hmet(a: A): a.B = new a.B
val hfun: (a: A) => a.B = hmet

더 많은 예제는 tests / run / polymorphic-functions.scala를 참조하십시오.


0

실제로 스칼라 프로그래머는 함수와 메소드를 올바르게 사용하려면 다음 세 가지 규칙 만 알아야합니다.

  • 로 정의 된 메소드 def및 함수 리터럴 =>은 함수입니다. 이 책은 스칼라 프로그래밍, 제 4 판의 143 페이지 8 장에 정의되어 있습니다.
  • 함수 값은 임의의 값으로 전달 될 수있는 객체입니다. 함수 리터럴과 부분적으로 적용된 함수는 함수 값입니다.
  • 코드의 한 지점에서 함수 값이 필요한 경우 부분적으로 적용된 함수의 밑줄을 생략 할 수 있습니다. 예를 들면 다음과 같습니다.someNumber.foreach(println)

스칼라에서 4 판의 프로그래밍을 수행 한 후에도 여전히 사람들이 두 가지 중요한 개념, 즉 기능과 기능 가치를 차별화하는 것이 여전히 중요합니다. 모든 판이 명확한 설명을 제공하지 않기 때문입니다. 언어 사양이 너무 복잡합니다. 위의 규칙이 간단하고 정확하다는 것을 알았습니다.

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