6

シーケンスをソートおよび/またはフィルタリングしたい。基本的には次のようなものです:

var result = getReallyLongSeq() // returns Seq[SomeClass]

if (doFilter) {
  result = result.filter( ... )
}

if (doSort) {
  result = result.sortWith( ... )
}

さて、これは明らかに有効なアプローチですが、それを行うためのより機能的な方法はありますか?

4

3 に答える 3

4

より読みやすいかどうかは、議論の余地があります。少し非効率的ですが、純粋に機能的でもあります。このアプローチは拡張も容易で、保守も容易です。

val f: Seq[SomeClass] => Seq[SomeClass] = if(doFilter) _.filter(...) else identity
val s: Seq[SomeClass] => Seq[SomeClass] = if(doSort) _.sortWith(...) else identity
(s compose f)(result)

次のように書くこともできます。これは、OP のコードに似ています。また、わずかに効率的です (ただし、一般的ではありません)。

val filtered = if(doFilter) result.filter(...) else result
if(doSort) filtered.sortWith(...) else filtered

コメントをもとに議論

なんらかの理由で、最初の例でカリー化された関数を使用したい場合 (コメントで述べたように)、次のように記述できます。

def fc(df: Boolean)(xs: Seq[SomeClass]) = if(df) _.filter(...) else identity
def sc(ds: Boolean)(xs: Seq[SomeClass]) = if(ds) _.sortWith(...) else identity
(sc(doSort) compose fc(doFilter))(result)

しかし、さらに次のように書くと、最初の例で示したのとほぼ同じ結果になる可能性があります。

def fc(df: Boolean)(xs: Seq[SomeClass]) = if(df) _.filter(...) else identity
def sc(ds: Boolean)(xs: Seq[SomeClass]) = if(ds) _.sortWith(...) else identity
val f = fc(doFilter)
val s = sc(doSort)
(s compose f)(result)
于 2012-10-25T08:20:23.937 に答える
2

scalaz 演算子|>を使用するか、独自に定義できます。

class PipedObject[T](value: T)
{
    def |>[R](f: T => R) = f(value)
}

implicit def toPiped[T](value: T) =  new PipedObject[T](value)

(result |> (r => if (doFilter) r.filter(...) else r) 
        |> (r => if (doSort) r.sortWith(...) else r))
于 2012-10-25T08:49:46.037 に答える
1

ライブラリのサポートがなければ、独自のBoolean Reader Monadをロールアウトできます。

これは機能的で、純粋で、構成可能です。

ブールリーダー

case class BoolConf[A](run: Boolean => A) {
    def apply(b: Boolean) = run(b)
    def map[B](f: A => B): BoolConf[B] = BoolConf(b => f(run(b)))
    def flatMap[B](f: A => BoolConf[B]): BoolConf[B] = BoolConf(b => f(run(b))(b))
}

Boolean => Aここでは、モナド構成を可能にする a のラッパーを作成しました。これは、おそらくすでにscalazなどのライブラリに実装されています。

この場合、私たちはメソッドにのみ関心がありrunますが、他の機会に関心を持つことができます。

構成されたフィルターと並べ替え

次に、フィルタと並べ替えチェックを次のようにラップします。Reader

val mFilter: Seq[SomeClass] => BoolConf[Seq[SomeClass]] = seq => BoolConf(if(_) seq.filter(...) else seq)
val mSort: Seq[SomeClass] => BoolConf[Seq[SomeClass]] = seq => BoolConf(if(_) seq.sortWith(...) else seq)

リフティング

ここで、これらの関数を構成するには、出力がもはや単純ではないため、関数の 1 つを持ち上げSeqて関数内で動作させる必要があります。BoolConf

def lift2Bool[A, B]: (A => B) => (BoolConf[A] => BoolConf[B]) =
    fun => cfg => BoolConf(bool => fun(cfg(bool)))

A => Bこれで、任意の関数をから持ち上げられた関数に変換できるようになりましたBoolConf[A] => BoolConf[B]

作曲

これで、機能的に構成できます。

val filterAndSort = lift2Bool(mSort) compose mFilter
//or the equivalent
val filterAndSort = mFilter andThen lift2Bool(mSort)
//applies as in filterAndSort(<sequence>)(<do filter>)(<do sort>)

もっとあります

との一般的な「ビルダー」を作成することもできmFilterますmSort

val configFilter[SomeClass]: (SomeClass => Boolean) => Seq[MyClass] => BoolConf[Seq[SomeClass]] = 
    filterer => seq => BoolConf(if(_) seq.filter(filterer))

ソートに相当するものを自分で「ソート」できます

インスピレーションを与えてくれたRunarに感謝します

于 2012-10-25T14:47:59.273 に答える