괄호, 점, 중괄호, = (함수) 등을 생략 할 수있는 정확한 규칙은 무엇입니까?


106

괄호, 점, 중괄호, = (함수) 등을 생략 (생략) 할 수있는 정확한 규칙은 무엇입니까?

예를 들면

(service.findAllPresentations.get.first.votes.size) must be equalTo(2).
  • service 내 물건이야
  • def findAllPresentations: Option[List[Presentation]]
  • votes 보고 List[Vote]
  • must and be 는 둘 다 사양의 기능입니다.

갈 수없는 이유 :

(service findAllPresentations get first votes size) must be equalTo(2)

?

컴파일러 오류는 다음과 같습니다.

"Option [List [com.sharca.Presentation]] 유형의 RestServicesSpecTest.this.service.findAllPresentations는 매개 변수를 사용하지 않습니다."

매개 변수를 전달하려고하는 이유는 무엇입니까? 모든 메서드 호출에 왜 점을 사용해야합니까?

(service.findAllPresentations get first votes size)equalTo (2) 여야하는 이유는 다음과 같습니다.

"찾을 수 없음 : 값 우선"

그러나 "must be equalTo 2"의 "must be equalTo 2"는 (service.findAllPresentations.get.first.votes.size)equalTo 2 여야합니다. 즉, 메서드 체인이 제대로 작동합니까? -객체 체인 체인 체인 매개 변수.

나는 Scala 책과 웹 사이트를 훑어 보았지만 실제로 포괄적 인 설명을 찾을 수 없습니다.

사실 Rob H가 Stack Overflow 질문에서 설명 했듯이 Scala에서 어떤 문자를 생략 할 수 있습니까? , '.'를 생략하는 유일한 유효한 사용 사례입니다. "연산자 연산자 피연산자"스타일 연산을위한 것이고 메서드 체인을위한 것이 아닙니다.

답변:


87

당신은 대답을 우연히 발견 한 것 같습니다. 어쨌든 분명히 해보도록하겠습니다.

접두사, 중위 및 접미사 표기법 (소위 연산자 표기법)을 사용할 때 점을 생략 할 수 있습니다 . 연산자 표기법을 사용하는 동안에 만 메소드에 전달 된 매개 변수가 두 개 미만인 경우 괄호를 생략 할 수 있습니다.

이제 연산자 표기법은 method-call 표기법입니다. 즉, 호출 되는 객체가 없으면 사용할 수 없습니다.

표기법에 대해 간략하게 설명하겠습니다.

접두사:

~, !, +-접두사 표기법으로 사용할 수 있습니다. 이것은 당신이 쓸 때 사용하는 표기법 !flag이나 val liability = -debt.

중위 :

객체와 매개 변수 사이에 메서드가 나타나는 표기법입니다. 산술 연산자는 모두 여기에 적합합니다.

접미사 (또는 접미사) :

이 표기법은 메서드가 개체를 따르고 매개 변수를받지 않을 때 사용됩니다 . 예를 들어,라고 쓸 수 list tail있으며, 이것이 접미사 표기법입니다.

메서드가 커리되지 않는 한 문제없이 중위 표기법 호출을 연결할 수 있습니다. 예를 들어 다음 스타일을 사용하고 싶습니다.

(list
 filter (...)
 map (...)
 mkString ", "
)

그것은 다음과 같은 것입니다.

list filter (...) map (...) mkString ", "

이제 필터와 맵이 단일 매개 변수를 사용하는 경우 왜 여기에 괄호를 사용합니까? 익명 함수를 그들에게 전달하고 있기 때문입니다. 익명 함수의 끝에 경계가 필요하기 때문에 익명 함수 정의와 중위 스타일을 혼합 할 수 없습니다. 또한 익명 함수의 매개 변수 정의는 중위 메서드에 대한 마지막 매개 변수로 해석 될 수 있습니다.

여러 매개 변수와 함께 infix를 사용할 수 있습니다.

string substring (start, end) map (_ toInt) mkString ("<", ", ", ">")

카레 함수는 중위 표기법과 함께 사용하기 어렵습니다. 접기 기능은 다음과 같은 명확한 예입니다.

