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

时间:2009-07-25 07:53:38

标签: syntax scala

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

例如,

(service.findAllPresentations.get.first.votes.size) must be equalTo(2).
  • service是我的对象
  • def findAllPresentations: Option[List[Presentation]]
  • votes返回List[Vote]
  • 必须 都是规格的功能

为什么我不能去:

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

编译错误是:

  

“RestServicesSpecTest.this.service.findAllPresentations   类型   选项[列表[com.sharca.Presentation]   没有参数“

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

为什么(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? 中所解释的那样,这是唯一有效的用例,用于省略'。'是用于“操作数运算符操作数”样式操作,而不是用于方法链接?

6 个答案:

答案 0 :(得分:82)

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

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

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

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

<强>前缀:

前缀表示法中只能使用~!+-。这是您在撰写!flagval liability = -debt时使用的符号。

<强>缀

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

后缀(也是后缀):

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

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

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

这与以下内容相同:

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

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

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

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

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

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

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

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

 list tail map (...)

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

 list tail length

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

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

请注意,不建议使用后缀表示法,因为it may be unsafe

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

答案 1 :(得分:37)

类定义:

可以从类参数中省略

valvar,这会使参数变为私有。

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

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

{},即

class EmptyClass

类实例化:

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

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

这将为您提供类型错误(找到Int,期望字符串)

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

虽然这样可以正常工作:

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

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


函数定义:

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

=

如果函数是单个语句,则可以删除函数体的

{},但前提是该语句返回一个值(需要=符号),即

def returnAString = "Hi!"

但这不起作用:

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

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

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

(),即

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

按惯例保留用于没有副作用的方法 - 稍后会更多。

在定义 pass by name 参数时,

()本身并未实际丢弃,但它实际上是一种语义上不同的符号,即

def myOp(passByNameString: => String)

表示myOp采用pass-by-name参数,这会产生一个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) { .. }

陈述:

可以删除

.以使用运算符表示法,可用于中缀运算符(带参数的方法的运算符)。有关详细信息,请参阅Daniel's answer

  • .也可以删除后缀功能 list tail

  • 可以为后缀运算符删除
  • () 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

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

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

(好吧,为了编译一个更完整,更简洁的答案(如果我错过了什么,或者得到了错误/不准确请注释),我已经添加到答案的开头。请注意这不是一个语言规范,所以我不是想让它在学术上完全正确 - 更像是参考卡。)

答案 2 :(得分:12)

答案 3 :(得分:2)

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

  

使用只需一个的方法   参数,Scala允许开发人员   替换。有空格而省略   括号

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

所以这可能实际上是一个非常严格的“语法糖”,只有 才能有效地在一个对象上调用一个方法,它接受一个参数。 e.g。

1 + 2
1.+(2)

没有别的。

这可以解释我在问题中的例子。

但正如我所说,如果有人能指出确切地指出了这个语言规范的位置,那将非常感激。

好的,一些好人(来自#scala的paulp_)指出了语言规范中的这些信息:

  

6.12.3:   优先权和相关性   运营商确定分组   表达式的一部分如下。

     
      
  • 如果表达式中有多个中缀操作,那么   具有更高优先级的运算符绑定   比较低的运营商更紧密   优先级。
  •   
  • 如果有连续的中缀操作e0 op1 e1 op2。 。 .opn en   与运营商op1 ,. 。 。 ,opn   同样的优先权,然后所有这些   运营商必须具有相同的   关联。如果所有运营商都是   左关联,序列是   解释为(...(e0 op1 e1)op2   。 。 。)opn en。否则,如果全部   运营商是正确的,关联的   序列被解释为e0 op1(e1   op2(... .opn en)。 。 )。
  •   
  • 后缀运算符的优先级始终低于中缀运算符。例如。   e1 op1 e2 op2总是相当于   (e1 op1 e2)op2。
  •   
     

a的右手操作数   左关联运算符可以包含   括号中的几个参数   括号,例如e op(e1,...   ,EN)。这个表达式就是这样   解释为e.op(e1,...,en)。

     

左关联二进制运算e1   op e2被解释为e1.op(e2)。如果   op是正确的,也是一样的   操作被解释为{val   X = E1; e2.op(x)},其中x是新鲜的   名。

嗯 - 对我而言,它与我所看到的不相符,或者我只是不理解它;)

答案 4 :(得分:2)

没有。您可能会收到有关该功能是否有副作用的建议。这是假的。更正是在Scala允许的合理范围内不使用副作用。在某种程度上它不能,那么所有的赌注都是关闭的。 全部投注。使用括号是集合“all”的一个元素,并且是多余的。所有投注结束后,它不会提供任何价值。

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

尽量不要副作用。在那之后,接受所有赌注都已关闭。隐藏在效果系统的事实上的句法符号背后,只会造成伤害。

答案 5 :(得分:1)

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