【问题标题】:Named parameters vs _, dot notation vs infix operation, curly vs round brackets when using higher-order functions in Scala在 Scala 中使用高阶函数时,命名参数 vs _、点符号 vs 中缀操作、花括号 vs 圆括号
【发布时间】:2014-12-13 20:34:39
【问题描述】:

我很难理解何时可以或不能省略括号和/或句号,以及这与 _ 的相互作用。

我遇到的具体情况是

val x: X = ???
val xss: List[List[X]] = ???
xss map x :: _ //this doesn't compile
xss map _.::(x) //this is the same as the above (and thus doesn't compile)

上面两个好像和xss.map(_).::(x)一样

xss map (x :: _) //this works as expected
xss map {x :: _} //this does the same thing as the above

同时,以下也失败了:

xss.map xs => x :: xs //';' expected but '=>' found.
xss.map x :: _ //missing arguments for method map in class List; follow this method with `_' if you want to treat it as a partially applied function
//so when I try following the method with _, I get my favourite:
xss.map _ x :: _ //Cannot construct a collection of type That with elements of type B based on a collection of type List[List[Main.X]]
//as opposed to
xss map _ x :: _ //missing parameter type for expanded function ((x$1) => xss.map(x$1).x(($colon$colon: (() => <empty>))))

现在,我经常玩“切换符号直到它编译”,我认为这是一种次优的编程策略。这一切是如何运作的?

【问题讨论】:

标签: scala type-inference higher-order-functions syntactic-sugar


【解决方案1】:

首先我们需要区分xss.map(f)xss map f。根据Scala Documentation,任何采用单个参数的方法都可以用作中缀运算符。

其实map method in List就是其中一种方法。忽略完整签名和它继承自TraversableLike的事实,签名如下:

final def map[B](f: (A) ⇒ B): List[B]

所以它只有一个参数,即f,它是一个类型为A =&gt; B的函数。因此,如果您将function value 定义为

val mySize = (xs:List[Int]) => xs.size

你可以选择

xss.map(mySize)

xss map mySize

这是一个偏好问题,但根据Scala Style Guide,对于这种情况,后者是首选,除非它是复杂表达式的一部分,最好坚持使用点表示法。

请注意,如果您选择使用点符号,则始终需要用括号限定函数应用程序!这就是为什么以下都没有成功编译的原因。

xss.map xs => x :: xs // Won't compile
xss.map x :: _ // Won't compile
xss.map _ x :: _ // Won't compile

但大多数时候,您需要传递function literal(又名anonymous function)而不是传递函数值。在这种情况下,如果您使用点表示法,您需要类似xss.map(_.size) 的东西。但是如果你使用中缀表示法,那将是一个优先级问题。

例如

xss map x :: _ // Won't compile!

由于运算符优先级而不起作用。所以你需要使用括号来消除xss map (x :: _)编译器的歧义。

使用大括号代替方括号有一个非常明确和简单的规则。同样,任何只接受一个参数的函数都可以用花括号代替括号,用于中缀和点表示法。所以下面的语句会编译。

xss.map{x :: _}
xss map {x :: _}

为避免混淆,您可以从点表示法和参数的显式类型开始。稍后在编译后 - 并且可能为您的代码编写一些单元测试 - 您可以通过删除不必要的类型、使用中缀表示法以及在有意义的地方使用花括号而不是括号来开始重构代码。

为此,您可以参考Scala Style GuideMartin Odersky's talk in Scala Days 2013,它们是关于Scala 编码风格的。此外,您还可以随时向 IDE 寻求帮助,以将代码重构得更简洁。

【讨论】:

  • 很好的答案,谢谢。至于要求 Eclipse 插件重构我的代码——根据我的经验,结果很糟糕,尽管我对 intellij 的看法很好
  • 我和IntelliJ IDEA Scala Plugin 度过了愉快的时光。它会自动检测可能更简单的部分代码。例如,如果您写xss map (mySize),它会建议删除圆括号。即使您写xss map (mySize(_)),它也建议首先删除不必要的_,然后在删除它之后建议删除括号。它足够智能,可以检测到大多数常见问题和代码异味。
猜你喜欢
  • 2023-04-10
  • 2012-09-17
  • 1970-01-01
  • 1970-01-01
  • 2017-04-30
  • 2019-11-30
  • 2011-01-12
相关资源
最近更新 更多