(0 /: list) ((cnt, string) => cnt + string.size)
(list foldLeft 0) ((cnt, string) => cnt + string.size)

중위 호출 외부에 괄호를 사용해야합니다. 여기에서 정확한 규칙을 잘 모르겠습니다.

이제 postfix에 대해 이야기 해 봅시다. 접미사 는 식의 끝을 제외하고는 사용할 수 없기 때문에 사용하기 어려울 수 있습니다 . 예를 들어 다음을 수행 할 수 없습니다.

 list tail map (...)

꼬리는 표현식 끝에 나타나지 않기 때문입니다. 다음 중 하나를 수행 할 수 없습니다.

 list tail length

식의 끝을 표시하기 위해 괄호를 사용하여 중위 표기법을 사용할 수 있습니다.

 (list tail) map (...)
 (list tail) length

후위 표기법은 안전하지 않을 수 있으므로 사용하지 않는 것이 좋습니다 .

나는 이것이 모든 의심을 없애기를 바랍니다. 그렇지 않은 경우 댓글을 남겨 주시면 개선 할 수있는 방법을 알아 보겠습니다.


아, 그래서 당신은 내 성명에서 다음과 같이 말하고 있습니다 : (((((realService findAllPresentations) get) first) votes) size) must be equalTo 2-get, first, votes 및 size는 매개 변수가 없기 때문에 모두 후위 연산자입니다. ? 그래서,하고 EqualTo가이 ... 있습니다해야하는지 궁금
안토니 스텁 스

나는 그럴 것이라고 확신하지만, 그런 종류의 말은하지 않는다. :-) "be"는 구문을 더 예쁘게 만드는 도우미 객체 일 것입니다. 또는 더 구체적으로 "must"와 함께 중위 표기법을 사용할 수 있습니다.
Daniel C. Sobral

잘 get은 Option.get, First는 list.first, votes는 케이스 클래스 속성이고 크기는 list.size입니다. 이제 어떻게 생각하세요?
Antony Stubbs

아 예-이것은 Service # findPresentations (id : Int)가 중위 연산자 인 것처럼 "(realService findPresentation 1) .get.id must be equalTo 1"이 작동한다는 사실에 의해 강화됩니다. 멋지다-나는 지금 그것을 얻는 것 같다. :)
Antony Stubbs

42

클래스 정의 :

val또는 var매개 변수를 비공개로 만드는 클래스 매개 변수에서 생략 할 수 있습니다.

var 또는 val을 추가하면 public이됩니다 (즉, 메서드 접근 자와 뮤 테이터가 생성됨).

{} 클래스에 본문이없는 경우 생략 할 수 있습니다.

class EmptyClass

클래스 인스턴스화 :

일반 매개 변수는 컴파일러에서 유추 할 수있는 경우 생략 할 수 있습니다. 그러나 유형이 일치하지 않으면 항상 일치하도록 유형 매개 변수가 유추됩니다. 따라서 유형을 지정하지 않으면 예상 한 결과를 얻지 못할 수 있습니다. 즉,

class D[T](val x:T, val y:T);

그러면 유형 오류가 발생합니다 (Int found, expected String).

var zz = new D[String]("Hi1", 1) // type error

이것이 잘 작동하는 반면 :

var z = new D("Hi1", 1)
== D{def x: Any; def y: Any}

유형 매개 변수 T는 둘 중 가장 덜 일반적인 상위 유형 인 Any로 추론되기 때문입니다.


기능 정의 :

= 함수가 단위 (아무것도)를 반환하면 삭제할 수 있습니다.

{}함수가 단일 문인 경우 함수 본문을 삭제할 수 있지만 문이 값을 반환하는 경우에만 ( =부호 가 필요함 ), 즉,

def returnAString = "Hi!"

그러나 이것은 작동하지 않습니다.

def returnAString "Hi!" // Compile error - '=' expected but string literal found."

함수의 반환 유형은 유추 할 수있는 경우 생략 할 수 있습니다 (재귀 메서드에는 반환 유형이 지정되어 있어야 함).

() 함수가 인수를받지 않으면 삭제할 수 있습니다.

