省略括号、圆点、大括号、 = (函数)等等的确切规则是什么?

省略(省略)括号、点、大括号、 = (函数)等等的确切规则是什么?

比如说,

(service.findAllPresentations.get.first.votes.size) must be equalTo(2).
  • service is my object
  • def findAllPresentations: Option[List[Presentation]]
  • 返回 List[Vote]
  • Must 都是 specs 的函数

为什么我不能去:

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

?

编译器错误是:

”RestServicesSpecTest.this.service.findAllPresentations 类别 选项[列表[ com.sharca. 简报]] 没有参数”

为什么它认为我要传递一个参数? 为什么我必须为每个方法调用使用点?

为什么 (service.findAllPresentations get first votes size)必须等于(2)导致:

“未找到: 价值优先”

然而,“必须等于2”的 (service.findAllPresentations.get.first.votes.size)必须等于2,也就是说,方法链工程细?-对象链链参数。

我浏览了 Scala 的书和网站,并没有找到一个全面的解释。

事实上,正如 Rob H 在 Stack Overflow 问题 Which characters can I omit in Scala?中解释的那样,省略‘的唯一有效用例是用于“ operand 操作符 operand”样式的操作,而不是用于方法链接?

23449 次浏览

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

我个人认为说明书里应该有更多内容。我相信肯定有,只是我没找到合适的词。

然而,这里有一些资料来源,我已经把它们收集在一起,但没有什么真正完整/全面/可以理解/来解释上述问题给我... ..:

”如果一个方法体有多个 表达式,你必须围绕它 花括号{ ... } 如果方法体只有一个大括号 表情”

来自 《 Scala 程序设计》第二章“少打字,多做事”:

“上层方法的主体来了 在等号‘ =’之后 等于符号? 为什么不直接用大括号呢 { ... } ,就像 Java 语言一样? 因为分号, 函数返回类型、方法 arguments lists, and even the curly 括号有时被省略,使用 等于号阻止了几种可能 解析歧义。使用等于 这个标志也提醒我们 函数是 Scala 中的值,它 与 Scala 支持的 中描述的函数式编程 详情请参阅第8章「功能性」 用 Scala 编程”

来自 第一章,《从零到六十: Scala 介绍》 ,出自《编程 Scala 》:

”没有参数的函数可以是 declared without parentheses, in which 如果它必须调用为 no 括号。这提供了对 统一存取原则,例如 that the caller does not know if the 符号是一个变量或函数 没有参数。

函数体前面是“ =” 如果它返回一个值(即返回 类型是单位以外的东西) , but the return type and the "=" can be 当类型为 Unit (即 看起来像是一个过程,而不是一个 功能)。

