我的一个问题是 - 如果我们依赖隐式退货,则容易出错,
例如
def foo(bar: Int): Int =
{
if (f1(bar)) 0
if (f2(bar)) 1
else -1
}
有时我们只是忘记了else
语句,为了解决这个问题,强制执行大括号,.e.g
def foo(bar: Int): Int =
{
if (f1(bar)) {
0
} else if (f2(bar)) {
1
} else {
-1
}
}
但新风格太verbose
恕我直言,还有什么方法可以解决?
答案 0 :(得分:8)
实际上,我从来没有遇到过这个问题,而且我犯了很多愚蠢的错误。我怀疑你完全有能力学习不跳过else语句。 (请注意,大括号对任何内容都没有帮助;您仍然可以跳过中间的else
。)
如果您真的发现这是一个问题,可以滥用match
声明:
true match {
case _ if f1(bar) => 0
case _ if f2(bar) => 1
case _ => -1
}
这会阻止你犯任何错误。或者你可以自己写一个实用方法:
trait Conditional[+A] {
def apply[B >: A](p: => Boolean, b: => B): Conditional[B]
def or[B >: A](default: => B): B
}
class FoundIt[+A](it: A) extends Conditional[A] {
def apply[B >: A](p: => Boolean, b: => B) = this
def or[B >: A](default: => B) = it
}
class NothingYet[+A] extends Conditional[A] {
def apply[B >: A](p: => Boolean, b: => B) = {
if (p) new FoundIt(b) else this
}
def or[B >: A](default: => B) = default
}
def ifelse[A](p: => Boolean, a: => A) = (new NothingYet[A]).apply(p,a)
ifelse( f1(bar), 0 )( f2(bar), 1 ) or -1
对于长表达式来说这有点乱,所以你也可以(如果你想让它在那里工作的话,使用:paste
将它固定在一个大块中的REPL中):
trait Predicate[+A] {
def Or(p: => Boolean): Loadable[A]
def Else[B >: A](default: => B): B
}
trait Loadable[+A] {
def Then[B >: A](b: => B): Predicate[B]
}
object NotYetTrue extends Predicate[Nothing] {
def Or(p: => Boolean) = if (p) ActuallyLoad else SkipLoading
def Else[B >: Nothing](default: => B) = default
}
object SkipLoading extends Loadable[Nothing] {
def Then[B >: Nothing](b: => B) = NotYetTrue
}
object ActuallyLoad extends Loadable[Nothing] {
def Then[B >: Nothing](b: => B) = new Loaded[B](b)
}
class Loaded[+A](a: A) extends Predicate[A] with Loadable[A] {
def Or(p: => Boolean) = this
def Else[B >: A](default: => B) = a
def Then[B >: A](b: => B) = this
}
def If(p: => Boolean): Loadable[Nothing] = NotYetTrue.Or(p)
现在唯一的技巧是你必须在REPL中使用:paste
来编写一个多行语句,并将延续句放在上一行的末尾:
If (f1(bar)) Then 0 Or
(f2(bar)) Then 1 Else
-1
你也可以在一行上使用Then { 0
,然后在下一行使用} Or
再次启动,或者用parens / braces和dots编写所有内容(这是REPL友好的):
If (f1(bar)) .Then (0) .
Or (f2(bar)) .Then (1) .
Else (-1)
无论如何,所有这些技巧都很好地说明了如何使用Scala构建复杂的DSL,但实际上并不是解决问题的最佳方法。你应该学会对if / else语句稍微小心一点;否则,人们会为你为什么不按标准方式做事而感到困惑。 (如果它的性能至关重要,可能会很慢 - match
非常好,但If
魔法在高性能循环中效果不佳。)
答案 1 :(得分:1)
我找到你了。没有人说,一个函数不止一个条件太多了!
折叠一些要应用的功能。也可以表达为collectFirst。
object Ifless extends App {
def f1(i: Int) = i == 1
def f2(i: Int) = i == 2
def p1(i: Int) = if (f1(i)) Some(0) else None
def p2(i: Int) = if (f2(i)) Some(1) else None
def default(i: Int) = Some(-1) //whatever
def f(i: Int): Int = {
val ps = List(p1 _, p2 _, default _)
ps.foldLeft(None: Option[Int])((r,v) => r match {
case None => v(i)
case x => x
}) getOrElse -1 //whatever
}
println(f(1))
println(f(2))
println(f(3))
}
即,
object Ifless2 extends App {
// imagine this is not a switchable match
def fs: List[PartialFunction[Int, Int]] = List (
{ case 1 => 0 },
{ case 2 => 1 },
{ case _ => -1 }
)
def f(i: Int): Int = {
fs.collectFirst { case pf if pf.isDefinedAt(i) => pf(i) } getOrElse -1
}
println(f(1))
println(f(2))
println(f(3))
}
此外,它太糟糕了scalac不会发出像臭名昭着的有用的东西,“警告:纯粹的表达在声明位置没有任何作用。”
答案 2 :(得分:0)
目前还不清楚你在问什么。如果你想让scala在你输入错误的东西时知道你是if
还是else if
......那么你可能运气不好。您必须键入正确的命令,无论是否出现编译时错误,添加或删除大括号都不会改变。
如果您只是想知道如何在没有括号的情况下编写if-elseif-else,那么:
if (f1(bar)) 0
else if (f2(bar)) 1
else -1
答案 3 :(得分:0)
如果你有很多陈述,那么就需要大括号。在这个特定的例子中,你只有一个表达式,你可以删除括号,它会使错误更明显和它会更简洁。此外,如果您使用IDE,您可以重新格式化代码,它有助于指出问题。
以下是没有任何大括号的格式示例。
foo
是正确的版本。foo2
是您的有时我们只是忘了。 请参阅语法错误。 foo3
也遗忘了else
,但由于代码位于大括号块中,编译器会将if (f1(bar)) 0
语句视为有效的隔离语句。
如果我在屏幕截图之前重新格式化了代码,那么在foo2
中if / else不匹配会更明显。
答案 4 :(得分:0)
以下指南可能会有所帮助:
(1)没有if
分支的else
语句必须具有副作用(因为表达式的结果类型为Unit
);因此,为避免上述错误,请始终在新行上使用大括号和正文,或在该语句下方强制使用空行。而不是......
if (f1(bar)) addToParent() // addToParent has side-effect
if (f2(bar)) 1
else -1
......使用......
if (f1(bar)) addToParent()
if (f2(bar)) 1
else -1
...或...
if (f1(bar)) {
addToParent()
}
if (f2(bar)) 1
else -1
结果是,您不应该看到两个if
个关键字直接排列在彼此之下。换句话说,当你看到彼此之上有两个if
时,你一定是犯了错误。
(2)如果您有多个支票,请使用显式括号,或者如果它们相当短(如您的示例中所示),请将它们放在一行:
if (f1(bar)) 0 else if (f2(bar)) 1 else -1
因为这不会编译:
if (f1(bar)) 0 if (f2(bar)) 1 else -1
<console>:3: error: ';' expected but 'if' found.
if (f1(bar)) 0 if (f2(bar)) 1 else -1
^
如果Scala是一种纯函数式语言或者可以识别副作用,编译器当然可以警告你。 IDE也可以帮助你。我在IntelliJ IDEA中试过这个(因为它有很多智能警告),但它没有发出警告。我认为这是一个很好的例子,演示编译器可以立即发现奇怪的分支,因为它们是未使用的文字(如你的情况),或者调用似乎没有副作用的方法(返回类型不是{{1并且根据样式指南方法没有空括号)。