[function] 스칼라의 방법과 기능의 차이

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

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

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



답변

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 ""

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


답변

방법과 함수의 실질적인 차이점 중 하나는 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


답변

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

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


답변

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

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의 다른 예제와 같은 예제로 다른 차이점을 전달하는 방법을 설명하는 다음 자습서 를 확인하십시오.


답변

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


답변

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

기능 : 기본적으로 객체입니다. 보다 정확하게 함수는 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>


답변

차이점을 설명하는 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