Clojure 在 Scala 中的“let"等价物 [英] Clojure's 'let' equivalent in Scala

查看:23
本文介绍了Clojure 在 Scala 中的“let"等价物的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经常面临以下情况:假设我有这三个功能

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 vals 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).

是否有更好的方法来实现我的目标?处理这种情况的惯用方法是什么(也许我应该继续使用 vals...但是 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 vals... 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"等价物的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