在Scala中实现ifTrue,ifFalse,ifSome,ifNone等,以避免if(…)和简
在
Scala中,我逐渐失去了以控制流方式思考的
Java / C习惯,并习惯于先获取我感兴趣的对象,然后通常应用匹配或映射之类的东西收集的()或foreach().我非常喜欢它,因为它现在感觉像是一种更自然,更直接的方式来构建我的代码.
渐渐地,我希望我能以相同的方式为条件编程;即,首先获得布尔值,然后匹配它以做各种事情.然而,一场全面的比赛对于这项任务来说似乎有点过分. 相比: obj.isSomethingValid match { case true => doX case false => doY } 与我接近Java的风格相比: if (obj.isSomethingValid) doX else doY 然后我想起了Smalltalk的ifTrue:和ifFalse:消息(及其变体).是不是可以在Scala中写这样的东西? obj.isSomethingValid ifTrue doX else doY 与变种: val v = obj.isSomethingValid ifTrue someVal else someOtherVal // with side effects obj.isSomethingValid ifFalse { numInvalid += 1 println("not valid") } 此外,这种风格是否可以用于简单的双状态类型,如Option?我知道使用Option的更惯用的方法是将其视为一个集合并在其上调用filter(),map(),exists(),但最后,我发现我想要执行一些doX,如果它是定义的,如果不是,则定义一些.就像是: val ok = resultOpt ifSome { result => println("Obtained: " + result) updateUIWith(result) // returns Boolean } else { numInvalid += 1 println("missing end result") false } 对我而言,这(仍然?)看起来比一场成熟的比赛更好. 我提供了一个我想出的基础实现;欢迎对这种风格/技术和/或更好的实施的一般评论! 解决方法
首先:我们可能无法重用其他,因为它是一个关键字,并且使用反引号强制它被视为标识符相当难看,所以我将使用其他方式.
这是一个实施尝试.首先,使用pimp-my-library模式将ifTrue和ifFalse添加到Boolean.它们在返回类型R上进行参数化并接受单个名称参数,如果实现了指定的条件,则应对其进行评估.但在这样做的时候,我们必须允许另外一个电话.因此,我们返回一个名为OTHER0的新对象(为什么0将在后面解释),它将可能的中间结果存储为Option [R].定义当前条件(ifTrue或ifFalse)是否已实现,否则为空. class BooleanWrapper(b: Boolean) { def ifTrue[R](f: => R) = new Otherwise0[R](if (b) Some(f) else None) def ifFalse[R](f: => R) = new Otherwise0[R](if (b) None else Some(f)) } implicit def extendBoolean(b: Boolean): BooleanWrapper = new BooleanWrapper(b) 现在,这有效,让我写 someTest ifTrue { println("OK") } 但是,如果没有以下其他条款,它当然不能返回R类型的值.所以这里是otherwise0的定义: class Otherwise0[R](intermediateResult: Option[R]) { def otherwise[S >: R](f: => S) = intermediateResult.getOrElse(f) def apply[S >: R](f: => S) = otherwise(f) } 当且仅当从前面的ifTrue或ifFalse获得的中间结果未定义时,它才会计算其传递的命名参数,这正是所需的.类型参数化[S>:R]具有以下效果:S被推断为命名参数的实际类型的最特定的常见超类型,例如,此片段中的r具有推断类型Fruit: class Fruit class Apple extends Fruit class Orange extends Fruit val r = someTest ifTrue { new Apple } otherwise { new Orange } apply()别名甚至允许您为短代码块完全跳过其他方法名称: someTest.ifTrue(10).otherwise(3) // equivalently: someTest.ifTrue(10)(3) 最后,这是选项的相应pimp: class OptionExt[A](option: Option[A]) { def ifNone[R](f: => R) = new Otherwise1(option match { case None => Some(f) case Some(_) => None },option.get) def ifSome[R](f: A => R) = new Otherwise0(option match { case Some(value) => Some(f(value)) case None => None }) } implicit def extendOption[A](opt: Option[A]): OptionExt[A] = new OptionExt[A](opt) class Otherwise1[R,A1](intermediateResult: Option[R],arg1: => A1) { def otherwise[S >: R](f: A1 => S) = intermediateResult.getOrElse(f(arg1)) def apply[S >: R](f: A1 => S) = otherwise(f) } 请注意,我们现在还需要otherwise1,以便我们可以方便地将unwrapped值传递给ifSome函数参数,还可以传递给ifNone后面的函数参数. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |