8

私は、Scala でワンライナー メソッドを記述できる方法が気に入っていますList(1, 2, 3).foreach(..).map(..)

しかし、Scala コードを書いているときに時々出てくる特定の状況があります。例:

def foo(a: A): Int = {
  // do something with 'a' which results in an integer
  // e.g. 'val result = a.calculateImportantThings

  // clean up object 'a'
  // e.g. 'a.cleanUp'

  // Return the result of the previous calculation
  return result
}

この状況では、結果を返す必要がありますが、返す前にクリーンアップを行う必要があるため、計算が完了した直後に結果を返すことはできません。

私はいつも 3 ライナーを書かなければなりません。これを行うためにワンライナーを書く可能性もありますか(Aこれは変更できない外部ライブラリである可能性があるため、のクラスを変更せずに)?

4

6 に答える 6

9

ここには明らかに副作用があります(そうでなければ、呼び出しの順序は重要ではcalculateImportantThingsありcleanUpません)ので、設計を再検討することをお勧めします。

ただし、それがオプションでない場合は、次のような方法を試すことができます。

scala> class A { def cleanUp {} ; def calculateImportantThings = 23 }
defined class A

scala> val a = new A
a: A = A@927eadd

scala> (a.calculateImportantThings, a.cleanUp)._1
res2: Int = 23

タプル値(a, b)はアプリケーションと同等でTuple2(a, b)あり、Scala仕様は、その引数が左から右に評価されることを保証します。これは、ここで必要なことです。

于 2012-07-09T09:21:41.517 に答える
7

tryこれは/の完璧なユースケースですfinally

try a.calculateImportantThings finally a.cleanUp

これが機能するのは、try / catch / finallyがscalaのであり、値を返すことを意味します。さらに良いことに、計算が例外をスローするかどうかに関係なく、クリーンアップを取得します。

例:

scala> val x = try 42 finally println("complete")
complete
x: Int = 42
于 2012-07-09T15:26:14.210 に答える
5

実際、まさにそのような機会のための Haskell 演算子があります。

(<*) :: Applicative f => f a -> f b -> f a

例えば:

ghci> getLine <* putStrLn "Thanks for the input!"
asdf
Thanks for the input!
"asdf"

scalaz は通常 Haskell が持っているものすべてを複製するので、あとは scalaz で同じ演算子を見つけるだけです。Scala は効果を分類するIdentity必要がないため、値を でラップできます。IO結果は次のようになります。

import scalaz._
import Scalaz._

def foo(a: A): Int = 
  (a.calculateImportantThings.pure[Identity] <* a.cleanup.pure[Identity]).value

ただし、副作用のある計算を Identity で明示的にラップする必要があるため、これはかなり厄介です。実のところ、scalaz はIdentity コンテナーとの間で暗黙的に変換する魔法を行っているので、次のように書くだけです。

def foo(a: A): Int = Identity(a.calculateImportantThings) <* a.cleanup()

一番左のものが Identity モナドにあることを何らかの形でコンパイラに示唆する必要があります。上記は私が考えることができる最短の方法でした。もう 1 つの可能性は、 を使用することです。これは、との効果をこの順序で呼び出し、の値を生成します。Identity() *> foo <* barfoobarfoo

ghci の例に戻るには:

scala> import scalaz._; import Scalaz._
import scalaz._
import Scalaz._

scala> val x : String = Identity(readLine) <* println("Thanks for the input!")
<< input asdf and press enter >>
Thanks for the input!
x: String = asdf
于 2012-07-09T11:54:23.960 に答える
5

たぶん、ケストレル コンビネータを使いたいですか? 次のように定義されています。

Kxy = x

したがって、戻りたい値と実行したい副作用のある操作を指定して呼び出します。

次のように実装できます。

def kestrel[A](x: A)(f: A => Unit): A = { f(x); x }

...そしてこのように使用します:

kestrel(result)(result => a.cleanUp)

詳細については、debasish gosh blogを参照してください。

[更新] Yaroslav が正しく指摘しているように、これはケストレル コンビネータの最適なアプリケーションではありません。しかし、引数なしの関数を使用して同様のコンビネータを定義しても問題はないはずです。そのため、代わりに次のようにします。

f: A => Unit

誰かが使用できます:

f: () => Unit
于 2012-07-09T09:16:11.070 に答える
2
class Test {
  def cleanUp() {}
  def getResult = 1
}

def autoCleanup[A <: Test, T](a: A)(x: => T) = {
  try { x } finally { a.cleanUp }
}  

def foo[A <: Test](a:A): Int = autoCleanup(a) { a.getResult }


foo(new Test)

型クラスベースのソリューションのscala-armプロジェクトを見ることができます。

于 2012-07-09T09:19:05.367 に答える