[scala] 중괄호와 괄호 사이의 스칼라의 공식적인 차이점은 무엇이며 언제 사용해야합니까?

괄호 ()와 중괄호 로 함수에 인수를 전달하는 것의 공식적인 차이점은 무엇입니까 {}?

ScalaProgramming에서 얻은 느낌 은 Scala가 매우 유연하고 내가 가장 좋아하는 것을 사용해야한다는 것입니다.하지만 어떤 경우는 컴파일하는 반면 다른 경우는 그렇지 않습니다.

예를 들어 (단지 예제로 의미합니다.이 특별한 예만이 아니라 일반적인 경우를 논의하는 모든 응답에 감사드립니다) :

val tupleList = List[(String, String)]()
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )

=> 오류 : 간단한 표현의 잘못된 시작

val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }

=> 좋아요.



답변

나는 이것에 대해 한 번 쓰려고했지만 규칙이 다소 확산되어 결국 포기했습니다. 기본적으로, 당신은 그것을 끊어야합니다.

매개 변수를 메서드 호출에 전달할 때 중괄호와 괄호를 서로 바꿔 사용할 수있는 위치에 집중하는 것이 가장 좋습니다. 메소드에 단일 매개 변수가 필요한 경우에만 중괄호로 괄호를 대체 할 수 있습니다 . 예를 들면 다음과 같습니다.

List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter

List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter

그러나 이러한 규칙을 더 잘 이해하려면 알아야 할 것이 더 많습니다.

Parens를 통한 컴파일 검사 향상

Spray의 저자는 라운드 검사를 권장합니다. 컴파일 검사가 증가하기 때문입니다. 이것은 스프레이와 같은 DSL에 특히 중요합니다. parens를 사용하면 컴파일러에게 단 한 줄만 주어져야한다는 것을 알리는 것입니다. 따라서 실수로 둘 이상을 제공하면 불만이 표시됩니다. 이제 중괄호의 경우에는 해당되지 않습니다. 예를 들어 어딘가에서 연산자를 잊어 버린 경우 코드가 컴파일되고 예기치 않은 결과가 발생하고 찾기 어려운 버그가 발생할 수 있습니다. 아래는 (표현이 순수하고 적어도 경고를 줄 것이기 때문에) 고안되었지만, 요점을 지적합니다.

method {
  1 +
  2
  3
}

method(
  1 +
  2
  3
)

첫 번째 컴파일, 두 번째는 제공합니다 error: ')' expected but integer literal found. 저자는 글을 쓰고 싶었다 1 + 2 + 3.

기본 인수를 가진 다중 매개 변수 메소드와 유사하다고 주장 할 수 있습니다. Parens를 사용할 때 매개 변수를 구분하기 위해 실수로 쉼표를 잊어 버리는 것은 불가능합니다.

다변

자세한 정보에 대해 간과되는 중요한 메모입니다. 중괄호를 사용하면 필연적으로 자세한 코드가 생깁니다. Scala 스타일 가이드 에는 중괄호를 닫는 것이 자신의 줄에 있어야한다고 명확하게 명시되어 있기 때문입니다.

… 닫는 중괄호는 함수의 마지막 행 바로 다음에 자체 행에 있습니다.

IntelliJ와 같은 많은 자동 재 포맷 프로그램이 자동으로이 재 포맷을 수행합니다. 따라서 가능하면 둥근 파렌을 사용하십시오.

접두사 표기법

접두사 표기법을 사용할 때 List(1,2,3) indexOf (2)매개 변수가 하나만 있으면 괄호를 생략하고로 쓸 수 있습니다 List(1, 2, 3) indexOf 2. 이것은 점 표기법의 경우가 아닙니다.

x + 2또는 과 같은 멀티 토큰 표현식 인 단일 매개 변수가있는 a => a % 2 == 0경우 괄호를 사용하여 표현식의 경계를 표시해야합니다.

튜플

