【问题标题】:Custom "let" expression in ScalaScala中的自定义“let”表达式
【发布时间】:2013-08-27 08:20:43
【问题描述】:

我希望 let 构造类似于 Scala 中的 Haskell 中的构造。我尝试了几种方法,但似乎没有一个是好的。这是一些代码:

object CustomLet extends App {
  val data = for (i <- 1 to 1024; j <- 1 to 512) yield (i % j) * i * (i + 1) - 1

  def heavyCalc() = { println("heavyCalc called"); data.sum }

  def doSomethingWithRes(res: Int) = {
    println(s"${res * res}")
    1
  }

  def cond(value: Int): Boolean = value > 256

  // not really usable, even though it's an expression (2x heavyCalc calls)
  def withoutLet() = if (cond(heavyCalc())) doSomethingWithRes(heavyCalc()) else 0

  // not an expression
  def letWithVal(): Int = {
    val res = heavyCalc()
    if (cond(res)) doSomethingWithRes(res)
    else 0
  }

  // a lot of code to simulate "let", at least it is an expression
  def letWithMatch(): Int = heavyCalc() match {
    case res => if (cond(res)) doSomethingWithRes(res) else 0
  }

  // not perfect solution from
  // http://stackoverflow.com/questions/3241101/with-statement-equivalent-for-scala/3241249#3241249
  def let[A, B](param: A)(body: A => B): B = body(param)

  // not bad, but I'm not sure if it could handle more bindings at once
  def letWithApp(): Int = let(heavyCalc()) {res => if (cond(res)) doSomethingWithRes(res) else 0}

  List[(String, () => Int)](
    ("withoutLet", withoutLet),
    ("letWithVal", letWithVal),
    ("letWithMatch", letWithMatch),
    ("letWithApp", letWithApp)
  ).foreach(
    item => item match {
      case (title, func) => {
        println(s"executing $title")
        val ret = func()
        println(s"$title finished with $ret")
        println()
      }
    }
  )
}

这是它的理想外观(只有一个绑定,更多可以用, 分隔;不确定in 关键字):

  // desired look
  def letTest(): Int =
    let res = heavyCalc() in
      if (cond(res)) doSomethingWithRes(res) else 0

我不确定这是否可能,但我没有使用大多数高级 Scala 东西(如宏)的经验,所以我无法确定。

EDIT1:明确地说,我对它的主要期望是:表达式和相对简单的语法(就像上面概述的那样)。

【问题讨论】:

  • 对于我们这些不会说 Haskell 的人,你能解释一下“让”应该做什么吗?
  • letWithVal 与letTest 中的let 做同样的事情。

标签: scala syntax let


【解决方案1】:

您可以使用正向管道:

object ForwardPipeContainer {
  implicit class ForwardPipe[A](val value: A) extends AnyVal {
    def |>[B](f: A => B): B = f(value)
  }
}

这样使用:

import ForwardPipeContainer._

def f(i: Int) = i * i

println( f(3) |> (x => x * x) )

您可以在一个元组中放置多个参数:

println( (f(2), f(3)) |> (x => x._1 * x._2) )

如果与偏函数 synatx 结合使用会更好看:

println( (f(2), f(3)) |> { case (x, y) => x * y } )

这个答案是What is a good way of reusing function result in Scala 的变体,两者都基于我最初的想法来自Cache an intermediate variable in an one-liner。

【讨论】:

    【解决方案2】:
    def letTest(): Int =
        let res = heavyCalc() in
          if (cond(res)) doSomethingWithRes(res) else 0
    

    我会这样写:

    def letTest(): Int = {
      val res = heavyCalc()
      if (cond(res)) doSomethingWithRes(res) else 0
    }
    

    忽略惰性,let 只是一个引入词法范围的构造,将一些术语绑定到一些名称,然后返回一个表达式。所以在 Scala 中你会这样做

    { // new lexical scope
      // bind terms section
      val a = f()
      def b = a + g() // may be I don't want g to be evaluated unless b is needed
      val c = h()
      // result expression
      if (c) b else a 
    }
    

    如果你想确保块中没有其他事情发生,宏应该能够强制执行这种语法布局。实际上有一个名为 Spores 的 SIP(​​Scala 改进流程)提案将强制执行一些相同的约束(还有一个附加约束:您不会在不知不觉中捕获对封闭对象的引用)。

    请注意,Scala 中的块是计算结果为块中最后一个表达式的表达式。因此,让我以 Haskell 中的 random let 为例:

    aaa = let y = 1+2
              z = 4+6
              in let f = 3
                     e = 3
                 in e+f
    

    这转化为:

    val aaa = {
      val y = 1 + 2
      val z = 4 + 6
      val u = {
        val f = 3
        val e = 3
        e + f
      }
      u
    }
    

    如您所见,块语句可以用作表达式。

    【讨论】:

    • 对于val 解决方案 - 它不是一个表达式(实际上与问题帖子中的letWithVal 相同)。
    • @monnef,是的,你已经考虑过了。好吧,它实际上 是 一个表达式(请参阅我的评论)。
    • 是的,你是对的。这是一个表达式,但不完全是我想要的——我想避免命令式的 val/var 和块。如果我的问题没有完全说明,我很抱歉。 (我知道在引擎盖下它会转化为类似于 val/var 的东西。我正在寻找一种以功能方式轻松绑定的方法。)
    • @monnef,一旦你习惯了它,我认为我的简单解决方案是有好处的:)
    猜你喜欢
    • 2014-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多