`Option.fold()()`和`Option.map()。getOrElse()`有什么区别?

时间:2018-06-16 22:57:36

标签: scala

Option类有一个名为fold()的方法。文档说:

sealed abstract class Option[+A] 

fold[B](ifEmpty: ⇒ B)(f: (A) ⇒ B): B
  

如果scala.Option为非空,则返回将 f 应用于此scala.Option的值的结果。否则,评估表达式 ifEmpty

文档继续:

  

这相当于 scala.Option map f getOrElse ifEmpty

但这是真的吗?我被告知,在某些情况下,某些类型的价值观存在差异,但从来没有得到合理的解释。这两种结构的表现方式究竟是什么?为什么会这样?

2 个答案:

答案 0 :(得分:4)

Option.fold.getOrElse更安全。您可以在下面.fold看到ifEmptyf类型B的定义。(仅在scala 2.10之后引入)

  @inline final def fold[B](ifEmpty: => B)(f: A => B): B =
    if (isEmpty) ifEmpty else f(this.get)

这意味着你可能不会弄乱数据类型(下面的例外),

scala> val data = Option("massive data").fold(-1) { _ => 1 }
data: Int = 1

// but if i try to return different type in either of ifEmpty or f
// compiler will curse me right at my face   
scala> val data = Option("massive data").fold(-1) { _ => "Let me caught by compiler" }
<console>:17: error: type mismatch;
 found   : String("Let me caught by compiler")
 required: Int
       val data = Option("massive data").fold(-1) { _ => "Let me caught by compiler" }
                                                     ^

除非您手动提供类型,否则getOrElse不会更安全。

  @inline final def getOrElse[B >: A](default: => B): B =
    if (isEmpty) default else this.get

表示您可以从getOrElse返回与Option[T]中包含的原始值不同的其他类型。

scala> val data = Option("massive data").map(_ => 1).getOrElse(List("I'm not integer"))
data: Any = 1

//you have to manually mention the type to getOrElse to restrict, 
// which is not that smart in my opinion
scala> val data = Option("massive data").map(_ => 1).getOrElse[Int](List("I'm not integer"))
<console>:17: error: type mismatch;
 found   : List[String]
 required: Int
       val data = Option("massive data").map(_ => 1).getOrElse[Int](List("I'm not integer"))
                                                                    ^

有趣的是,您可以从unitgetOrElse返回fold,这可能会在应用程序中引入错误,除非您注意到单位品味。

scala> val data = Option("massive data").fold() { _ => 1 }
data: Unit = ()

scala> val data = Option("massive data").map(_ => 1).getOrElse()
data: AnyVal = 1

答案 1 :(得分:1)

作为对@ prayagupd答案的反对,06-17 11:58:04.855 29388-29388/? E/SELinux: [DEBUG] get_category: variable seinfo: default sensitivity: NULL, cateogry: NULL 06-17 11:58:04.865 172-172/? E/BufferQueueCore: [Starting kz.pillowz.app] setDefaultMaxBufferCount: setting count to 3, previous is 2 06-17 11:58:04.956 29388-29388/? E/AndroidRuntime: FATAL EXCEPTION: main Process: kz.pillowz.app, PID: 29388 java.lang.RuntimeException: Unable to instantiate application kz.pillowz.app.App: java.lang.ClassNotFoundException: Didn't find class "kz.pillowz.app.App" on path: DexPathList[[zip file "/data/app/kz.pillowz.app-1/base.apk"],nativeLibraryDirectories=[/data/app/kz.pillowz.app-1/lib/arm, /vendor/lib, /system/lib]] at android.app.LoadedApk.makeApplication(LoadedApk.java:661) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6027) at android.app.ActivityThread.access$1700(ActivityThread.java:218) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1805) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:145) at android.app.ActivityThread.main(ActivityThread.java:7007) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199) Caused by: java.lang.ClassNotFoundException: Didn't find class "kz.pillowz.app.App" on path: DexPathList[[zip file "/data/app/kz.pillowz.app-1/base.apk"],nativeLibraryDirectories=[/data/app/kz.pillowz.app-1/lib/arm, /vendor/lib, /system/lib]] at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) at java.lang.ClassLoader.loadClass(ClassLoader.java:511) at java.lang.ClassLoader.loadClass(ClassLoader.java:469) at android.app.Instrumentation.newApplication(Instrumentation.java:1003) at android.app.LoadedApk.makeApplication(LoadedApk.java:651) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6027)  at android.app.ActivityThread.access$1700(ActivityThread.java:218)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1805)  at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:145)  at android.app.ActivityThread.main(ActivityThread.java:7007)  at java.lang.reflect.Method.invoke(Native Method)  at java.lang.reflect.Method.invoke(Method.java:372)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)  Suppressed: java.lang.ClassNotFoundException: kz.pillowz.app.App at java.lang.Class.classForName(Native Method) at java.lang.BootClassLoader.findClass(ClassLoader.java:781) at java.lang.BootClassLoader.loadClass(ClassLoader.java:841) at java.lang.ClassLoader.loadClass(ClassLoader.java:504) ... 13 more Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available 06-17 11:58:04.986 753-29404/? E/android.os.Debug: ro.product_ship = true 经常邀请你以特定方式弄乱类型。

问题是,根据Scala的规则,只有fold用于推断 ifEmpty,然后检查B是否合适。这意味着使用fNone作为Nil这很常见,会导致他们的单身人士类型被用作ifEmpty而不是B,不会无论Option/List[SomeType]返回什么。

当然,有一些解决方法:明确指定f,使用BOption.empty[SomeType]代替None: Option[SomeType]。或者只使用模式匹配。