때때로 괄호를 생략 할 수 있기 때문에 때때로 튜플에서 ((1, 2))와 같이 추가 괄호가 필요 하고 때로는에서와 같이 외부 괄호를 생략 할 수 있습니다 (1, 2). 혼동 될 수 있습니다.

함수 / 부분 함수 리터럴 case

스칼라는 함수와 부분 함수 리터럴에 대한 구문을 가지고 있습니다. 다음과 같이 보입니다 :

{
    case pattern if guard => statements
    case pattern => statements
}

case명령문 을 사용할 수있는 유일한 다른 위치 는 matchand catch키워드입니다.

object match {
    case pattern if guard => statements
    case pattern => statements
}
try {
    block
} catch {
    case pattern if guard => statements
    case pattern => statements
} finally {
    block
}

case다른 상황 에서는 문장을 사용할 수 없습니다 . 당신이 사용하고자한다면, case당신은 필요 중괄호. 함수와 부분 함수 리터럴을 구별하는 것이 궁금한 경우 대답은 다음과 같습니다. 문맥. 스칼라는 함수를 기대하면 얻을 수있는 함수입니다. 부분 함수가 필요한 경우 부분 함수를 얻습니다. 둘 다 예상되는 경우 모호성에 대한 오류가 발생합니다.

표현과 블록

괄호를 사용하여 하위 표현식을 만들 수 있습니다. 중괄호는 코드 블록을 만드는 데 사용할 수 있습니다 (이것은 함수 리터럴 이 아니므 로 코드 처럼 사용하도록주의하십시오). 코드 블록은 여러 명령문으로 구성되며 각 명령문은 가져 오기 명령문, 선언 또는 표현식 일 수 있습니다. 다음과 같이 진행됩니다.

{
    import stuff._
    statement ; // ; optional at the end of the line
    statement ; statement // not optional here
    var x = 0 // declaration
    while (x < 10) { x += 1 } // stuff
    (x % 5) + 1 // expression
}

( expression )

따라서 선언, 여러 명령문 import또는 이와 유사한 것이 필요한 경우 중괄호가 필요합니다. 그리고 표현식은 문장이므로 중괄호 안에 괄호가 나타날 수 있습니다. 그러나 흥미로운 점은 코드 블록 표현식이므로 표현식 내부 어디에서나 사용할 수 있다는 것입니다 .

( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1

따라서 표현식은 명령문이고 코드 블록은 표현식이므로 아래의 모든 것이 유효합니다.

1       // literal
(1)     // expression
{1}     // block of code
({1})   // expression with a block of code
{(1)}   // block of code with an expression
({(1)}) // you get the drift...

교환 할 수없는 곳

기본적으로, 당신은 대체 할 수 {}와 함께 ()또는 그 다른 곳도 마찬가지입니다. 예를 들면 다음과 같습니다.

while (x < 10) { x += 1 }

이것은 메소드 호출이 아니므로 다른 방법으로는 쓸 수 없습니다. 글쎄, 당신은 중괄호를 넣을 수 있습니다 내부 의 괄호 condition사용 괄호뿐만 아니라, 내부 코드 블록의 중괄호 :

while ({x < 10}) { (x += 1) }

그래서 이것이 도움이되기를 바랍니다.


답변

여기에 진행되는 몇 가지 규칙과 추론이 있습니다. 우선, Scala는 매개 변수가 함수일 때 중괄호를 유추합니다. 예를 list.map(_ * 2)들어 중괄호가 유추되면 이는 짧은 형식입니다 list.map({_ * 2}). 둘째, Scala를 사용하면 마지막 매개 변수 목록에서 괄호를 건너 뛸 수 있습니다. 해당 매개 변수 목록에 매개 변수가 하나 있고 함수 인 경우에는 다음 list.foldLeft(0)(_ + _)과 같이 작성할 수 있습니다 list.foldLeft(0) { _ + _ }(또는 list.foldLeft(0)({_ + _})명시 적으로 명시하려는 경우).

그러나 추가하면 case다른 사람이 언급 한대로 얻을, 일부 기능을 대신하는 기능, 및 스칼라하지 않습니다 부분 기능 중괄호, 그래서 추론 list.map(case x => x * 2)하지 않습니다 작동하지만 모두 list.map({case x => 2 * 2})list.map { case x => x * 2 }것입니다.


답변

중괄호 및 괄호 사용을 표준화하기 위해 커뮤니티의 노력이 있습니다. 스칼라 스타일 가이드 (21 페이지)를 참조하십시오 : http://www.codecommit.com/scala-style-guide.pdf

고차 메소드 호출에 권장되는 구문은 항상 중괄호를 사용하고 점을 생략하는 것입니다.

val filtered = tupleList takeWhile { case (s1, s2) => s1 == s2 }

“정상적인”메쏘드 호출의 경우 점과 괄호를 사용해야합니다.

val result = myInstance.foo(5, "Hello")


답변

스칼라에는 중괄호에 대해 구체적이거나 복잡한 것이 없다고 생각합니다. 스칼라에서 복잡해 보이는 사용법을 익히려면 몇 가지 간단한 사항을 명심하십시오.

  1. 중괄호는 코드 블록을 형성하며 마지막 코드 행으로 평가됩니다 (거의 모든 언어에서 수행)
  2. 원하는 경우 코드 블록으로 생성 할 수있는 기능 (규칙 1 준수)
  3. case 절을 ​​제외하고 한 줄의 코드에 중괄호를 생략 할 수 있습니다 (Scala choice)
  4. 코드 블록을 매개 변수로 사용하여 함수 호출에서 괄호를 생략 할 수 있습니다 (Scala choice)

위의 세 가지 규칙에 따라 몇 가지 예를 설명하겠습니다.

val tupleList = List[(String, String)]()
// doesn't compile, violates case clause requirement
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )
// block of code as a partial function and parentheses omission,
// i.e. tupleList.takeWhile({ case (s1, s2) => s1 == s2 })
val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }

// curly braces omission, i.e. List(1, 2, 3).reduceLeft({_+_})
List(1, 2, 3).reduceLeft(_+_)
// parentheses omission, i.e. List(1, 2, 3).reduceLeft({_+_})
List(1, 2, 3).reduceLeft{_+_}
// not both though it compiles, because meaning totally changes due to precedence
List(1, 2, 3).reduceLeft _+_ // res1: String => String = <function1>

// curly braces omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _})
List(1, 2, 3).foldLeft(0)(_ + _)
// parentheses omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _})
List(1, 2, 3).foldLeft(0){_ + _}
// block of code and parentheses omission
List(1, 2, 3).foldLeft {0} {_ + _}
// not both though it compiles, because meaning totally changes due to precedence
List(1, 2, 3).foldLeft(0) _ + _
// error: ';' expected but integer literal found.
List(1, 2, 3).foldLeft 0 (_ + _)

def foo(f: Int => Unit) = { println("Entering foo"); f(4) }
// block of code that just evaluates to a value of a function, and parentheses omission
// i.e. foo({ println("Hey"); x => println(x) })
foo { println("Hey"); x => println(x) }

// parentheses omission, i.e. f({x})
def f(x: Int): Int = f {x}
// error: missing arguments for method f
def f(x: Int): Int = f x


답변

함수 호출에서의 사용법과 다양한 일이 발생하는 이유를 설명 할 가치가 있다고 생각합니다. 누군가 이미 말했듯이 중괄호는 코드 블록을 정의합니다.이 코드는 표현식이기도하므로 표현식이 필요한 곳에 놓을 수 있으며 평가됩니다. 평가 될 때 그 문장이 실행되고 last의 진술 값은 전체 블록 평가의 결과입니다 (루비에서와 비슷 함).

우리는 다음과 같은 일을 할 수 있습니다.

2 + { 3 }             // res: Int = 5
val x = { 4 }         // res: x: Int = 4
List({1},{2},{3})     // res: List[Int] = List(1,2,3)