def endOfString {
  return "myDog".substring(2,1)
}

관례 상 부작용이없는 메서드를 위해 예약되어 있습니다.

()이름 paramenter 로 패스를 정의 할 때 실제로 삭제되지는 않지만 실제로는 의미 상 상당히 다른 표기법입니다. 즉,

def myOp(passByNameString: => String)

myOp가 pass-by-name 매개 변수를 취하여 함수 매개 변수와 반대로 문자열 (즉, 문자열을 리턴하는 코드 블록이 될 수 있음)을 생성한다고 말합니다.

def myOp(functionParam: () => String)

myOp매개 변수가없는 함수를 취하고 문자열을 반환 한다고 말합니다 .

(이름에 의한 전달 매개 변수는 함수로 컴파일됩니다. 단지 구문을 더 멋지게 만듭니다.)

() 함수가 하나의 인수 만 사용하는 경우 함수 매개 변수 정의에서 삭제할 수 있습니다. 예를 들면 다음과 같습니다.

def myOp2(passByNameString:(Int) => String) { .. } // - You can drop the ()
def myOp2(passByNameString:Int => String) { .. }

그러나 둘 이상의 인수가 필요한 경우 ()를 포함해야합니다.

def myOp2(passByNameString:(Int, String) => String) { .. }

진술 :

.중위 연산자 (인수를받는 메서드의 연산자) 에만 사용할 수있는 연산자 표기법을 사용하기 위해 삭제할 수 있습니다 . 자세한 내용은 Daniel의 답변 을 참조하십시오.

  • . 후위 함수 목록 꼬리에 대해 삭제할 수도 있습니다.

  • () 접미사 연산자 list.tail에 대해 삭제할 수 있습니다.

  • () 다음과 같이 정의 된 메소드와 함께 사용할 수 없습니다.

    def aMethod = "hi!" // Missing () on method definition
    aMethod // Works
    aMethod() // Compile error when calling method

이 표기법은 List # tail과 같이 부작용이없는 메서드에 대한 규칙에 의해 예약되어 있기 때문입니다 (즉, 부작용이없는 함수의 호출은 함수가 반환 값을 제외하고 관찰 가능한 효과가 없음을 의미 함).

  • () 단일 인수를 전달할 때 연산자 표기법을 위해 삭제할 수 있습니다.

  • () 명령문 끝에없는 접미사 연산자를 사용해야 할 수 있습니다.

  • () 중첩 된 문, 익명 함수의 끝 또는 둘 이상의 매개 변수를 사용하는 연산자를 지정해야 할 수 있습니다.

함수를받는 함수를 호출 할 때 내부 함수 정의에서 ()를 생략 할 수 없습니다. 예를 들면 다음과 같습니다.

def myOp3(paramFunc0:() => String) {
    println(paramFunc0)
}
myOp3(() => "myop3") // Works
myOp3(=> "myop3") // Doesn't work

이름 별 매개 변수를 사용하는 함수를 호출 할 때 인수를 매개 변수없는 익명 함수로 지정할 수 없습니다. 예를 들면 다음과 같습니다.

def myOp2(passByNameString:Int => String) {
  println(passByNameString)
}

다음과 같이 호출해야합니다.

myOp("myop3")

또는

myOp({
  val source = sourceProvider.source
  val p = myObject.findNameFromSource(source)
  p
})

하지만:

myOp(() => "myop3") // Doesn't work

IMO, 반환 유형 삭제의 남용은 코드 재사용에 해로울 수 있습니다. 코드에 명시적인 정보가 없기 때문에 가독성이 떨어지는 좋은 예는 사양을 참조하십시오. 변수의 유형이 무엇인지 실제로 파악하기위한 간접 수준의 수는 엉터리 일 수 있습니다. 더 나은 도구가이 문제를 피하고 코드를 간결하게 유지할 수 있기를 바랍니다.

(좋아요, 더 완전하고 간결한 답변을 모으기위한 탐구에서 (누락 한 것이 있거나 잘못된 것이 있거나 부정확 한 것을 코멘트하십시오), 저는 답변의 시작 부분에 추가했습니다. 이것은 언어가 아닙니다. 사양, 그래서 나는 정확히 학문적으로 정확하게 만들려고 노력하지 않습니다-단지 참조 카드처럼.)


10
난 울고있어. 이게 뭐야.
Profpatsch 2015

12

다양한 조건에 대한 통찰력을 제공하는 인용문 모음 ...

개인적으로 사양에 더 많은 것이있을 것이라고 생각했습니다. 나는 틀림없이있을 것이라고 확신합니다, 나는 단지 올바른 단어를 찾고 있지 않습니다 ...

그러나 몇 가지 소스가 있으며 함께 수집했지만 위의 문제를 설명하는 완전히 / 포괄적 / 이해할 수있는 것은 없습니다 ... :

"메서드 본문에 둘 이상의 표현식이있는 경우 중괄호 {…}로 묶어야합니다. 메소드 본문에 표현식이 하나만 있으면 중괄호를 생략 할 수 있습니다."

에서 2 장,의, "형 덜, 더 수행" 스칼라 프로그래밍 :

"상위 메소드의 본문은 등호 '='뒤에옵니다. 왜 등호가 필요한가요? Java 에서처럼 중괄호 {…} 만 사용하지 않는 이유는 무엇입니까? 세미콜론, 함수 반환 유형, 메소드 인수 목록 및 중괄호까지도 등호를 사용하면 몇 가지 가능한 파싱 모호성을 방지 할 수 있습니다. 등호를 사용하면 함수조차도 Scala의 값이라는 것을 상기시켜줍니다. 이는 Scala의 함수형 프로그래밍 지원과 일치합니다. 이는 8 장, 함수형 프로그래밍에서 자세히 설명합니다. 스칼라. "

에서 의 : 제 1 장, "스칼라를 소개 제로 육십에" 스칼라 프로그래밍 :

"매개 변수가없는 함수는 괄호없이 선언 할 수 있습니다.이 경우에는 괄호없이 호출해야합니다. 이렇게하면 호출자가 기호가 변수인지 또는없는 함수인지 알 수 없도록 통일 액세스 원칙을 지원합니다. 매개 변수.

함수 본문은 값을 반환하는 경우 (즉, 반환 유형이 Unit이 아닌 경우) "="앞에옵니다. 그러나 유형이 Unit 일 때 반환 유형과 "="를 생략 할 수 있습니다 (즉, 프로 시저처럼 보입니다). 기능과는 반대로).

몸 주위의 중괄호는 필요하지 않습니다 (몸이 단일 표현식 인 경우). 더 정확하게는 함수의 본문은 표현식 일 뿐이며 여러 부분이있는 표현식은 중괄호로 묶어야합니다 (한 부분이있는 표현식은 선택적으로 중괄호로 묶을 수 있음). "

"인수가 0 개 또는 1 개인 함수는 점과 괄호없이 호출 할 수 있습니다. 그러나 모든 표현식 주변에 괄호가있을 수 있으므로 점을 생략하고 괄호를 계속 사용할 수 있습니다.

그리고 괄호를 사용할 수있는 곳이면 어디에서나 중괄호를 사용할 수 있으므로 점을 생략하고 여러 문을 포함 할 수있는 중괄호에 넣을 수 있습니다.

인수가없는 함수는 괄호없이 호출 할 수 있습니다. 예를 들어, String의 length () 함수는 "abc".length ()가 아닌 "abc".length로 호출 될 수 있습니다. 함수가 괄호없이 정의 된 Scala 함수 인 경우 함수는 괄호없이 호출되어야합니다.

관례 적으로 println과 같이 부작용이있는 인수가없는 함수는 괄호로 호출됩니다. 부작용이없는 것은 괄호없이 호출됩니다. "

블로그 게시물 Scala Syntax Primer에서 :

"프로 시저 정의는 결과 유형과 등호가 생략 된 함수 정의입니다. 정의 표현식은 블록이어야합니다. 예를 들어, def f (ps) {stats}는 def f (ps)와 동일합니다. Unit = {stats }.

Example 4.6.3 다음은 write라는 프로 시저의 선언과 정의입니다.

trait Writer {
    def write(str: String)
}
object Terminal extends Writer {
    def write(str: String) { System.out.println(str) }
}