身体周围的牙套就不是了 需要(如身体是一个单一的 更准确地说,是身体 只是一个表达式, 以及任何有多个部分的表达式 must be enclosed in braces (an 一部分的表达式可以 可以选择用括号括起来)

”具有零个或一个参数的函数 可以没有点和 但任何表达式都可以 在它周围加上括号,这样你就可以 省略点仍然使用 括号。

And since you can use braces anywhere 你可以用括号,也可以省略 点和大括号,这可以 包含多个语句。

Functions with no arguments can be 没有括号 上的 length ()函数 字符串可以调用为“ abc”. length 而不是“ abc”。 length () function is a Scala function defined 没有括号,那么函数 必须不带括号地调用。

根据约定,函数的值为 no 有副作用的参数,例如 作为 println 调用 parentheses; those without side effects are called without 括号

来自博客文章 Scala Syntax Primer:

”过程定义是一个函数 定义其中的结果类型和 省略等号; 它的 定义表达式必须是块。 例如,def (ps){ stats }是 等效于 def (ps) : Unit = { stats }.

示例4.6.3下面是一个声明 和一个程序的定义 写道:

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

上面的代码是隐式完成的 to the following code:

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

Here again, to(Int) is a vanilla 类中声明的方法 (实际上还有一些更含蓄的 在这里输入转换,但是您会得到 drift)."

来自 Scala 用于 Java 难民第六部分: 克服 Java:

“现在,当你尝试“ m 0”,Scala discards it being a unary operator, on 不合法的理由 (~ ,! ,-and +)。它发现“ m”是 一个有效的对象——它是一个函数, not a method, and all functions are 物品。

因为“0”不是有效的 Scala 标识符时,它不能既是 中缀或后缀操作符。 因此,Scala 抱怨 expected ";" -- which would separate 两个(几乎)有效的表达式: “ m” and "0". If you inserted it, then it 会抱怨 m 要求 an argument, or, failing that, a "_" 把它变成部分应用的 功能。」

"I believe the operator syntax style 只有当你有一个露骨的 在左手边的对象 语法的目的是让您表达 “ operand 操作符 operand”样式 operations in a natural way."

在 Scala 中我可以省略哪些字符?

但同样让我困惑的是这句话:

”需要有一个目标 接收方法调用。例如, 你不能做“ println“ Hello World!” as the println needs an object 收件人。你可以做“控制台 “你好,世界!” 满足需求”

因为据我所知,有一个 对象接收调用..。

事实上,在二读时,也许这就是关键:

方法只需要一个 参数,Scala 允许开发人员 用空格替换. 并省略 括号

正如博客文章中提到的: http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6

因此,也许这实际上是一个非常严格的“语法糖”,只有在实际上是 calling a method, on an object, which takes one parameter的情况下工作。

1 + 2
1.+(2)

没别的了。

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.

好吧,一些不错的家伙(paulp _ from # scala)已经指出了这些信息在语言规范中的位置:

6.12.3: 优先性和结合性 运算符确定 表达式的一部分,如下。

  • 如果表达式中有多个中缀操作,则 具有较高优先级绑定的运算符 more closely than operators with lower 优先级。
  • 如果有连续的中缀操作 e0 op1 e1 op2. . open 与操作员 op1,. . ,开放的 same precedence, then all these 运算符必须具有相同的 如果所有运算符都是 左结合,序列是 解释为(. . (e0 op1 e1) op2 . .)打开。否则,如果所有 运算符是右结合的 序列解释为 e0 op1(e1 Op2(. . 打开) . . 。
  • 后缀运算符的优先级总是低于中缀运算符。 E1 op1 e2 op2总是等价于 (e1 op1 e2) op2.

控件的右操作数 左结合运算符可由 附在 括号,例如 e op (e1,. . 。 ,en) . 这个表达式是 then 解释为 e.op (e1,. . ,en)。

左联想二元运算 e 1 Ope2被解释为 e1.op (e2) 操作是右联想,相同的 操作解释为{ val X = e1; e2.op (x)} ,其中 x 是一个 new 姓名。

嗯——对我来说,它与我所看到的不一致,或者我只是不理解它;)

你似乎无意中找到了答案。不管怎样,我会尽量说清楚的。

在使用前缀、中缀和后缀符号时,可以省略点号——即所谓的 运算符号。在使用运算符表示法时,只有在这种情况下,如果传递给方法的参数少于两个,则可以省略括号。

现在,运算符符号是 方法调用的符号,这意味着它不能在没有被调用的对象的情况下使用。

我会简要说明一下注释的细节。

前缀:

只有 ~!+-可以用作前缀符号。这是您在编写 !flagval liability = -debt时使用的符号。

中缀:

这是方法出现在对象和它的参数之间的符号。

Postfix (also suffix):

当方法遵循对象 没有任何参数时使用该表示法。例如,你可以编写 ABc0,这就是逆波兰表示法。

您可以毫无问题地链接中缀符号调用,只要没有套用任何方法。例如,我喜欢使用以下风格:

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

这和:

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

现在,为什么我在这里使用括号,如果过滤器和映射采取一个参数?那是因为我给他们传递了匿名函数。我不能将匿名函数定义和中缀样式混合使用,因为我需要一个结束匿名函数的边界。此外,匿名函数的参数定义可能被解释为中缀方法的最后一个参数。

可以使用带有多个参数的中缀:

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

中缀表示法很难使用咖喱函数,折叠函数就是一个明显的例子:

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

你需要在中缀调用之外使用括号。我不确定这里的具体规则。

现在,我们来谈谈后缀。后缀很难使用,因为它是 can never be used anywhere except the end of an expression。例如,您不能执行以下操作:

 list tail map (...)

因为 tail 不会出现在表达式的末尾,你也不能这样做:

 list tail length

你可以使用中缀符号,使用圆括号来标记表达式的结束:

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

请注意,逆波兰表示法是不被鼓励的,因为 可能不安全

我希望这消除了所有的疑虑。如果没有,只要发表评论,我会看看我能做些什么来改进它。

类别定义:

可以从类参数中省略 valvar,这将使参数成为私有的。

添加 var 或 val 将使其成为 public (即生成方法访问器和 mutator)。

{} can be omitted if the class has no body, that is,

class EmptyClass

类实例化:

如果编译器可以推断泛型参数,则可以省略它们。但是请注意,如果类型不匹配,则始终推断类型参数以使其匹配。因此,如果没有指定类型,您可能得不到所期望的结果——也就是给定的结果

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

这将给出一个类型错误 (发现整数,期望字符串)

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

然而,这种方法很有效:

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

因为类型参数 T 被推断为两个-Any 中最不常见的超类型。


功能定义:

如果函数返回 Unit (无) ,则可以删除 =

如果函数是单个语句,那么函数体的 {}可以被删除,但是只有当语句返回一个值(您需要 =符号)时,

def returnAString = "Hi!"

但这不管用:

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

如果可以推断函数的返回类型,则可以省略它(递归方法必须指定其返回类型)。

如果函数不接受任何参数,也就是说,

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

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

在定义 pass by name参数时,()本身并没有被删除,但它实际上是一种语义上完全不同的表示法,即,

def myOp(passByNameString: => String)

说 myOp 接受一个按名称传递的参数,结果是一个 String (也就是说,它可以是一个返回字符串的代码块) ,而不是函数参数,

def myOp(functionParam: () => String)

表示 myOp接受一个参数为零的函数并返回一个 String。

(请注意,按名称传递的参数会被编译成函数; 这只会使语法更好。)

如果函数只接受一个参数,那么可以在函数参数定义中删除 (),例如:

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

但是如果需要多个参数,则必须包括() :

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

声明:

可以删除 .以使用运算符表示法,只有可用于中缀运算符(带参数的方法的运算符)。有关更多信息,请参见 丹尼尔的回答

  • 对于后缀函数,也可以删除 . 列表 tail

  • 后缀操作符可以删除 () List. tail

  • ()不能用于定义为:

    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

当调用接受按名称参数的函数时,不能将参数指定为无参数的匿名函数。例如:

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

You must call it as:

myOp("myop3")

或者

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

但不是:

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.

(好吧,为了编辑一个更完整、更简洁的答案(如果我错过了什么,或者得到了什么错误/不准确的评论) ,我在答案的开头添加了一些内容。请注意,这不是一个语言规范,所以我并不打算让它在学术上完全正确——只是更像一个参考卡片。)

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. 全部 bets. Using parentheses is an element of the set "all" and is superfluous. It does not provide any value once all bets are off.

这个建议本质上是一个失败的 effect system的尝试(不要混淆: 没有其他效果系统有用)。

尽量不要产生副作用。在那之后,接受所有的赌注都会被取消。隐藏在效果系统的事实语法符号之后只会造成伤害。

我发现更容易遵循这个经验法则: 在表达式中,方法和参数之间交替使用空格。在您的示例中,(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