2

我的问题之一是 -如果我们依赖隐式返回,它很容易出错

例如。

def foo(bar: Int): Int =
{
    if (f1(bar)) 0
    if (f2(bar)) 1 
    else -1
}

有时我们只是忘记了else声明,为了解决这个问题,大括号被强制执行,例如,.eg

def foo(bar: Int): Int =
{
    if (f1(bar)) {
        0
    } else if (f2(bar)) {
        1
    } else {
        -1
    }
}

但是新样式太verbose恕我直言,有什么办法解决吗?

4

5 回答 5

9

实际上,我从来没有遇到过这个问题,而且我犯了很多愚蠢的错误。我怀疑你完全有能力学会不跳过 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)

现在唯一的技巧是你必须:paste在 REPL 中使用来编写一个多行语句,并将延续放在上一行的末尾:

If (f1(bar)) Then 0 Or
(f2(bar)) Then 1 Else
-1

您还可以Then { 0在一行上使用并在下一行重新开始} Or,或者用括号/大括号和点编写所有内容(这是 REPL 友好的):

If (f1(bar)) .Then (0) .
Or (f2(bar)) .Then (1) .
Else (-1)

无论如何,所有这些技巧都很好地说明了如何使用 Scala 构建复杂的 DSL,但并不是解决问题的最佳方法。你应该学会对 if/else 语句更加小心;否则,人们会对你为什么不以标准方式做事感到困惑。(如果它是性能关键的,它可能会很慢——match非常好,但在If高性能循环中魔法并不好。)

于 2012-09-22T07:12:54.837 回答
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))
}

IE,

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 没有发出像臭名昭著的有用的东西,“警告:纯表达式在语句位置不做任何事情”。

于 2012-09-22T06:53:59.820 回答
0

目前还不清楚你在问什么。如果你想让 scala 知道你的意思是if或者else if你什么时候输入了错误的东西......那么你可能不走运。您必须输入正确的命令,无论您是否遇到编译时错误,添加或删除大括号都不会改变。

如果您只想知道如何编写不带括号的 if-elseif-else,那么:

if (f1(bar)) 0
else if (f2(bar)) 1 
else -1
于 2012-09-22T05:43:32.943 回答
0

如果您有许多语句,则需要大括号。在这个特定的示例中,您有一个表达式,您可以完全删除大括号,这会使错误更加明显并且不那么冗长。此外,如果您使用 IDE,您可以重新格式化代码,这有助于指出问题。

这是一个没有任何大括号的格式化示例。

  • foo是正确的版本。
  • foo2是你有时我们只是忘记了一些东西。请参阅语法错误。
  • foo3也有else被遗忘的,但因为代码在一个大括号块中,编译器认为该if (f1(bar)) 0语句是一个有效的孤立语句。

在此处输入图像描述

foo2如果我在截屏之前重新格式化了代码,那么 if/else 不匹配会更加明显。

于 2012-09-22T07:27:39.733 回答
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直接在彼此下方对齐的关键字。换句话说,当你看到两个ifs 相互重叠时,你一定是搞错了。

(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 中试过这个(因为它有很多智能警告),但它没有给出警告。我认为这是一个很好的情况,演示文稿编译器可以立即发现奇怪的分支,因为它们要么是未使用的文字(如您的情况),要么是对似乎没有副作用的方法的调用(返回类型不是Unit并且根据样式指南方法没有空括号)。

于 2012-09-22T07:32:22.067 回答