【问题标题】:Simplifying Option[Boolean] expression in Scala简化 Scala 中的 Option[Boolean] 表达式
【发布时间】:2021-03-15 07:33:15
【问题描述】:

我有这样的代码:

optionBoolean.getOrElse(false) && otherOptionBoolean.getOrElse(false)

Scalastyle 告诉我它可以被简化。怎么样?

【问题讨论】:

  • 看看Optionexists函数
  • 看看this的问题。
  • 你可以将exists 用作otherOptionBoolean.exists(identity),但我认为它并不比getOrElse 简单
  • “可以简化”是什么意思?对我来说,这看起来是满足您要求的最简单的表达方式。是的,也许可以以更简洁和“智能”的方式编写它,但我看不出有必要或好处。 Scalastyle 并不总是正确的。
  • 我没什么意思,我只是想知道 Scalastyle 在这种情况下想告诉我什么 :)

标签: scala optional boolean-logic scala-option scalastyle


【解决方案1】:

您可以尝试以下方法:

Seq(optionBoolean, otherOptionBoolean).forall(_.contains(true))

在 Scala 2.13 中(在之前的版本中非常相似)forall 方法位于IterableOnce,其实现是:

def forall(p: A => Boolean): Boolean = {
  var res = true
  val it = iterator
  while (res && it.hasNext) res = p(it.next())
  res
}

因此一旦有一个值不满足条件,循环就会中断,其余的将不被测试。

代码在Scastie 运行。

【讨论】:

  • 这是所有答案中可读性最高的答案,但我仍然不会说它是我的解决方案的简化版本。这只是一种不同的方法。
  • @Ava,我认为它被简化了,因为我每个动作都执行一次(一个 forall 和一个 contains)。在你的方法中,如果你想添加更多的布尔值,你必须为它们重复getOrElse。在我的方法中,您只需将它们添加到Seq。换句话说,无论我有多少个布尔值,我都会执行 2 次操作,而您对每个布尔值执行 1 次。话虽如此,时间效率是一样的,但我认为我的更具可读性。
  • @TomerShetah 这段代码创建了一个全新的List,填充它(两个内存分配),然后迭代它。因此,就执行而言,这确实不是一个“简单”的解决方案。依次测试每个选项更简单、更有效。
  • @Tim 你所说的一切都是正确的。它创建一个列表,并对其进行迭代。但是一旦超出范围,这个列表就会被垃圾收集。我认为,如果您对程序进行内存基准测试,然后发现这是您的瓶颈,那么 Scala 可能不适合您。可能你应该用 C++ 编写类似的东西。
  • @TomerShetah 仅仅因为 Scala 的效率低于 C++ 并不意味着当有更快、更简单的替代方案时你应该放弃性能。 JIT 编译器可以很好地处理其他答案中更简单的代码,但会因这个答案的复杂性而挣扎。
【解决方案2】:

如果是 Boolean,那么自定义 dsl 可以让您使用 Option[Boolean] 吗?使用所有相同的运算符和相同的行为。

你可以这样使用:

object Booleans {
  def and(fb: Option[Boolean], other: => Option[Boolean]): Option[Boolean] =
    fb.flatMap(b => if (!b) Some(false) else other)

  def or(fb: Option[Boolean], other: => Option[Boolean]): Option[Boolean] =
    fb.flatMap(b => if (b) Some(true) else other)

  def negate(fb: Option[Boolean]): Option[Boolean] =
    fb.map(!_)

  object Implicits {

    implicit class OptionBooleanDecorator(val fb: Option[Boolean]) extends AnyVal {

      def &&(other: => Option[Boolean]): Option[Boolean] =
        and(fb, other)

      def ||(other: => Option[Boolean]): Option[Boolean] =
        or(fb, other)

      def unary_!(): Option[Boolean] = negate(fb)

    }
  }
}

在代码中的某处:

import Booleans.Implicits._

val b1 = Some(true)
val b2 = Some(true)

val b3 = b1 && b2
val b4 = b1 || b2
val b5 = !b1

您可以使其与任何其他容器一起使用。 这种扩展某种类型的 dsl 在 Scala 世界中很常见。

编辑: 至于最后一部分,即orElse 方法 - 这也可以放入 dsl 中,但在这种情况下,您会失去操作的可组合性。所以我让所有方法都返回一个Option

【讨论】:

  • 虽然看起来不错,但我认为提供逻辑运算符&& 并不是很好。如果不知道背后有隐含,那就令人困惑了。此外,它不适用于该问题。你还是要做(optionBoolean && otherOptionBoolean).getOrElse(false)
  • 这只是一个想法。您实际上并没有覆盖逻辑运算符。期权一开始就没有它们。您正在为盒装类型提供逻辑运算符。而且由于它们的工作方式与纯布尔值完全相同,我认为这不会引起太多混乱。至于最后一部分,orElse - 这也可以放在 dsl 中,但在这种情况下,您会失去操作的可组合性。
【解决方案3】:

这可能更清楚一点:

optionBoolean.contains(true) && otherOptionBoolean.contains(true)

【讨论】:

    【解决方案4】:

    只是为了扔另一个,不一定更好的答案,

    optionBoolean == Some(true) && otherOptionBoolean == Some(true)
    

    甚至

    (optionBoolean, otherOptionBoolean) == (Some(true), Some(true))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-10
      • 1970-01-01
      • 2013-06-15
      • 2021-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多