什么时候可以省略括号,点,大括号,=(函数)等的精确规则?

Ant*_*bbs 101 syntax scala

什么时候可以省略(省略)括号,圆点,大括号,=(函数)等的精确规则?

例如,

(service.findAllPresentations.get.first.votes.size) must be equalTo(2).
Run Code Online (Sandbox Code Playgroud)
  • service 是我的对象
  • def findAllPresentations: Option[List[Presentation]]
  • votes 回报 List[Vote]
  • 必须有规格的两种功能

为什么我不能去:

(service findAllPresentations get first votes size) must be equalTo(2)
Run Code Online (Sandbox Code Playgroud)

编译器错误是:

"类型为Option [List [com.sharca.Presentation]]的RestServicesSpecTest.this.service.findAllPresentations不带参数"

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

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

"没找到:价值第一"

然而,"必须等于2" (service.findAllPresentations.get.first.votes.size)必须等于2,即方法链是否正常? - 对象链链链接.

我查看了Scala的书籍和网站,无法找到全面的解释.

事实上,正如Rob H在Stack Overflow问题中解释的那样,我可以在Scala中省略哪些字符?,这是省略'.'的唯一有效用例.是用于"操作数运算符操作数"样式操作,而不是用于方法链接?

Dan*_*ral 84

你似乎偶然发现了答案.无论如何,我会尽力说清楚.

使用前缀,中缀和后缀表示法时可以省略点 - 所谓的运算符表示法.使用运算符表示法时,只有这样,如果传递给方法的参数少于两个,则可以省略括号.

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

我将简要介绍一下这些符号.

字首:

只有~,!,+并且-可以在前缀符号使用.这是当你写你所使用的符号!flagval liability = -debt.

中缀:

这是方法出现在对象和它的参数之间的符号.算术运算符都适合这里.

后缀(也是后缀):

当方法跟随对象并且不接收任何参数时,使用该表示法.例如,您可以编写list tail,这是后缀表示法.

只要没有方法,就可以毫无问题地链接中缀符号.例如,我喜欢使用以下样式:

(list
 filter (...)
 map (...)
 mkString ", "
)
Run Code Online (Sandbox Code Playgroud)

这与以下内容相同:

list filter (...) map (...) mkString ", "
Run Code Online (Sandbox Code Playgroud)

现在,为什么我在这里使用括号,如果filter和map只使用一个参数?这是因为我将匿名函数传递给他们.我不能将匿名函数定义与中缀样式混合,因为我需要一个边界来结束我的匿名函数.此外,匿名函数的参数定义可能被解释为中缀方法的最后一个参数.

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

string substring (start, end) map (_ toInt) mkString ("<", ", ", ">")
Run Code Online (Sandbox Code Playgroud)

使用中缀表示法很难使用Curried函数.折叠功能就是一个明显的例子:

(0 /: list) ((cnt, string) => cnt + string.size)
(list foldLeft 0) ((cnt, string) => cnt + string.size)
Run Code Online (Sandbox Code Playgroud)

您需要在中缀调用之外使用括号.我不确定这里的确切规则.

现在,我们来谈谈postfix.Postfix可能很难使用,因为除了表达式的结尾之外,它永远不会被使用.例如,您无法执行以下操作:

 list tail map (...)
Run Code Online (Sandbox Code Playgroud)

因为尾部没有出现在表达式的末尾.你不能这样做:

 list tail length
Run Code Online (Sandbox Code Playgroud)

您可以使用括号来标记表达式的结尾来使用中缀表示法:

 (list tail) map (...)
 (list tail) length
Run Code Online (Sandbox Code Playgroud)

请注意,不建议使用后缀表示法,因为它可能不安全.

我希望这已经消除了所有的疑虑.如果没有,只需发表评论,我就会看到我可以做些什么来改进它.


Ant*_*bbs 39

类定义:

val或者var可以从类参数中省略,这将使参数变为私有.

添加var或val将导致它是公共的(即生成方法访问器和更改器).

{} 如果类没有正文,则可以省略,即

class EmptyClass
Run Code Online (Sandbox Code Playgroud)

类实例化:

