何时以及为何解除功能
When and why to Lift a Function
考虑这段代码:
def f1(a: Int, b: Int) = a + b
def f2(a: Option[Int], b: Option[Int]): Int = (a, b) match {
case (Some(x), Some(y)) => x + y
case _ => throw new IllegalArgumentException
}
println(f1(10, 20))
println(f2(Some(10), Some(20)))
我听说您可以“提升”f1 使其像 f2。作为初学者,我有以下问题:
什么是提升,为什么要使用?在实现方面,如何“提升”f1?
非常感谢任何解释,因为很难找到我为什么要“举起”某物的原因
原因:当您有一个带有 f1
签名的函数并想在 Option[Int]
s(或 List[Int]
s 等)上“调用它”时
方法:可以直接写:
def lift2option[A, B, C](f: (A, B) => C): (Option[A], Option[B]) => Option[C] = ???
我将其保留为未定义状态,因为您应该尝试自己编写它;您对 f2
的定义应该是一个很好的起点。请注意,我将其设为 return Option[Int]
而不是 Int
。如果你愿意,我稍后可以编辑并给出答案。
然后不是将 f2
定义为单独的函数,而是:
val f2 = lift2option(f1 _)
println(f2(Some(10), Some(20)))
当然,关键是现在对于 任何 带有像 f1
这样的签名的函数,你可以获得 f2
等价物。
它可以进一步推广,不仅适用于 Option
,但您稍后会想研究它。
我只是想补充一点,提升一个函数可以用作 functor 上映射的替代方法。例如,如果您有 2 个 Option[Int]
对象,您想要应用 f1
,您可以这样做:
val sum: Option[Int] = option1.flatMap { x => option2.map{ y => x + y } }
请注意,结果是 Option[Int]
。正如 Alexey Romanov 所说,f2
的 return 类型也应该是 Option
。 Option
的全部要点是让您对值进行操作而不用担心 NullPointerException
或其他错误,因为该值不存在。
然而,这个映射有点冗长,而且必须决定何时需要使用 flatMap
和 map
很烦人。这就是提升派上用场的地方。
让我们定义 f2
更好地处理 None
s:
def f2(a: Option[Int], b: Option[Int]): Option[Int] =
a match {
case Some(x) => b match {
case Some(y) => Some(x + y)
case None => None
}
case None => None
}
我们也可以根据 f1
来定义它,将 x + y
替换为 f1(x + y)
def f2(a: Option[Int], b: Option[Int]): Option[Int] =
a match {
case Some(x) => b match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
现在,f2
不需要知道如何添加数字,它只需要使用 f1
来添加它们。实际上,我们甚至可以将 f1
作为 f2
的参数。
def f2(f1: (Int, Int) => Int)(a: Option[Int], b: Option[Int]): Option[Int] =
a match {
case Some(x) => b match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
看到那里发生了什么吗?我们刚刚使用 f2
将 f1
从 (Int, Int) => Int
“提升”到 (Option[Int], Option[Int]) => Option[Int]
。实际上,我们将其重命名为 lift2
。我们还可以让它更通用:
def lift2[A, B, C](f1: (A, B) => C)(a: Option[A], b: Option[B]): Option[C] =
a match {
case Some(x) => b match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
lift2
现在是一个接受类型函数 (A, B) => C
的函数(这里,A
、B
和 C
都是 Int
for f1
) 和 return 类型 (Option[A], Option[B]) => Option[C]
的另一个函数。现在,我们不必使用那些笨拙的嵌套 map
s 和 flatMap
s。你可以这样做:
val sum: Option[Int] = lift2(f1)(option1, option2)
你当然也可以定义lift3
、lift4
等,但是只定义一个lift1
函数并使用currying来做可能更容易剩下的
当然,如果您知道如何拆分和组合要提升到的类型,则只能 lift
一个函数。例如,如果 Some
是一个具有私有 unapply
方法的对象,并且不可能对其进行模式匹配,那么您将无法提升 f1
。如果 Some
的构造函数是私有的并且您不可能创建新的 Option
s.
,也会发生同样的情况
编辑:以下是您可以将多个 Option[Int]
对象与 f1
和 lift2
函数一起添加的方法。
val f2 = lift2(f1)
val optionSum = f2(f2(option1, option2), option3)
没有 f2,它看起来像这样
val sum1 = option1 match {
case Some(x) => option2 match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
val finalSum = sum1 match {
case Some(x) => option3 match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
考虑这段代码:
def f1(a: Int, b: Int) = a + b
def f2(a: Option[Int], b: Option[Int]): Int = (a, b) match {
case (Some(x), Some(y)) => x + y
case _ => throw new IllegalArgumentException
}
println(f1(10, 20))
println(f2(Some(10), Some(20)))
我听说您可以“提升”f1 使其像 f2。作为初学者,我有以下问题:
什么是提升,为什么要使用?在实现方面,如何“提升”f1?
非常感谢任何解释,因为很难找到我为什么要“举起”某物的原因
原因:当您有一个带有 f1
签名的函数并想在 Option[Int]
s(或 List[Int]
s 等)上“调用它”时
方法:可以直接写:
def lift2option[A, B, C](f: (A, B) => C): (Option[A], Option[B]) => Option[C] = ???
我将其保留为未定义状态,因为您应该尝试自己编写它;您对 f2
的定义应该是一个很好的起点。请注意,我将其设为 return Option[Int]
而不是 Int
。如果你愿意,我稍后可以编辑并给出答案。
然后不是将 f2
定义为单独的函数,而是:
val f2 = lift2option(f1 _)
println(f2(Some(10), Some(20)))
当然,关键是现在对于 任何 带有像 f1
这样的签名的函数,你可以获得 f2
等价物。
它可以进一步推广,不仅适用于 Option
,但您稍后会想研究它。
我只是想补充一点,提升一个函数可以用作 functor 上映射的替代方法。例如,如果您有 2 个 Option[Int]
对象,您想要应用 f1
,您可以这样做:
val sum: Option[Int] = option1.flatMap { x => option2.map{ y => x + y } }
请注意,结果是 Option[Int]
。正如 Alexey Romanov 所说,f2
的 return 类型也应该是 Option
。 Option
的全部要点是让您对值进行操作而不用担心 NullPointerException
或其他错误,因为该值不存在。
然而,这个映射有点冗长,而且必须决定何时需要使用 flatMap
和 map
很烦人。这就是提升派上用场的地方。
让我们定义 f2
更好地处理 None
s:
def f2(a: Option[Int], b: Option[Int]): Option[Int] =
a match {
case Some(x) => b match {
case Some(y) => Some(x + y)
case None => None
}
case None => None
}
我们也可以根据 f1
来定义它,将 x + y
替换为 f1(x + y)
def f2(a: Option[Int], b: Option[Int]): Option[Int] =
a match {
case Some(x) => b match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
现在,f2
不需要知道如何添加数字,它只需要使用 f1
来添加它们。实际上,我们甚至可以将 f1
作为 f2
的参数。
def f2(f1: (Int, Int) => Int)(a: Option[Int], b: Option[Int]): Option[Int] =
a match {
case Some(x) => b match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
看到那里发生了什么吗?我们刚刚使用 f2
将 f1
从 (Int, Int) => Int
“提升”到 (Option[Int], Option[Int]) => Option[Int]
。实际上,我们将其重命名为 lift2
。我们还可以让它更通用:
def lift2[A, B, C](f1: (A, B) => C)(a: Option[A], b: Option[B]): Option[C] =
a match {
case Some(x) => b match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
lift2
现在是一个接受类型函数 (A, B) => C
的函数(这里,A
、B
和 C
都是 Int
for f1
) 和 return 类型 (Option[A], Option[B]) => Option[C]
的另一个函数。现在,我们不必使用那些笨拙的嵌套 map
s 和 flatMap
s。你可以这样做:
val sum: Option[Int] = lift2(f1)(option1, option2)
你当然也可以定义lift3
、lift4
等,但是只定义一个lift1
函数并使用currying来做可能更容易剩下的
当然,如果您知道如何拆分和组合要提升到的类型,则只能 lift
一个函数。例如,如果 Some
是一个具有私有 unapply
方法的对象,并且不可能对其进行模式匹配,那么您将无法提升 f1
。如果 Some
的构造函数是私有的并且您不可能创建新的 Option
s.
编辑:以下是您可以将多个 Option[Int]
对象与 f1
和 lift2
函数一起添加的方法。
val f2 = lift2(f1)
val optionSum = f2(f2(option1, option2), option3)
没有 f2,它看起来像这样
val sum1 = option1 match {
case Some(x) => option2 match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
val finalSum = sum1 match {
case Some(x) => option3 match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}