如果定义了选项,则用于在链中应用函数的惯用Scala

时间:2013-12-29 11:40:42

标签: scala coding-style monads optional-parameters named-parameters

是否有预先存在的/ Scala-idiomatic /更好的方法来实现这一目标?

def sum(x: Int, y: Int) = x + y

var x = 10
x = applyOrBypass(target=x, optValueToApply=Some(22), sum)
x = applyOrBypass(target=x, optValueToApply=None, sum)
println(x) // will be 32

我的applyOrBypass可以像这样定义:

def applyOrBypass[A, B](target: A, optValueToApply: Option[B], func: (A, B) => A) = {
  optValueToApply map { valueToApply =>
    func(target, valueToApply)
  } getOrElse {
    target
  }
}

基本上我想要应用操作,具体取决于是否定义了某些Option值。如果不是,我应该得到预先存在的价值。理想情况下,我想链接这些操作,而不必使用var。

我的直觉告诉我,折叠或减少会涉及到,但我不确定它是如何工作的。或者也许还有另一种与monadic-fors有关的方法......

任何建议/提示都赞赏!

4 个答案:

答案 0 :(得分:9)

Scala有一种方法可以用于理解(如果你熟悉它,语法类似于haskell的do符号):

(for( v <- optValueToApply ) 
  yield func(target, v)).getOrElse(target)

当然,如果您要检查是否存在以下几个变量,这会更有用:

(for( v1 <- optV1
    ; v2 <- optV2
    ; v3 <- optV3
    ) yield func(target, v1, v2, v3)).getOrElse(target)

如果您尝试在选项列表上累积值,那么我建议您使用折叠,因此您的可选总和将如下所示:

val vs = List(Some(1), None, None, Some(2), Some(3))

(target /: vs) ( (x, v) => x + v.getOrElse(0) )
  // => 6 + target

如果您的操作func具有某些标识值identity,则可以对此进行概括:

(target /: vs) ( (x, v) => func(x, v.getOrElse(identity)) )

从数学上讲,这个条件是(func, identity)形成 Monoid 。但那是旁边的。实际效果是,只要达到None,将func应用于x并且x将始终生成NoneSome将被忽略,并且{{1}}值被解包并正常应用),这就是你想要的。

答案 1 :(得分:3)

在这种情况下我会做的是使用部分应用的函数和identity

def applyOrBypass[A, B](optValueToApply: Option[B], func: B => A => A): A => A =
  optValueToApply.map(func).getOrElse(identity)

你会像这样申请:

def sum(x: Int)(y: Int) = x + y

var x = 10
x = applyOrBypass(optValueToApply=Some(22), sum)(x)
x = applyOrBypass(optValueToApply=None, sum)(x)
println(x)

答案 2 :(得分:2)

是的,您可以使用fold。如果你有多个可选操作数,我相信Scalaz库中有一些有用的抽象。

var x = 10
x = Some(22).fold(x)(sum(_, x))
x = None    .fold(x)(sum(_, x))

答案 3 :(得分:1)

如果您有多个功能,可以使用Scalaz完成。

有几种方法可以做到,但这是最简洁的方法之一。

首先,添加您的导入:

import scalaz._, Scalaz._

然后,创建你的函数(如果你的函数总是相同,这种方式是不值得的,但如果它们不同,那就有意义了)

val s = List(Some(22).map((i: Int) => (j: Int) => sum(i,j)),
             None    .map((i: Int) => (j: Int) => multiply(i,j)))

最后,全部应用它们:

(s.flatten.foldMap(Endo(_)))(x)