如果可以由编译器推断出通用参数,则可以省略它们.但请注意,如果您的类型不匹配,则始终输入type参数以使其匹配.因此,如果没有指定类型,您可能无法得到您所期望的 - 也就是说,给定

class D[T](val x:T, val y:T);
Run Code Online (Sandbox Code Playgroud)

这将给你一个类型错误(找到Int,期望String)

var zz = new D[String]("Hi1", 1) // type error
Run Code Online (Sandbox Code Playgroud)

虽然这很好用:

var z = new D("Hi1", 1)
== D{def x: Any; def y: Any}
Run Code Online (Sandbox Code Playgroud)

因为类型参数T被推断为两者中最不常见的超类型 - 任何.


功能定义:

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

{}如果函数是单个语句,则可以删除函数体,但仅当语句返回值(您需要=符号)时,即,

def returnAString = "Hi!"
Run Code Online (Sandbox Code Playgroud)

但这不起作用:

def returnAString "Hi!" // Compile error - '=' expected but string literal found."
Run Code Online (Sandbox Code Playgroud)

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

() 如果函数不带任何参数,则可以删除,即

def endOfString {
  return "myDog".substring(2,1)
}
Run Code Online (Sandbox Code Playgroud)

按照惯例,它被保留用于没有副作用的方法 - 稍后会更多.

()按名称参数定义传递时,实际上并没有丢弃,但它实际上是一个语义上不同的表示法,即

def myOp(passByNameString: => String)
Run Code Online (Sandbox Code Playgroud)

表示myOp采用pass-by-name参数,这会产生一个String(也就是说,它可以是一个返回字符串的代码块),而不是函数参数,

def myOp(functionParam: () => String)
Run Code Online (Sandbox Code Playgroud)

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

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

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

def myOp2(passByNameString:(Int) => String) { .. } // - You can drop the ()
def myOp2(passByNameString:Int => String) { .. }
Run Code Online (Sandbox Code Playgroud)

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

def myOp2(passByNameString:(Int, String) => String) { .. }
Run Code Online (Sandbox Code Playgroud)

声明:

.可以删除使用运算符表示法,该表示法只能用于中缀运算符(带参数的方法的运算符).有关更多信息,请参阅Daniel的答案.

  • . 也可以删除postfix函数列表尾

  • () 可以删除后缀运算符list.tail

  • () 不能与定义为的方法一起使用:

    def aMethod = "hi!" // Missing () on method definition
    aMethod // Works
    aMethod() // Compile error when calling method
    
    Run Code Online (Sandbox Code Playgroud)

因为这种符号是按照惯例为没有副作用的方法保留的,比如List#tail(也就是说,没有副作用的函数的调用意味着函数没有可观察到的效果,除了它的返回值).

  • () 传入单个参数时,可以删除运算符表示法

  • () 可能需要使用不在语句末尾的后缀运算符

  • () 可能需要指定嵌套语句,匿名函数的结尾或带有多个参数的运算符

当调用带函数的函数时,不能省略内部函数定义中的(),例如:

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

调用带有by-name参数的函数时,不能将参数指定为无参数的匿名函数.例如,给定:

def myOp2(passByNameString:Int => String) {
  println(passByNameString)
}
Run Code Online (Sandbox Code Playgroud)

您必须将其称为:

myOp("myop3")
Run Code Online (Sandbox Code Playgroud)

要么

myOp({
  val source = sourceProvider.source
  val p = myObject.findNameFromSource(source)
  p
})
Run Code Online (Sandbox Code Playgroud)

但不是:

myOp(() => "myop3") // Doesn't work
Run Code Online (Sandbox Code Playgroud)

IMO,过度使用丢弃的返回类型可能对重用代码有害.只需查看规范,就可以了解由于代码中缺少显式信息而导致可读性降低的好例子.实际计算出变量类型的间接层次数可以是坚果.希望更好的工具可以避免这个问题,并保持我们的代码简洁.

(OK,在寻求编制一个更完整,简洁的答案(如果我错过了什么,或者得到一些错误/不精确请评论),我已经加入到答案的开始.请注意,这是不是一种语言规范,所以我不是想让它在学术上完全正确 - 更像是参考卡.)

  • 我在哭.这是什么. (8认同)
  • @Profpatsch 这就是所谓的过度设计 (2认同)

Ant*_*bbs 12

一系列报价,可以深入了解各种情况......

就个人而言,我认为规范中还有更多内容.我肯定一定有,我只是不寻找合适的词......

然而,有几个来源,我已经收集了它们,但没有真正完整/全面/可理解的/向我解释上述问题......:

"如果一个方法体有一个以上的表达式,你必须用花括号{...}包围它.如果方法体只有一个表达式,你可以省略括号."

Scala编程的第2章"Type Less,Do More":

"上层方法的主体来自等号'='.为什么等号?为什么不只是花括号{...},就像在Java中一样?因为分号,函数返回类型,方法参数列表,甚至花括号有时省略,使用等号可以防止几种可能的解析歧义.使用等号也提醒我们,甚至函数都是Scala中的值,这与Scala对函数式编程的支持是一致的,在第8章"函数式编程"中有更详细的描述.斯卡拉".

第1章"0至60:介绍斯卡拉"的编程斯卡拉:

"没有参数的函数可以在没有括号的情况下声明,在这种情况下,它必须在没有括号的情况下被调用.这提供了对统一访问原则的支持,这样调用者不知道符号是变量还是函数没有参数.

如果函数体返回一个值(即返回类型不是Unit),则函数体前面带有"=",但当类型为Unit时,返回类型和"="可以省略(即它看起来像一个过程而不是一个功能).

身体周围的大括号不是必需的(如果身体是单个表情); 更确切地说,函数体只是一个表达式,任何包含多个部分的表达式都必须用括号括起来(一个部分的表达式可以选择用括号括起来)."

"可以在没有点和圆括号的情况下调用具有零个或一个参数的函数.但是任何表达式都可以在其周围加上括号,因此您可以省略该点并仍然使用括号.

由于您可以在任何可以使用括号的地方使用大括号,因此您可以省略点并放入大括号,其中可以包含多个语句.

没有参数的函数可以在没有括号的情况下调用.例如,String上的length()函数可以调用为"abc".length而不是"abc".length().如果函数是没有括号定义的Scala函数,则必须在没有括号的情况下调用该函数.

按照惯例,使用括号调用没有带副作用的参数的函数,例如println; 那些没有副作用的人被称为没有括号."

来自博客文章Scala Syntax Primer:

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

例4.6.3这是一个名为write的过程的声明和定义:

trait Writer {
    def write(str: String)
}
object Terminal extends Writer {
    def write(str: String) { System.out.println(str) }
}
Run Code Online (Sandbox Code Playgroud)

以上代码隐式完成以下代码:

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

从语言规范:

"对于只采用单个参数的方法,Scala允许开发人员用空格替换.并省略括号,启用插入运算符示例中显示的运算符语法.此语法用于Scala API中的其他位置,例如作为构建Range实例:

val firstTen:Range = 0 to 9
Run Code Online (Sandbox Code Playgroud)

在这里,(Int)是一个在类中声明的vanilla方法(这里实际上有一些隐式类型转换,但是你得到漂移)."

来自Scala for Java Refugees第6部分:克服Java:

"现在,当你尝试"m 0"时,Scala将其作为一元算子丢弃,理由是它不是有效的(〜,!, - 和+).它发现"m"是一个有效的对象 - 它是一个函数,而不是一个方法,所有函数都是对象.

由于"0"不是有效的Scala标识符,因此它既不能是中缀也不能是后缀运算符.因此,Scala抱怨它预期";" - 它将两个(几乎)有效的表达式分开:"m"和"0".如果你插入它,那么它会抱怨m需要一个参数,或者,如果没有,那么"_"将它变成一个部分应用的函数."

"我相信只有当您在左侧有一个显式对象时,运算符语法风格才有效.语法旨在让您以自然的方式表达"操作数运算符操作数"样式操作."

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

但令我困惑的是这句话:

"需要有一个对象来接收方法调用.例如,你不能做"println"Hello World!""因为println需要一个对象接收者.你可以做"Console println"Hello World!""满足需要."

因为据我所看到的,一个对象接收呼叫...