마지막 예는 세 개의 매개 변수를 가진 함수 호출이며 각 매개 변수가 먼저 평가됩니다.

이제 함수 호출과의 작동 방식을 확인하기 위해 다른 함수를 매개 변수로 사용하는 간단한 함수를 정의 해 보겠습니다.

def foo(f: Int => Unit) = { println("Entering foo"); f(4) }

이를 호출하려면 Int 유형의 매개 변수를 하나 사용하는 함수를 전달해야하므로 함수 리터럴을 사용하여 foo에 전달할 수 있습니다.

foo( x => println(x) )

앞에서 말했듯이 식 대신 코드 블록을 사용할 수 있으므로 사용하겠습니다.

foo({ x => println(x) })

여기서 발생하는 것은 {} 내부의 코드가 평가되고 함수 값이 블록 평가의 값으로 리턴 된 후이 값은 foo에 전달됩니다. 이것은 의미 상 이전 호출과 동일합니다.

그러나 우리는 더 많은 것을 추가 할 수 있습니다 :

foo({ println("Hey"); x => println(x) })

이제 코드 블록에는 두 개의 문이 포함되어 있으며, foo가 실행되기 전에 평가되므로 먼저 “Hey”가 인쇄 된 다음 함수가 foo에 전달되고 “foo 입력”이 인쇄되고 마지막으로 “4”가 인쇄됩니다 .

이것은 조금 추한 것처럼 보이며 Scala는이 경우 괄호를 건너 뛸 수 있으므로 다음과 같이 쓸 수 있습니다.

foo { println("Hey"); x => println(x) }

또는

foo { x => println(x) }

그것은 훨씬 더 멋지게 보이고 이전과 같습니다. 여기서도 여전히 코드 블록이 먼저 평가되고 평가 결과 (x => println (x))가 foo에 인수로 전달됩니다.


답변

을 사용 case하고 있으므로 부분 함수를 정의하고 있으며 부분 함수에는 중괄호가 필요합니다.


답변

Parens를 통한 컴파일 검사 향상

Spray의 저자는 둥근 괄호가 컴파일 검사를 증가시킬 것을 권장합니다. 이것은 스프레이와 같은 DSL에 특히 중요합니다. parens를 사용하면 컴파일러에게 단 한 줄만 주어져야한다고 알려주므로 실수로 두 개 이상 줄 경우 불평 할 것입니다. 예를 들어 코드가 컴파일되는 곳의 연산자를 잊어 버리면 예기치 않은 결과가 발생하고 찾기 어려운 버그가 발생할 수 있습니다. 아래는 (표현이 순수하고 적어도 경고를 줄 것이기 때문에) 고안되었습니다.

method {
  1 +
  2
  3
}

method(
  1 +
  2
  3
 )

첫 번째는 컴파일하고 두 번째는 error: ')' expected but integer literal found.작성자가 쓰기를 원했습니다 1 + 2 + 3.

기본 인수를 가진 다중 매개 변수 메소드와 유사하다고 주장 할 수 있습니다. Parens를 사용할 때 매개 변수를 구분하기 위해 실수로 쉼표를 잊어 버리는 것은 불가능합니다.

다변

자세한 정보에 대해 간과되는 중요한 메모입니다. 중괄호를 사용하면 필연적으로 장황한 코드가 생깁니다. scala 스타일 가이드는 중괄호를 닫는 것이 자신의 줄에 있어야한다고 분명히 명시하고 있기 때문입니다. http://docs.scala-lang.org/style/declarations.html “… 닫는 중괄호 함수의 마지막 행 바로 다음에 자체 행에 있습니다. ” Intellij와 같은 많은 자동 재 포맷 프로그램이 자동으로이 재 포맷을 수행합니다. 따라서 가능하면 둥근 파렌을 사용하십시오. 예 List(1, 2, 3).reduceLeft{_ + _}:

List(1, 2, 3).reduceLeft {
  _ + _
}