8

我对 Scala 还很陌生,偶然发现了一个一直困扰我的小问题。假设有一些带有默认参数的方法

def foo(v: Any = "default"): String = s"called with parameter '$v'"

和一个选项val opt: Option[String]。如何使用选项值(如果已定义)或默认参数调用此方法?我的意思是尽管有明显的解决方案

val result = if (opt.isDefined)
               from.here.to.foo(opt.get)
             else
               from.here.to.foo()

并且必须使用(可能很长)对象链两次键入方法?更不用说有多个可选/默认参数...


我能想到的只是无用的帮手

def definedOrDefault[A, B](opt: Option[A], f0: => B, f1: A => B): B =
  if (opt.isDefined) f1(opt.get) else f0

但是当无法在高阶函数中提及默认参数时……就是这样。让我想起了 Java 的糟糕过去,方法重载会产生同样的问题。

4

3 回答 3

8

似乎您在一个地方使用了两个可选值的概念,即可选参数和Option. 他们不喜欢一起玩,也许最好只用一个。

如果您总是只将值传递Option给方法或不传递任何内容以获取默认值,则可以考虑更改函数以接受Option.

def foo(v: Option[String]) = {
  val param = v getOrElse "default"
  s"called with parameter '$param'"
}

如果您仍然想要默认参数,您可以将签名更改为

def foo(v: Option[String] = None)

但是,这种方法将要求您Some在进行常规调用时将参数包装到其中,例如

foo(Some("regular param"))

但是当你使用Options 时效果很好。您也可以轻松添加更多可选参数。

这是一个例子

def foo(v1: Option[String] = None, v2: Option[Int] = None) = {
  val param1 = v1 getOrElse "default"
  val param2 = v2 getOrElse 42
  s"'$param1', '$param2'"
}

foo() // "default", 42

foo(v2 = Some(12)) // "default", 12

foo(Some("asd"), Some(11)) // "asd", 11

val optionFromSomewhere = Some("a")
val anotherOptionFromSomewhere = Option.empty[Int]
foo(optionFromSomewhere, anotherOptionFromSomewhere) // "a", 42

Any您还可以引入从to 的隐式转换Option,这会让您省略Some,但我认为这不是一个好主意

 implicit def any2option[A](e: A): Option[A] = Some(e)
于 2016-06-10T15:31:44.967 回答
2

对@Łukasz 的回答稍加扩展,太大而无法放入评论:

您可以通过创建一个特殊用途的类型来避免在Some没有危险的情况下包装当前参数:any2option

sealed trait OptParam[+A] { def toOption: Option[A] }
case class Param[+A](value: A) extends OptParam[A] { def toOption = Some(value) }
case object NoParam extends OptParam[Nothing] { def toOption = None }

object OptParam {
  implicit def any2optParam[A](x: A): OptParam[A] = Param(x)
}

def foo(v1: OptParam[String] = NoParam, v2: OptString[Int] = NoParam) = {
  val param1 = v1.toOption.getOrElse("default")
  val param2 = v2.toOption.getOrElse(42)
  s"'$param1', '$param2'"
}

foo("a") // "'a', '42'"

使这更安全的是它OptParam应该只作为方法参数类型出现,因此转换不会在您不期望的地方被触发。

于 2016-06-10T17:35:40.550 回答
0

您可以使用map定义的函数Option来转换包含在其中的值(如果已定义)。这看起来像这样:

opt.map(foo)
// same as
opt.map(x => foo(x))

Option[String]如果某个值在里面,或者None之前在里面,这将返回一个None

这是来自 REPL 的完整示例:

scala> def foo(v: Any = "default"): String = s"called with parameter '$v'"
foo: (v: Any)String

scala> Some("Hello")
res0: Some[String] = Some(Hello)

scala> res0.map(foo)
res1: Option[String] = Some(called with parameter 'Hello')

scala> val x: Option[String] = None
x: Option[String] = None

scala> x.map(foo)
res2: Option[String] = None

编辑:

要使用默认值调用它,您应该在调用之前匹配您的 Option,因为该方法需要一个非Option参数。

val optOrDefault = opt getOrElse "default"
foo(optOrDefault)
于 2016-06-10T15:33:21.340 回答