위의 코드는 암시 적으로 다음 코드로 완성됩니다.

trait Writer {
    def write(str: String): Unit
}
object Terminal extends Writer {
    def write(str: String): Unit = { System.out.println(str) }
}"

언어 사양에서 :

"단일 매개 변수 만 취하는 메서드를 사용하면 Scala를 사용하면 개발자가.를 공백으로 바꾸고 괄호를 생략하여 삽입 연산자 예제에 표시된 연산자 구문을 사용할 수 있습니다.이 구문은 Scala API의 다른 위치에서 사용됩니다. Range 인스턴스를 구성 할 때 :

val firstTen:Range = 0 to 9

여기서 다시 to (Int)는 클래스 내부에 선언 된 바닐라 메서드입니다 (실제로 여기에 더 많은 암시 적 형식 변환이 있지만 드리프트를 얻습니다). "

에서 자바 난민 6 부 스칼라 : 이상 자바 얻기 :

"이제"m 0 "을 시도하면 Scala는 유효한 단항 연산자 (~,!,-및 +)가 아니라는 이유로 단항 연산자를 버립니다."m "이 유효한 객체라는 것을 발견합니다. 메서드가 아니라 함수이며 모든 함수는 객체입니다.

"0"은 유효한 Scala 식별자가 아니므로 중위 또는 접미사 연산자가 될 수 없습니다. 따라서 Scala는 ";"를 예상했다고 불평합니다. -두 개의 (거의) 유효한 표현식 인 "m"과 "0"을 분리합니다. 만약 당신이 그것을 삽입한다면, m은 인수가 필요하다고 불평 할 것입니다. 그렇지 않을 경우 부분적으로 적용된 함수로 바꾸려면 "_"가 필요합니다. "

"연산자 구문 스타일은 왼쪽에 명시적인 객체가있을 때만 작동한다고 생각합니다. 구문은"연산자 연산자 "스타일 연산을 자연스럽게 표현할 수 있도록하기위한 것입니다."

Scala에서 어떤 문자를 생략 할 수 있습니까?

그러나 저를 혼란스럽게하는 것은이 인용문입니다.

"메서드 호출을 수신하려면 객체가 있어야합니다. 예를 들어 println에 객체 수신자가 필요하므로"println "Hello World!" "를 수행 할 수 없습니다. "Console println"Hello World! ""를 수행 할 수 있습니다. "

내가 볼 수있는 한, 전화를받을 물건 있기 때문에 ...


1
좋아, 그래서 몇 가지 단서와 woah를 얻기 위해 사양 소스를 읽어 보았습니다. 이는 너무 많은 믹스 인, 유형 추론 및 암시 적 변환 및 암시 적 매개 변수와 같은 매직 코드의 문제에 대한 훌륭한 예입니다. 외부에서 이해하기가 너무 어렵습니다! 그런 큰 라이브러리의 경우, 더 나은 도구는 ... 몇 가지 놀라운 ... 일일를 할 수
안토니 스텁 스

3

이 경험 법칙을 따르는 것이 더 쉽다는 것을 알았습니다. 표현식에서 공간은 메소드와 매개 변수 사이를 번갈아 가며 나타납니다. 당신의 예에서, (service.findAllPresentations.get.first.votes.size) must be equalTo(2)로 구문 분석합니다 (service.findAllPresentations.get.first.votes.size).must(be)(equalTo(2)). 2 주위의 괄호는 공간보다 연관성이 더 높습니다. 도트는 또한, 더 높은 연관성을 가지고 (service.findAllPresentations.get.first.votes.size) must be.equalTo(2)로서 구문 분석 (service.findAllPresentations.get.first.votes.size).must(be.equalTo(2)).

service findAllPresentations get first votes size must be equalTo 2파싱로 service.findAllPresentations(get).first(votes).size(must).be(equalTo).2.


2

실제로 두 번째 독서에서 이것이 핵심 일 수 있습니다.

단일 매개 변수 만 사용하는 메서드를 사용하면 Scala를 통해 개발자가. 공백이 있고 괄호는 생략

