4

letScala で 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()
      }
    }
  )
}

これはその理想的な外観です (バインディングが 1 つだけの場合、より多くを で区切ることができます。キーワード,については不明です):in

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

それが可能かどうかはわかりませんが、マクロなどの高度な Scala のほとんどを扱った経験がないので、よくわかりません。

EDIT1:明確にするために、私が期待している主なものは、と比較的単純な構文(上記で概説したものなど)であることです。

4

2 に答える 2

6

フォワードパイプを使用できます:

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 } )

この回答は、Scala で関数の結果を再利用する良い方法とはのバリエーションであり、両方とも、最初のアイデアを得たワンライナーで中間変数をキャッシュするに基づいています。

于 2013-08-24T19:19:16.317 に答える
3
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 Improvement Process) の提案があり、同じ制約のいくつかを強制します (さらに別の制​​約: 囲まれているオブジェクトの参照を無意識のうちにキャプチャしないという制約もあります)。

Scala のブロックは、ブロック内の最後の式に評価される式であることに注意してください。それでは、Haskellのランダムな 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
}

ご覧のとおり、ブロック ステートメントは式として使用できます。

于 2013-08-24T14:06:54.773 に答える