11

Clojure は、doto引数と関数のリストを受け取り、(評価された) 引数を先頭に追加して、基本的に各関数を呼び出すというマクロを提供します。

(doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))
-> {a=1, b=2}

Scalaで同様のものを実装する方法はありますか? 次のような形を想定しています。

val something =
  doto(Something.getInstance) {
    x()
    y()
    z()
  }

これはと同等になります

val something = Something.getInstance
something.x()
something.y()
something.z()

sを使用することは可能scala.util.DynamicVariableでしょうか?

のようなファクトリ メソッドでSomething.getInstanceは、一般的な Scala パターンを使用できないことに注意してください。

val something =
  new Something {
    x()
    y()
    z()
  }
4

6 に答える 6

12

ライブラリにそのようなものが組み込まれているとは思いませんが、非常に簡単に模倣できます。

def doto[A](target: A)(calls: (A => A)*) =
  calls.foldLeft(target) {case (res, f) => f(res)}

使用法:

scala> doto(Map.empty[String, Int])(_ + ("a" -> 1), _ + ("b" ->2))
res0: Map[String,Int] = Map(a -> 1, b -> 2)

scala> doto(Map.empty[String, Int])(List(_ + ("a" -> 1), _ - "a", _ + ("b" -> 2)))
res10: Map[String,Int] = Map(b -> 2)

もちろん、関数が適切な型を返す限り、それは機能します。あなたの場合、関数に副作用しかない場合(これはそれほど「スカラッシュ」ではありません)、次の代わりに変更dotoして使用できます。foreachfoldLeft

def doto[A](target: A)(calls: (A => Unit)*) =
  calls foreach {_(target)}

使用法:

scala> import collection.mutable.{Map => M}
import collection.mutable.{Map=>M}

scala> val x = M.empty[String, Int]
x: scala.collection.mutable.Map[String,Int] = Map()

scala> doto(x)(_ += ("a" -> 1), _ += ("a" -> 2))

scala> x
res16: scala.collection.mutable.Map[String,Int] = Map(a -> 2)
于 2012-06-20T12:13:10.913 に答える
5

Scala では、これを行う「典型的な」方法は、「タップ」または「パイプ」メソッドを連鎖させることです。これらは標準ライブラリにはありませんが、頻繁に次のように定義されています。

implicit class PipeAndTap[A](a: A) {
  def |>[B](f: A => B): B = f(a)
  def tap[B](f: A => B): A = { f(a); a }
}

その後、あなたは

(new java.util.HashMap[String,Int]) tap (_.put("a",1)) tap (_.put("b",2))

これは Clojure バージョンほどコンパクトではありません (または Scala ほどコンパクトではありません)。

(注: これらのメソッドを追加するための実行時のオーバーヘッドを最小限に抑えたい場合は、aaを作成しprivate valPipeAndTapextendを使用できAnyValます。これは、オブジェクトを渡す必要がある場合にのみ実際のクラスに変換される「値クラス」になります。 ; メソッドを呼び出すだけでは、実際にはクラスを作成する必要はありません。)

(2 番目の注意: 古いバージョンの Scala では、implicit classは存在しません。クラスと、implicit defジェネリックaを に変換する を別々に作成する必要がありPipeAndTapます。)

于 2012-06-20T13:36:17.733 に答える
4

最も近いのは、このオブジェクトのメンバーをスコープにインポートすることだと思います。

val something = ...
import something._

x()
y()
z()

この投稿では、別の例を見つけることができます (セクション「理論的根拠に関する小さな更新」):

http://hacking-scala.posterous.com/side-effecting-without-braces


また、このアプローチの小さな利点-個々のメンバーをインポートして名前を変更できます。

import something.{x, y => doProcessing}
于 2012-06-20T12:01:22.123 に答える
1

もっと簡単だと思います:

val hm = Map [String, Int] () + ("a"-> 1) + ("b"-> 2) 

あなたのサンプル

val something =
  doto (Something.getInstance) {
    x()
    y()
    z()
  }

あまり機能的に見えないので、結果はどうなりますか?私はあなたが副作用していると思います。

Something.x().y().z()

各呼び出しが次の関数が作用できるタイプを生成する場合の方法である可能性があります。

z(y(x(Something)))

別の種類の結果を生成します。

またandThen、コレクションのメソッド呼び出しをチェーンするメソッドがあります。ご覧になることをお勧めします。

マップの例では、左に折りたたむことも別の方法です。

val hm = Map [String, Int] () + ("a"-> 1) + ("b"-> 2) 

val l = List (("a", 8), ("b", 7), ("c", 9))
(hm /: l)(_ + _)
// res8: scala.collection.immutable.Map[String,Int] = Map(a -> 8, b -> 7, c -> 9)
于 2012-06-20T12:15:23.560 に答える
0

複数のアクションを連鎖させる非常に基本的な方法の1つは、関数の合成です。

val f:Map[Int,String]=>Map[Int,String] = _ + (1 -> "x")
val g:Map[Int,String]=>Map[Int,String] = _ + (2 -> "y")
val h:Map[Int,String]=>Map[Int,String] = _ + (3 -> "z")

(h compose g compose f)(Map(42->"a"))
// Map[Int,String] = Map((42,a), (1,x), (2,y), (3,z))

ただし、この場合、関数のタイプを簡単に推測できないため、あまり実用的ではありません...

于 2012-06-20T18:38:38.907 に答える
0

それには 2 つの方法が考えられます。文字列をパラメーターとして渡す方法と、マクロで文字列を変更してコンパイルする方法、または単にメソッドをインポートする方法です。Scala に型指定されていないマクロがあれば、それらも使用できるかもしれません。

いずれにせよ、マクロの代替案は他の人に任せます。メソッドのインポートはかなり簡単です。

val map = collection.mutable.Map[String, Int]()
locally {
  import map._
  put("a", 1)
  put("b", 2)
}

のメンバーがインポートさlocallyれるスコープを制限することを除いて、何もしないことに注意してください。map

于 2012-06-20T16:50:50.410 に答える