我经常遇到以下情况:假设我有这三个功能
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 函数,我可以在一个表达式中定义此函数。
所以我的目标是calculate
用一个表达式定义函数。我想出了2个解决方案。
1 - 使用scalaz我可以这样定义它(有没有更好的方法来使用 scalaz?):
def calculate(a: Long) =
firstFn |> {first => secondFn(first) |> {second => thirdFn(first, second, second + a)}}
我不喜欢这个解决方案的是它是嵌套的。我拥有的 s越多val
,这个嵌套就越深。
2 - 通过for
理解,我可以实现类似的目标:
def calculate(a: Long) =
for (first <- Option(firstFn); second <- Option(secondFn(first))) yield thirdFn(first, second, second + a)
一方面,这个解决方案具有扁平结构,就像let
在 Clojure 中一样,但另一方面我需要将函数的结果包装起来并作为结果Option
接收(这很好,我正在处理空值,但我不...... .并且不想)。Option
calculate
有没有更好的方法来实现我的目标?处理这种情况的惯用方式是什么(也许我应该和val
s 呆在一起......但是let
这样做的方式看起来很优雅)?
另一方面,它与参照透明度有关。所有三个函数都是引用透明的(在我的示例中firstFn
计算一些常数,如 Pi),因此理论上它们可以用计算结果替换。我知道这一点,但编译器不知道,所以它无法优化我的第一次尝试。这是我的第二个问题:
我能否以某种方式(可能带有注释)向编译器提示我的函数是引用透明的,以便它可以为我优化这个函数(例如,在那里放置某种缓存)?
编辑
感谢大家的精彩回答!选择一个最佳答案是不可能的(可能是因为它们都很好)所以我会接受投票最多的答案,我认为这很公平。