我经常遇到以下情况:假设我具有以下三个功能
def firstFn: Int = ...
def secondFn(b: Int): Long = ...
def thirdFn(x: Int, y: Long, z: Long): Long = ...
而且我还有calculate
函数。我的第一种方法如下所示:def calculate(a: Long) = thirdFn(firstFn, secondFn(firstFn), secondFn(firstFn) + a)
它看起来很漂亮,没有任何花括号-仅是一种表达。但这不是最佳选择,因此我得到了以下代码:def calculate(a: Long) = {
val first = firstFn
val second = secondFn(first)
thirdFn(first, second, second + a)
}
现在是几个用大括号括起来的表达式。在这样的时刻,我有点羡慕Clojure。使用let function,我可以在一个表达式中定义此函数。因此,我的目标是使用一个表达式定义
calculate
函数。我想出了两种解决方案。1-使用 scalaz 我可以这样定义(是否有更好的方法可以使用scalaz?):
def calculate(a: Long) =
firstFn |> {first => secondFn(first) |> {second => thirdFn(first, second, second + a)}}
我对此解决方案不满意的是它是嵌套的。我拥有的val
越多,嵌套的深度就越大。2-通过
for
理解,我可以实现类似的效果: def calculate(a: Long) =
for (first <- Option(firstFn); second <- Option(secondFn(first))) yield thirdFn(first, second, second + a)
一方面,此解决方案具有扁平的结构,就像Clojure中的let
,但另一方面,我需要将函数的结果包装在Option
中,并从Option
接收结果中的calculate
(很好,我正在处理null,但是我不这样做't ...,也不想)。是否有更好的方法可以实现我的目标?处理这种情况的惯用方式是什么(也许我应该继续使用
val
s ...,但是let
的处理方式看起来如此优雅)? 另一方面,它连接到Referential transparency。所有这三个函数都是参照透明的(在我的示例中,
firstFn
计算像Pi一样的常数),因此从理论上讲,它们可以用计算结果代替。我知道这一点,但是编译器不知道,因此它无法优化我的第一次尝试。这是我的第二个问题:我可以以某种方式(可能带有批注)提示编译器,我的函数是参照透明的,以便它可以为我优化此函数(例如,在其中进行某种缓存)吗?
编辑
谢谢大家的精彩回答!选择一个最佳答案是不可能的(可能是因为它们都很好),所以我会以最高的投票数接受答案,我认为这很公平。
最佳答案
在非递归情况下,让我们进行lambda的重组。
def firstFn : Int = 42
def secondFn(b : Int) : Long = 42
def thirdFn(x : Int, y : Long, z : Long) : Long = x + y + z
def let[A, B](x : A)(f : A => B) : B = f(x)
def calculate(a: Long) = let(firstFn){first => let(secondFn(first)){second => thirdFn(first, second, second + a)}}
当然,这仍然是嵌套的。无法避免。但是你说你喜欢单子(monad)形式。这是身份单子(monad)
case class Identity[A](x : A) {
def map[B](f : A => B) = Identity(f(x))
def flatMap[B](f : A => Identity[B]) = f(x)
}
这是您的单子(monad)计算。通过调用.x解开结果
def calculateMonad(a : Long) = for {
first <- Identity(firstFn)
second <- Identity(secondFn(first))
} yield thirdFn(first, second, second + a)
但是在这一点上,它肯定看起来像原始的val版本。
Identity Monad在Scalaz中存在并且更加复杂
http://scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/Identity.scala.html
关于scala - 与Scala中Clojure的 'let'等效,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4881443/