问题描述
我经常面临以下情况:假设我有这三个功能
Often I face following situation: suppose I have these three functions
def firstFn: Int = ...
def secondFn(b: Int): Long = ...
def thirdFn(x: Int, y: Long, z: Long): Long = ...
而且我也有 calculate
功能.我的第一种方法可能如下所示:
and I also have calculate
function. My first approach can look like this:
def calculate(a: Long) = thirdFn(firstFn, secondFn(firstFn), secondFn(firstFn) + a)
它看起来很漂亮,没有任何大括号——只有一种表达方式.但这不是最优的,所以我最终得到了这个代码:
It looks beautiful and without any curly brackets - just one expression. But it's not optimal, so I end up with this code:
def calculate(a: Long) = {
val first = firstFn
val second = secondFn(first)
thirdFn(first, second, second + a)
}
现在是几个用大括号括起来的表达式.在这样的时刻,我有点羡慕 Clojure.使用 let 函数,我可以在一个表达式中定义这个函数.
Now it's several expressions surrounded with curly brackets. At such moments I envy Clojure a little bit. With let function I can define this function in one expression.
所以我的目标是用一个表达式定义calculate
函数.我想出了 2 个解决方案.
So my goal here is to define calculate
function with one expression. I come up with 2 solutions.
1 - 使用 scalaz 我可以这样定义它(使用 scalaz 有没有更好的方法来做到这一点?):
1 - With scalaz I can define it like this (are there better ways to do this with scalaz?):
def calculate(a: Long) =
firstFn |> {first => secondFn(first) |> {second => thirdFn(first, second, second + a)}}
我不喜欢这个解决方案的地方在于它是嵌套的.val
越多,嵌套越深.
What I don't like about this solution is that it's nested. The more val
s I have the deeper this nesting is.
2 - 使用 for
理解我可以实现类似的东西:
2 - With for
comprehension I can achieve something similar:
def calculate(a: Long) =
for (first <- Option(firstFn); second <- Option(secondFn(first))) yield thirdFn(first, second, second + a)
一方面,这个解决方案具有扁平结构,就像 Clojure 中的 let
,但另一方面我需要将函数的结果包装在 Option
中并接收 Option
作为 calculate
的结果(很好,我正在处理空值,但我不......也不想要).
From one hand this solution has flat structure, just like let
in Clojure, but from the other hand I need to wrap functions' results in Option
and receive Option
as result from calculate
(it's good it I'm dealing with nulls, but I don't... and don't want to).
是否有更好的方法来实现我的目标?处理这种情况的惯用方法是什么(也许我应该继续使用 val
s...但是 let
这样做的方式看起来很优雅)?
Are there better ways to achieve my goal? What is the idiomatic way for dealing with such situations (may be I should stay with val
s... but let
way of doing it looks so elegant)?
另一方面,它与参考透明度有关.这三个函数都是引用透明的(在我的例子中,firstFn
计算一些像 Pi 一样的常数),所以理论上它们可以用计算结果代替.我知道这一点,但编译器不知道,所以它不能优化我的第一次尝试.这是我的第二个问题:
From other hand it's connected to Referential transparency. All three functions are referentially transparent (in my example firstFn
calculates some constant like Pi), so theoretically they can be replaced with calculation results. I know this, but compiler does not, so it can't optimize my first attempt. And here is my second question:
我能否以某种方式(可能带有注释)向编译器提示,我的函数是引用透明的,以便它可以为我优化此函数(例如,在那里放置某种缓存)?
感谢大家的精彩回答!选择一个最佳答案是不可能的(可能是因为它们都很好)所以我会接受最高票数的答案,我认为这足够公平.
Thanks everybody for the great answers! It's just impossible to select one best answer (may be because they all so good) so I will accept answer with the most up-votes, I think it's fair enough.
推荐答案
在非递归情况下,让我们对 lambda 进行重构.
in the non-recursive case, let is a restructuring of 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)}}
当然,这仍然是嵌套的.无法避免.但是你说你喜欢 monadic 形式.所以这里是身份 monad
Of course, that's still nested. Can't avoid that. But you said you like the monadic form. So here's the identity 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)
}
这是您的 monadic 计算.通过调用 .x
And here's your monadic calculate. Unwrap the result by calling .x
def calculateMonad(a : Long) = for {
first <- Identity(firstFn)
second <- Identity(secondFn(first))
} yield thirdFn(first, second, second + a)
但在这一点上它确实看起来像原始的 val 版本.
But at this point it sure looks like the original val version.
Scalaz 中的 Identity monad 更加复杂
The Identity monad exists in Scalaz with more sophistication
http://scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/Identity.scala.html
这篇关于Clojure 在 Scala 中的“let"等价物的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!