什么时候可以省略括号,点,大括号,=(函数)等等的准确规则?
什么时候可以省略(省略)圆括号,点,大括号,=(函数)等等的准确规则?
例如,(service.findAllPresentations.get.first.votes.size)必须等于(2)。
-
service
是我的对象 - def findAllPresentations:Option [List [Presentation]]
-
votes
返回列表[投票] - 必须和既是规范的function
为什么我不能去:
(service findAllPresentations get first votes size) must be equalTo(2)
?
编译器错误是:
“types选项[List [com.sharca.Presentation]]的RestServicesSpecTest.this.service.findAllPresentations不带参数”
为什么它认为我想传递一个参数? 为什么每个方法调用都必须使用点?
为什么必须(service.findAllPresentations get first votes size)
等于(2)导致:
“找不到:价值第一”
然而, (service.findAllPresentations.get.first.votes.size)
的“必须等于2”必须等于2,也就是说,方法链接工作正常吗? – 对象链式链条参数。
我已经浏览了Scala书籍和网站,找不到一个全面的解释。
实际上,正如Rob H在Stack Overflow中解释的那样, 我可以在Scala中忽略哪些字符? ,唯一有效的用例是省略“。” 是用于“操作数运算符”风格的操作,而不是用于方法链接?
你好像绊倒了答案。 无论如何,我会尽力说清楚。
当使用前缀,中缀和后缀符号时,可以省略点 – 所谓的操作符 。 在使用操作符表示法时,只有在传递给方法的参数less于两个的情况下,才可以省略括号。
现在,运算符符号是方法调用的符号,这意味着它不能在没有被调用的对象的情况下使用。
我将简要地详细介绍这些符号。
字首:
只~
!
, +
和-
可以用在前缀表示法中。 这是您在编写时使用的标记!flag
或val liability = -debt
。
中缀:
这是方法出现在对象和它的参数之间的符号。 算术运算符都适合在这里。
后缀(也是后缀):
当该方法跟随一个对象并且不接收任何参数时使用该符号。 例如,你可以写list tail
,这是后缀表示法。
只要没有咖啡的方法,你可以毫无问题地连锁中缀记法。 例如,我喜欢使用以下样式:
(list filter (...) map (...) mkString ", " )
这是一样的事情:
list filter (...) map (...) mkString ", "
现在,为什么我在这里使用圆括号,如果filter和map只有一个参数? 这是因为我将匿名函数传递给他们。 我不能将匿名函数定义和中缀样式混合使用,因为我需要一个匿名函数结束的边界。 此外,匿名函数的参数定义可能被解释为中缀方法的最后一个参数。
您可以使用多个参数的中缀:
string substring (start, end) map (_ toInt) mkString ("<", ", ", ">")
咖喱function很难用中缀表示法。 折叠function就是一个明显的例子:
(0 /: list) ((cnt, string) => cnt + string.size) (list foldLeft 0) ((cnt, string) => cnt + string.size)
您需要在中缀调用之外使用括号。 我不确定在这里玩的确切规则。
现在,我们来讨论一下postfix。 Postfix可能很难使用,因为除了expression式的结尾,它永远不能被使用 。 例如,您不能执行以下操作:
list tail map (...)
因为尾部不出现在expression式的末尾。 你也不能这样做:
list tail length
您可以使用圆括号来标记expression式的结尾:
(list tail) map (...) (list tail) length
请注意,由于可能不安全,因此不build议使用后缀表示法。
我希望这清除了所有的疑虑。 如果没有,只要放下评论,我会看看我能做些什么来改善它。
类定义:
val
或var
可以从类参数中省略,这会使参数变成private。
添加var或val会导致它公开(即,生成方法访问器和增变器)。
{}
可以省略,如果类没有主体,也就是说,
class EmptyClass
类实例化:
通用参数如果可以由编译器推断,则可以省略。 但是请注意,如果您的types不匹配,则始终传递types参数以使其匹配。 所以没有指定types,你可能得不到你所期望的 – 就是说
class D[T](val x:T, val y:T);
这会给你一个types错误(INT发现,预期string)
var zz = new D[String]("Hi1", 1) // type error
而这工作正常:
var z = new D("Hi1", 1) == D{def x: Any; def y: Any}
因为types参数T被推断为两者中最不常见的超types – Any。
函数定义:
=
可以被丢弃,如果函数返回单位(没有)。
如果函数是一个单独的语句,函数体可以被删除,但是只有在语句返回一个值(你需要=
符号)的时候,也就是说,
def returnAString = "Hi!"
但是这不起作用:
def returnAString "Hi!" // Compile error - '=' expected but string literal found."
如果可以推断函数的返回types,则可以省略(recursion方法必须指定返回types)。
()
可以被删除,如果该函数没有任何参数,也就是说,
def endOfString { return "myDog".substring(2,1) }
按惯例是保留给没有副作用的方法 – 稍后再说。
()
在定义一个按名称参数传递的时候实际上并没有被删除,但是它实际上是一个在语义上相当不同的记法,也就是说,
def myOp(passByNameString: => String)
说myOp采取传递名称参数,这将导致一个string(也就是说,它可以是一个代码块返回一个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) { .. }
声明:
.
可以使用运算符表示法, 只能用于中缀运算符(带有参数的方法的运算符)。 请参阅但以理的回答了解更多信息。
-
.
也可以针对postfix函数列表尾部进行删除 -
()
可以删除后缀运算符list.tail -
()
不能用于定义如下的方法:def aMethod = "hi!" // Missing () on method definition aMethod // Works aMethod() // Compile error when calling method
因为这个表示法对于没有副作用的方法(比如List#tail)是惯例保留的(也就是说,调用没有副作用的函数意味着函数除了返回值外没有可观察的效果)。
-
()
在传入一个参数时可以放弃操作符 -
()
可能需要使用不在语句结尾的postfix操作符 -
()
可能需要指定嵌套的语句,匿名函数的结束或对于多个参数的操作符
当调用一个带函数的函数时,不能从内部函数定义中省略(),例如:
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)过度使用返回types可能会对代码重用造成危害。 只要看一下规范,就可以看到由于缺less代码中的明确信息而导致可读性降低的一个很好的例子。 实际弄清楚variablestypes的间接级别的数量可以是坚果。 希望更好的工具可以避免这个问题,并保持我们的代码简洁。
(好的,为了编写一个更加完整,简洁的答案(如果我错过了什么,或者有什么错误/不准确的请评论),我已经添加到了答案的开头。请注意,这不是一门语言规范,所以我并不是试图使它在学术上是正确的 – 更像是参考卡。)
一系列的报价给出了各种条件的洞察力…
就个人而言,我认为在规范中会有更多。 我确定一定有,我只是没有寻找正确的话…
然而,有几个来源,我已经把它们收集在一起,但没有真正完整/全面/可以理解的/这解释了上述问题对我来说:
“如果方法体有多个expression式,则必须用花括号{…}来包围它,如果方法体只有一个expression式,则可以省略大括号。
从编程Scala的第2章“less键入,多做”
“上面的方法的主体是在等号'='之后,为什么是等号呢?为什么不像Java那样花括号{…}呢?因为分号,函数返回types,方法参数列表,甚至花括号有时会被省略,使用等号可以防止出现几种可能的parsing歧义,使用等号也会提醒我们,甚至函数也是Scala中的值,这与Scala对函数式编程的支持是一致的,在第8章“函数式编程”斯卡拉“。
从Scala 编程的第1章“从零到六十:Scala介绍”
“没有参数的函数可以在没有括号的情况下声明,在这种情况下,它必须被调用,而不用括号,这样就支持统一访问原则,这样调用者不知道符号是variables还是函数没有参数。
如果返回一个值(即返回types不是Unit),那么函数体前面加“=”,但是当types是Unit时,返回types和“=”可以省略(即看起来像一个过程而不是一个函数)。
身体周围的括号不是必需的(如果身体是一个单一的expression); 更确切地说,一个函数的主体只是一个expression式,任何具有多个部分的expression式都必须用大括号括起来(一个expression式可以有一个大括号)。
“带有零个或一个参数的函数可以不带圆点和圆括号,但任何expression式都可以在圆括号周围,所以可以省略圆点并仍然使用圆括号。
而且由于你可以在任何地方使用括号,所以你可以省略小圆点并放置大括号,可以包含多个语句。
没有参数的函数可以在没有括号的情况下被调用。 例如,String上的length()函数可以调用为“abc”.length而不是“abc”.length()。 如果函数是一个没有括号的Scala函数,那么函数必须在没有括号的情况下被调用。
按照惯例,没有带副作用的参数的函数,例如println,用圆括号来调用; 没有副作用的人被称为没有括号“。
来自博客postScala 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) } }
上面的代码隐含地完成了以下代码:
trait Writer { def write(str: String): Unit } object Terminal extends Writer { def write(str: String): Unit = { System.out.println(str) } }"
从语言规范:
“只使用一个参数的方法,Scala允许开发人员用一个空格replace,并省略括号,从而使我们的插入操作符示例中显示的操作符语法成为可能,这个语法在Scala API的其他地方使用,例如作为构buildRange实例:
val firstTen:Range = 0 to 9
在这里,(Int)是一个在类中声明的vanilla方法(实际上这里有一些更隐式的types转换,但你得到了漂移)。
从Java的难民斯卡拉第6部分:越过Java :
“现在,当你尝试”m 0“时,Scala放弃它作为一元运算符,理由是它不是一个有效的(〜,!, – 和+),它发现”m“是一个有效的对象 – 它是一个function,而不是一个方法,所有的function都是对象。
由于“0”不是有效的Scala标识符,它不能既不是中缀也不是后缀运算符。 因此,斯卡拉抱怨说,“预计” – 这将分隔两个(几乎)有效的expression式:“m”和“0”。 如果你插入了它,那么它会抱怨m需要一个参数,否则,一个“_”把它变成一个部分应用的函数。
“我相信操作符的语法风格只有在左边有一个明确的对象时才起作用,语法的目的是让你以一种自然的方式expression”操作数操作符“风格的操作。
我可以在Scala中忽略哪些字符?
但是,这也引起了我的困惑:
“需要有一个对象来接收一个方法调用,例如,你不能做”println“Hello World!”“,因为println需要一个对象接收者。 你可以做“Console println”Hello World!“”来满足需求。“
因为据我所知,有一个对象接受电话…
事实上,二读时,也许这是关键:
使用只有一个参数的方法,Scala允许开发人员replace。 与一个空格,省略括号
正如博客文章所述: http : //www.codecommit.com/blog/scala/scala-for-java-refugees-part-6 。
所以也许这实际上是一个非常严格的“语法糖”, 它只在有效地调用一个方法的地方,在一个只有一个参数的对象上有效。 例如
1 + 2 1.+(2)
而没有别的。
这将解释我在这个问题中的例子。
但正如我所说,如果有人能指出这是明确的语言规范,将不胜感激。
好的,一些好人(来自#scala的paulp_)指出了语言规范中的这个信息:
6.12.3:运算符的优先级和关联性决定了expression式部分的分组,如下所示。
- 如果在expression式中有多个中缀操作,那么具有较高优先级的运算符比具有较低优先级的运算符绑定更紧密。
- 如果有连续的中缀操作e0 op1 e1 op2。 。 .opn en与操作符op1,。 。 。 ,优先级相同,那么所有这些运算符必须具有相同的关联性。 如果所有操作符都是左关联的,则序列被解释为(…(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)。 这个expression式然后被解释为e.op(e1,…,en)。
左关联二元运算e1 op e2被解释为e1.op(e2)。 如果op是rightassociative,则相同的操作被解释为{val x = e1; e2.op(x)},其中x是一个新的名字。
嗯 – 对我来说,它不会与我所看到的或我只是不明白;)
没有任何 您可能会收到有关该function是否有副作用的build议。 这是假的。 修正是不要在Scala允许的合理范围内使用副作用。 如果不能,那么所有的投注都是closures的。 所有投注。 使用括号是集合“全部”的一个元素,是多余的。 一旦所有投注都closures,它不会提供任何价值。
这个build议本质上是一个失败效果系统的尝试(不要混淆:不如其他效果系统)。
尽量不要副作用。 之后,接受所有的赌注。 隐藏在效果系统的事实上的句法符号后面,可以做,只会造成伤害。
我发现更容易遵循这个经验法则:在expression式中,空格在方法和参数之间交替。 在你的例子中, (service.findAllPresentations.get.first.votes.size) must be equalTo(2)
parsing为(service.findAllPresentations.get.first.votes.size).must(be)(equalTo(2))
。 请注意,围绕2的括号具有比空格更高的关联性。 点也有较高的关联性,所以(service.findAllPresentations.get.first.votes.size) must be.equalTo(2)
将parsing为(service.findAllPresentations.get.first.votes.size).must(be.equalTo(2))
。
service findAllPresentations get first votes size must be equalTo 2
parsing为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
。