developer tip

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

copycodes 2020. 8. 19. 19:48
반응형

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


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

예를 들면

(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에서 어떤 문자를 생략 할 수 있습니까? , '.'를 생략하는 유일한 유효한 사용 사례입니다. "연산자 연산자 피연산자"스타일 연산을위한 것이고 메서드 체인을위한 것이 아닙니다.


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

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

이제 연산자 표기법은 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

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

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


클래스 정의 :

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

The return type of the function can be omitted if it can be inferred (a recursive method must have its return type specified).

() can be dropped if the function doesn't take any arguments, that is,

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

which by convention is reserved for methods which have no side effects - more on that later.

() isn't actually dropped per se when defining a pass by name paramenter, but it is actually a quite semantically different notation, that is,

def myOp(passByNameString: => String)

Says myOp takes a pass-by-name parameter, which results in a String (that is, it can be a code block which returns a string) as opposed to function parameters,

def myOp(functionParam: () => String)

which says myOp takes a function which has zero parameters and returns a String.

(Mind you, pass-by-name parameters get compiled into functions; it just makes the syntax nicer.)

() can be dropped in the function parameter definition if the function only takes one argument, for example:

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

But if it takes more than one argument, you must include the ():

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

Statements:

. can be dropped to use operator notation, which can only be used for infix operators (operators of methods that take arguments). See Daniel's answer for more information.

  • . can also be dropped for postfix functions list tail

  • () can be dropped for postfix operators list.tail

  • () cannot be used with methods defined as:

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

Because this notation is reserved by convention for methods that have no side effects, like List#tail (that is, the invocation of a function with no side effects means that the function has no observable effect, except for its return value).

  • () can be dropped for operator notation when passing in a single argument

  • () may be required to use postfix operators which aren't at the end of a statement

  • () may be required to designate nested statements, ends of anonymous functions or for operators which take more than one parameter

When calling a function which takes a function, you cannot omit the () from the inner function definition, for example:

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

When calling a function that takes a by-name parameter, you cannot specify the argument as a parameter-less anonymous function. For example, given:

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

You must call it as:

myOp("myop3")

or

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

but not:

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

IMO, overuse of dropping return types can be harmful for code to be re-used. Just look at specification for a good example of reduced readability due to lack of explicit information in the code. The number of levels of indirection to actually figure out what the type of a variable is can be nuts. Hopefully better tools can avert this problem and keep our code concise.

(OK, in the quest to compile a more complete, concise answer (if I've missed anything, or gotten something wrong/inaccurate please comment), I have added to the beginning of the answer. Please note this isn't a language specification, so I'm not trying to make it exactly academically correct - just more like a reference card.)


A collection of quotes giving insight into the various conditions...

Personally, I thought there'd be more in the specification. I'm sure there must be, I'm just not searching for the right words...

There are a couple of sources however, and I've collected them together, but nothing really complete / comprehensive / understandable / that explains the above problems to me...:

"If a method body has more than one expression, you must surround it with curly braces {…}. You can omit the braces if the method body has just one expression."

From chapter 2, "Type Less, Do More", of Programming Scala:

"The body of the upper method comes after the equals sign ‘=’. Why an equals sign? Why not just curly braces {…}, like in Java? Because semicolons, function return types, method arguments lists, and even the curly braces are sometimes omitted, using an equals sign prevents several possible parsing ambiguities. Using an equals sign also reminds us that even functions are values in Scala, which is consistent with Scala’s support of functional programming, described in more detail in Chapter 8, Functional Programming in Scala."

From chapter 1, "Zero to Sixty: Introducing Scala", of Programming Scala:

"A function with no parameters can be declared without parentheses, in which case it must be called with no parentheses. This provides support for the Uniform Access Principle, such that the caller does not know if the symbol is a variable or a function with no parameters.

The function body is preceded by "=" if it returns a value (i.e. the return type is something other than Unit), but the return type and the "=" can be omitted when the type is Unit (i.e. it looks like a procedure as opposed to a function).

Braces around the body are not required (if the body is a single expression); more precisely, the body of a function is just an expression, and any expression with multiple parts must be enclosed in braces (an expression with one part may optionally be enclosed in braces)."

"Functions with zero or one argument can be called without the dot and parentheses. But any expression can have parentheses around it, so you can omit the dot and still use parentheses.

And since you can use braces anywhere you can use parentheses, you can omit the dot and put in braces, which can contain multiple statements.

Functions with no arguments can be called without the parentheses. For example, the length() function on String can be invoked as "abc".length rather than "abc".length(). If the function is a Scala function defined without parentheses, then the function must be called without parentheses.

By convention, functions with no arguments that have side effects, such as println, are called with parentheses; those without side effects are called without parentheses."

From blog post Scala Syntax Primer:

"A procedure definition is a function definition where the result type and the equals sign are omitted; its defining expression must be a block. E.g., def f (ps) {stats} is equivalent to def f (ps): Unit = {stats}.

Example 4.6.3 Here is a declaration and a de?nition of a procedure named write:

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

The code above is implicitly completed to the following code:

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

From the language specification:

"With methods which only take a single parameter, Scala allows the developer to replace the . with a space and omit the parentheses, enabling the operator syntax shown in our insertion operator example. This syntax is used in other places in the Scala API, such as constructing Range instances:

val firstTen:Range = 0 to 9

Here again, to(Int) is a vanilla method declared inside a class (there’s actually some more implicit type conversions here, but you get the drift)."

From Scala for Java Refugees Part 6: Getting Over Java:

"Now, when you try "m 0", Scala discards it being a unary operator, on the grounds of not being a valid one (~, !, - and +). It finds that "m" is a valid object -- it is a function, not a method, and all functions are objects.

As "0" is not a valid Scala identifier, it cannot be neither an infix nor a postfix operator. Therefore, Scala complains that it expected ";" -- which would separate two (almost) valid expressions: "m" and "0". If you inserted it, then it would complain that m requires either an argument, or, failing that, a "_" to turn it into a partially applied function."

"I believe the operator syntax style works only when you've got an explicit object on the left-hand side. The syntax is intended to let you express "operand operator operand" style operations in a natural way."

Which characters can I omit in Scala?

But what also confuses me is this quote:

"There needs to be an object to receive a method call. For instance, you cannot do “println “Hello World!”" as the println needs an object recipient. You can do “Console println “Hello World!”" which satisfies the need."

Because as far as I can see, there is an object to receive the call...


I find it easier to follow this rule of thumb: in expressions spaces alternate between methods and parameters. In your example, (service.findAllPresentations.get.first.votes.size) must be equalTo(2) parses as (service.findAllPresentations.get.first.votes.size).must(be)(equalTo(2)). Note that the parentheses around the 2 have a higher associativity than the spaces. Dots also have higher associativity, so (service.findAllPresentations.get.first.votes.size) must be.equalTo(2)would parse as (service.findAllPresentations.get.first.votes.size).must(be.equalTo(2)).

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


Actually, on second reading, maybe this is the key:

With methods which only take a single parameter, Scala allows the developer to replace the . with a space and omit the parentheses

As mentioned on the blog post: http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6 .

So perhaps this is actually a very strict "syntax sugar" which only works where you are effectively calling a method, on an object, which takes one parameter. e.g.

1 + 2
1.+(2)

And nothing else.

This would explain my examples in the question.

But as I said, if someone could point out to be exactly where in the language spec this is specified, would be great appreciated.

Ok, some nice fellow (paulp_ from #scala) has pointed out where in the language spec this information is:

6.12.3: Precedence and associativity of operators determine the grouping of parts of an expression as follows.

  • If there are several infix operations in an expression, then operators with higher precedence bind more closely than operators with lower precedence.
  • If there are consecutive infix operations e0 op1 e1 op2 . . .opn en with operators op1, . . . , opn of the same precedence, then all these operators must have the same associativity. If all operators are left-associative, the sequence is interpreted as (. . . (e0 op1 e1) op2 . . .) opn en. Otherwise, if all operators are rightassociative, the sequence is interpreted as e0 op1 (e1 op2 (. . .opn en) . . .).
  • Postfix operators always have lower precedence than infix operators. E.g. e1 op1 e2 op2 is always equivalent to (e1 op1 e2) op2.

The right-hand operand of a left-associative operator may consist of several arguments enclosed in parentheses, e.g. e op (e1, . . . ,en). This expression is then interpreted as e.op(e1, . . . ,en).

A left-associative binary operation e1 op e2 is interpreted as e1.op(e2). If op is rightassociative, the same operation is interpreted as { val x=e1; e2.op(x ) }, where x is a fresh name.

Hmm - to me it doesn't mesh with what I'm seeing or I just don't understand it ;)


There aren't any. You will likely receive advice around whether or not the function has side-effects. This is bogus. The correction is to not use side-effects to the reasonable extent permitted by Scala. To the extent that it cannot, then all bets are off. All bets. Using parentheses is an element of the set "all" and is superfluous. It does not provide any value once all bets are off.

This advice is essentially an attempt at an effect system that fails (not to be confused with: is less useful than other effect systems).

Try not to side-effect. After that, accept that all bets are off. Hiding behind a de facto syntactic notation for an effect system can and does, only cause harm.

참고URL : https://stackoverflow.com/questions/1181533/what-are-the-precise-rules-for-when-you-can-omit-parenthesis-dots-braces-f

반응형