Scala if(Option.nonEmpty)与Option.foreach

Scala if(Option.nonEmpty)与Option.foreach,scala,Scala,如果设置了选项的值,我想执行一些逻辑 来自java背景,我使用: if (opt.nonEmpty) { //something } 再深入到scala,我可以这样写: opt.foreach(o => { //something }) 哪一个更好?“foreach”一词听起来更“惯用”,Java更少,但可读性较差——“foreach”应用于单个值听起来很奇怪。foreach如果您认为选项类似于列表,但最多只有一个元素,那么它确实有意义 一种更简洁的风格,IMO,是使用一个

如果设置了选项的值,我想执行一些逻辑

来自java背景,我使用:

if (opt.nonEmpty) {
   //something
}
再深入到scala,我可以这样写:

opt.foreach(o => {
  //something 
})

哪一个更好?“foreach”一词听起来更“惯用”,Java更少,但可读性较差——“foreach”应用于单个值听起来很奇怪。

foreach
如果您认为
选项
类似于
列表,但最多只有一个元素,那么它确实有意义

一种更简洁的风格,IMO,是使用一个字母来理解:

for (o <- opt) {
  something(o)
}

<代码> >(o> p> <代码>前缀 >如果您认为<代码>选项>代码>可以是最多包含单个值的列表。这也会导致对<代码>选项> <代码> >

的许多其他方法的正确直觉。 在这种情况下,我至少可以想到一个重要的原因,您可能更喜欢使用
foreach
:它删除了可能的运行时错误。使用
非空的
方法,您将不得不在某一点上执行
get
*,如果您意外地忘记检查一次是否为空,这可能会使您的程序崩溃

如果您完全清除了
get
以避免此类错误,那么副作用是
nonEmpty
的使用也会减少!您将开始享受
foreach
的乐趣,并让编译器处理
选项恰好为空时应该发生的事情

你会看到这个概念在其他地方突然出现,你永远不会这么做

if (age.nonEmpty)
    Some(age.get >= 18)
else
    None
你宁愿看到的是

age.map(_ >= 18)
原则是,您希望避免编写处理故障情况的代码–您希望将这一负担卸给编译器。许多人会告诉您,无论您多么小心地进行预检查,都不应该使用
get
。这样做会使事情变得更简单



*除非您实际上只是想知道
选项是否包含值,而不是真正关心值,否则在这种情况下
非空
就可以了。在这种情况下,它就像是一种
toBoolean

,您的示例不完整,并且没有使用最小语法。只需比较这两个版本:

if (opt.nonEmpty) {
  val o = opt.get
  // ...
}

// vs

opt foreach {
  o => // ...
}

在这两个版本中,
if
解决方案的语法开销都比较大,但我同意,如果将
选项
上的
foreach
仅视为一个可选值,则可能会造成混淆


相反,
foreach
描述了你想要做一些副作用,如果你认为
选项
是一个单子,而
foreach
只是一个转换它的方法,这是很有意义的。使用
foreach
还有一个巨大的优势,那就是它使重构更容易——你只需改变它的类型到
列表
或任何其他monad,您将不会得到任何编译器错误(由于Scalas great collections library,您不必仅使用在monad上工作的操作,在许多类型上定义了许多方法)。

我在其他答案中没有找到的东西:

  • 当使用
    if
    时,我更喜欢
    if(opt-isDefined)
    而不是
    if(opt-nonEmpty)
    ,因为前者不像集合,并且更清楚地表明我们正在处理一个选项,但这可能是一个品味问题

  • if
    foreach
    是不同的,因为
    if
    是一个返回值的表达式,而
    foreach
    返回
    Unit
    。因此在某种程度上使用
    foreach
    if
    更像java,因为java有一个foreach循环,而java的
    if
    不是表达式。使用a表示理解更像scala

  • 您还可以使用模式匹配(但这也不太惯用)

  • 您可以使用
    fold
    方法,该方法具有两个函数,因此您可以在
    Some
    情况下计算一个表达式,在
    None
    情况下计算另一个表达式。由于类型推断的工作方式,您可能需要显式指定预期的类型(如图所示)因此,在我看来,有时使用模式匹配或
    val result=if(opt isDefined)表达式1 else表达式2
    可能更为清晰


  • 如果您不需要返回值,并且确实不需要处理未定义的情况,那么可以使用
    foreach

    我喜欢这种推理。我还想补充一点,使用foreach时,您永远不会在代码中引入
    get()
    ,即使正确测试
    非空()
    。如果无论选项是否为空,都需要执行某些操作,
    Some(x)
    None
    案例匹配。在您的第一个代码片段中,它不应该是
    Some(age.get>=18)
    是否也有一个
    选项作为结果类型?@berylium当然应该!我不知道这是怎么发生的。可能是因为太多Python了。“据我所知,没有一个选项方法可以在某些情况下对一个表达式求值,而在无情况下对另一个表达式求值”有:
    opt.fold(“None”)(v=>s“Some($v)”
    if (opt.nonEmpty)
      doSomething(opt.get)
    
    // vs
    
    opt foreach doSomething