2011-12-16 12 views
7

私はGroovyから来て、単一引数のクロージャを受け入れるすべての型に対して.withメソッドを持っています。引数は、.withメソッドが呼び出されているオブジェクトです。これにより、関数型連鎖機能を拡張する非常にクールな手法が可能になります。これにより、一時変数の導入義務を解消し、コードを考慮して読みやすくなり、その他の細かいことが可能になります。 .with.foreachにちょっと似ていますが、代わりにスルー反復の代わりに、最初の例では、言い換えれば.with alternative in scala

val yetAnotherMeaninglessNameForTemporaryVariable = 
    Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) 
if (!yetAnotherMeaninglessNameForTemporaryVariable.isEmpty) 
    println(yetAnotherMeaninglessNameForTemporaryVariable) 

Seq(1, 2, 3, 4, 5) 
    .filter(_ % 2 == 0) 
    .with(it => if (!it.isEmpty) println(it)) 

私はこのような何かを行うことができるようにしたいですそれはオブジェクト自体の上で一度呼び出されているオブジェクトの項目です。従ってitSeq(1, 2, 3, 4, 5).filter(_ % 2 == 0)に等しい。

私はScalaではそのようなものを見つけることではない非常に驚いたので、私の質問は以下のとおりです。

  • 私は何かが足りないのですか?
  • Scala固有の代替テクニックはありますか?
  • もしそうでなければ、この機能がScalaに実装されていない理由はありますか?

更新:https://issues.scala-lang.org/browse/SI-5324 適切な機能要求は、Scalaの課題追跡に掲載されています。投票して宣伝してください

+1

ちょうどメモ: 'with'はScalaの予約語なので、メソッドを同じ名前にすることはできません。それはまだ他の名前の下に存在するはずです。これはStackOverflow上で最も一般的なScalaの質問と回答です(私はそれが存在しない、あなたのようにこのようにしています)。 –

+0

私は 'convert'という名前が最適だと思うので、このメソッドは副作用がなく、呼び出し元をパラメータとして受け取り、新しい何かを返すので、ある種の変換でなければならないことを示唆しています。その意味で、この関数は標準ライブラリでは置き換えられません。また、http://stackoverflow.com/a/8538277/485115で提案されているように、呼び出し元オブジェクトを返す 'tap'という名前の副作用バリアントも存在するはずです。 –

答えて

12

標準ライブラリにはこのようなメソッドはありませんが、独自のメソッドを定義するのは難しくありません。

implicit def aW[A](a: A) = new AW(a) 
class AW[A](a: A) { 
    def tap[U](f: A => U): A = { 
    f(a) 
    a 
    } 
} 

val seq = Seq(2, 3, 11). 
      map(_ * 3).tap(x => println("After mapping: " + x)). 
      filter(_ % 2 != 0).tap(x => println("After filtering: " + x)) 

EDIT:(コメントへの応答で)

ああ、私は誤解。あなたが必要とするものはScalazのライブラリにあります。それは名前|>の下にあります(パイプオペレータと呼ばれます)。あなたがScalazを使用できない場合は、あなた自身で、あなたは演算子を定義することができます

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) |> { it => if(!it.isEmpty) println(it) } 

:それを使用すると、あなたの例は、以下に示すようになり

implicit def aW[A](a: A) = new AW(a) 
class AW[A](a: A) { 
    def |>[B](f: A => B): B = f(a) 
} 

そして、それは有用な方法をヒモにする悪い習慣はありません既存のタイプに追加する必要があります。あなたは暗黙のコンバージョンを控えめに使うべきですが、私はこれらの2つのコンビネータは、彼らのポンピングが正当化されるのに十分な共通点だと考えています。

+0

はい標準ライブラリを暗黙のうちに拡張することができますが、スタンダードから逸脱していることが分かります。だから私の質問はなぜScalaの人たちがこれを実装していないのかということです。なぜなら、おそらくこれは彼らのissue-trackerに投稿されるべきだからです。 あなたの 'tap'関数の他に、非常に便利ですが、閉鎖の結果の代わりにオブジェクト自体を返すという意味では' with'と少し違っています。 標準ライブラリIMOに '.with'と' .tap'の両方を持つことは非常に涼しいでしょう。 –

+0

私は同じ方法の名前を 'effect'と名づけます。なぜタップすればいいの?私はそれがより短いのが好きですが、ニーモニックのメリットはありません。 –

+3

@NikitaVolkov Oderskyは、中途半端なval宣言を回避するScalaプログラマの習慣を批判しました。パイプ宣言が要求されたときには必ず貢献します。だから今のところ、私はそれが標準ライブラリの一部になるとは思わない。 –

3

のTry STHで説明したように

object Test { 
def main(args: Array[String]) { 
    delayed(time()); 
} 

def time() = { 
    println("Getting time in nano seconds") 
    System.nanoTime 
} 

def delayed(t: => Long) = { 
    println("In delayed method") 
    println("Param: " + t) 
    t 
} 
} 

:多分それはあなたが望む能力を備えています。

println(Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).ensuring(!_.isEmpty)) 

条件が満たされない場合、アサーション例外がスローされます。

+0

+1ですが、単一ステートメントの実行(例:ここではprintln)でのみ動作します。複数の操作を実行する場合は、操作が面倒になります。 – aishwarya

+0

他の目的のための素晴らしいテクニック。このために例外を投げることは過剰です。 –

7

Scalaで含まれ、このパターンのためのいくつかの構文があります:あなたは多分それを使用して(AB)を控える必要がありますので、

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) match { case it => if (!it.isEmpty) println(it) } 

はしかし、これは一般に認められたイディオムではありません。

あなたが考案負荷とダミー変数の名前の負荷を嫌う場合は、スコープを使用することができることを覚えてはブレース:

val importantResult = { 
    val it = Seq(1,2,3).filter(_ % 2 == 0) 
    if (!it.isEmpty) println(it) 
    it 
} 

val otherImportantResultWithASpeakingVariableName = { 
    val it = // ... 
    /* ... */ 
    it 
} 
+0

マッチで素晴らしい提案!少し荒いですが、私の質問の最初の2つに答えます。 –

1

私はより良い他のソリューションを好むが、(彼らはより多くの地域のため、従うことが容易であるため)あなたの無意味な変数の名前の衝突を回避し、それらを適切な範囲に制限し続けるために、あなたができることを忘れないでください。

{ val x = Seq(1,2,3,4,5).filter(_ % 2 == 0); println(x); x } 

1

これは単にアプリケーションf(x)は、その頭の上にひっくり返し機能:あなたの場合、

(it => if (!it.isEmpty) println(it)) (Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0)) 

を同様に:あなたはそれをアン反転、Scalaでwithを行うための慣用的な方法を探しているなら... x.with(f)g(f(x)) ... g(f(x))を使用します。

+2

:8:エラー:パラメータタイプがありません」 – david