Scala Functions ( Scala 의 또 다른 여행의 일부)를 읽었습니다 . 그 게시물에서 그는 이렇게 말했습니다.
방법과 기능은 동일하지 않습니다
그러나 그는 그것에 대해 아무 것도 설명하지 않았습니다. 그는 무엇을 말하려고 했습니까?
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
본문을 제외한 모든 것 .
값 선언과 정의 및 변수 선언과 정의가 있는 val
및 var
모두를 포함 선언, 유형과 값 -하는 수 있으며, 각각 기능 유형 및 익명 함수 또는 메소드의 값 . JVM에서 이러한 (메소드 값)은 Java가 "메소드"라고 부르는 것으로 구현됩니다.
함수 선언 A는 def
포함 선언 유형 및 신체 . 유형 부분은 메소드 유형이고 본문은 표현식 또는 블록 입니다. 이것은 Java가 "메소드"라고 부르는 JVM에서도 구현됩니다.
마지막으로 Anonymous Function 은 Function 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 ""
나는 이것이 거의 모든 것을 다루고 있다고 생각하지만, 남아있을 수있는 질문에 대한 답변으로 이것을 보완 해 드리겠습니다.
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
아래에서 지적한 것처럼).
방법과 함수의 실질적인 차이점 중 하나는 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
for (a <- List(1, 2, 3)) { return ... }
어떻습니까? 그것은 폐쇄로 설탕을 제거합니다.
return
함수로부터 값을 반환하고, 어떤 형태 escape
나 break
또는 continue
방법에서 복귀.
function 인수 목록과 함께 함수를 호출하여 결과를 생성 할 수 있습니다. 함수에는 매개 변수 목록, 본문 및 결과 유형이 있습니다. 클래스, 특성 또는 단일 객체의 멤버 인 함수를 메소드 라고 합니다 . 다른 함수 내에 정의 된 함수를 로컬 함수라고합니다. 결과 유형이 Unit 인 함수를 프로 시저라고합니다. 소스 코드의 익명 함수를 함수 리터럴이라고합니다. 런타임시 함수 리터럴은 함수 값이라는 객체로 인스턴스화됩니다.
당신이 목록을 가지고 있다고하자
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의 다른 예제와 같은 예제로 다른 차이점을 전달하는 방법을 설명하는 다음 자습서 를 확인하십시오.
여기 에 내 설명의 대부분을 취하는 멋진 기사가 있습니다 . 내 이해에 관한 기능과 방법의 짧은 비교. 그것이 도움이되기를 바랍니다.
기능 : 기본적으로 객체입니다. 보다 정확하게 함수는 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
스칼라 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를 참조하십시오.
실제로 스칼라 프로그래머는 함수와 메소드를 올바르게 사용하려면 다음 세 가지 규칙 만 알아야합니다.
def
및 함수 리터럴 =>
은 함수입니다. 이 책은 스칼라 프로그래밍, 제 4 판의 143 페이지 8 장에 정의되어 있습니다.someNumber.foreach(println)
스칼라에서 4 판의 프로그래밍을 수행 한 후에도 여전히 사람들이 두 가지 중요한 개념, 즉 기능과 기능 가치를 차별화하는 것이 여전히 중요합니다. 모든 판이 명확한 설명을 제공하지 않기 때문입니다. 언어 사양이 너무 복잡합니다. 위의 규칙이 간단하고 정확하다는 것을 알았습니다.