블로그 게시물에서 언급했듯이 http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6 .

따라서 이것은 실제로 매우 엄격한 "구문 설탕"으로 , 하나의 매개 변수를 사용하는 객체에서 효과적으로 메소드를 호출하는 경우 에만 작동합니다 . 예 :

1 + 2
1.+(2)

그리고 다른 것은 없습니다.

이것은 질문의 내 예를 설명합니다.

그러나 내가 말했듯이 누군가가 언어 사양에서 이것이 지정된 정확한 위치를 지적 할 수 있다면 대단히 감사하겠습니다.

좋은 동료 (#scala의 paulp_)가 언어 사양에서이 정보가 어디에 있는지 지적했습니다.

6.12.3 : 연산자의 우선 순위와 연관성은 다음과 같이 표현식 부분의 그룹화를 결정합니다.

  • 식에 여러 중위 연산이있는 경우 우선 순위가 높은 연산자는 우선 순위가 낮은 연산자보다 더 밀접하게 바인딩됩니다.
  • 연속 중위 연산이있는 경우 e0 op1 e1 op2. . .opn en 연산자 op1,. . . , opn이 동일한 우선 순위의 경우 이러한 모든 연산자는 동일한 연관성을 가져야합니다. 모든 연산자가 왼쪽 연관이면 시퀀스는 (... (e0 op1 e1) op2...) opn en으로 해석됩니다. 그렇지 않고 모든 연산자가 rightassociative이면 시퀀스는 e0 op1 (e1 op2 (.. .opn en)...)으로 해석됩니다.
  • 후위 연산자는 항상 중위 연산자보다 우선 순위가 낮습니다. 예를 들어 e1 op1 e2 op2는 항상 (e1 op1 e2) op2와 같습니다.

왼쪽 연관 연산자의 오른쪽 피연산자는 괄호로 묶인 여러 인수로 구성 될 수 있습니다 (예 : e op (e1,..., en)). 이 표현식은 e.op (e1,..., en)으로 해석됩니다.

왼쪽 연관 이진 연산 e1 op e2는 e1.op (e2)로 해석됩니다. op가 rightassociative이면 동일한 연산이 {val x = e1; e2.op (x)}, 여기서 x는 새로운 이름입니다.

흠-나에게 그것은 내가보고있는 것과 맞지 않거나 이해하지 못합니다.)


흠, 혼동을 더하기 위해 이것도 유효합니다. (((((realService findAllPresentations) get) first) votes) size)는 반드시 equalTo 2 여야하지만 괄호 쌍 중 하나라도 제거하면 안됩니다 ...
Antony Stubbs

2

아무것도 없습니다. 함수에 부작용이 있는지 여부에 대한 조언을받을 수 있습니다. 이것은 가짜입니다. 수정은 Scala에서 허용하는 합당한 범위까지 부작용을 사용하지 않는 것입니다. 할 수없는 경우 모든 베팅이 해제됩니다. 모든 베팅. 괄호를 사용하는 것은 "all"집합의 요소이며 불필요합니다. 모든 베팅이 해제되면 어떠한 가치도 제공하지 않습니다.

이 조언은 본질적으로 실패한 효과 시스템에 대한 시도 입니다 (혼동하지 마십시오 : 다른 효과 시스템보다 유용하지 않음).

부작용을 일으키지 마십시오. 그 후, 모든 베팅이 꺼져 있음을 수락하십시오. 효과 시스템에 대한 사실상의 구문 표기법 뒤에 숨기는 것은 해를 입힐 수 있으며 해로울뿐입니다.


하지만 그것이 하이브리드 OO / 기능적 언어로 작업 할 때 문제입니다. 맞죠? 어떤 실제적인 예에서 부작용 기능을 원할 것입니다. "효과 시스템"에 대한 정보를 알려 주시겠습니까? 더 많은 인용문은 "파라미터가없는 함수는 괄호없이 선언 할 수 있습니다.이 경우에는 괄호없이 호출해야합니다. 이는 호출자가 다음을 알 수 없도록 통일 액세스 원칙을 지원합니다. 기호는 매개 변수가없는 변수 또는 함수입니다. ".
Antony Stubbs
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.