10

可能な限り少ないコードで、可能な限り機能的に次のことを実行しようとしています。

def restrict(floor : Option[Double], cap : Option[Double], amt : Double) : Double

明らかに、次のように機能します。

= (floor -> cap) match {
    case (None, None)       => amt
    case (Some(f), None)    => f max amt 
    case (None, Some(c))     => c min amt
    case (Some(f), Some(c)) => (f max amt) min c
  }

私は本当にもっとエレガントなものを望んでいて、Scalazライブラリの使用を受け入れます! 次のことが true であると想定できます

floor.forall( f => cap.forall( _ > f))

誰かが興味を持っている場合は、ここにいくつかのテストコードがあります:

object Comparisons {
  sealed trait Cf {
    def restrict(floor: Option[Double], cap: Option[Double], amt: Double): Double
  }

  def main(args: Array[String]) {
    val cf : Cf = //TODO - your impl here!
    def runtest(floor: Option[Double], cap: Option[Double], amt: Double, exp : Double) : Unit = {
      val ans = cf.restrict(floor, cap, amt)
      println("floor=%s, cap=%s, amt=%s === %s (%s) : %s".format(floor, cap, amt, ans, exp, if (ans == exp) "PASSED" else "FAILED"))
    }
    runtest(Some(3), Some(5), 2, 3)
    runtest(Some(3), Some(5), 3, 3)
    runtest(Some(3), Some(5), 4, 4)
    runtest(Some(3), Some(5), 5, 5)
    runtest(Some(3), Some(5), 6, 5)

    runtest(Some(3), None, 2, 3)
    runtest(Some(3), None, 3, 3)
    runtest(Some(3), None, 4, 4)
    runtest(Some(3), None, 5, 5)
    runtest(Some(3), None, 6, 6)

    runtest(None, Some(5), 2, 2)
    runtest(None, Some(5), 3, 3)
    runtest(None, Some(5), 4, 4)
    runtest(None, Some(5), 5, 5)
    runtest(None, Some(5), 6, 5)

    runtest(None, None, 2, 2)
    runtest(None, None, 3, 3)
    runtest(None, None, 4, 4)
    runtest(None, None, 5, 5)
    runtest(None, None, 6, 6)
  }
}
4

13 に答える 13

16

編集2

cataX方法を考えているうちに、それは単純な折り方に他ならないことがcataXわかりました。それを使用して、追加のライブラリなしで純粋な scala ソリューションを取得できます。

だから、ここにあります:

( (amt /: floor)(_ max _) /: cap)(_ min _)

これはと同じです

cap.foldLeft( floor.foldLeft(amt)(_ max _) )(_ min _)

(これが必ずしも理解しやすいというわけではありません)。

これ以上短くすることはできないと思います。


良くも悪くも、scalaz を使って解決することもできます:

floor.map(amt max).getOrElse(amt) |> (m => cap.map(m min).getOrElse(m))

あるいは:

floor.cata(amt max, amt) |> (m => cap.cata(m min, m))

「通常の」Scala プログラマーとして、使用される特別な Scalaz 演算子とメソッド (|>およびOption.cata) について知らないかもしれません。それらは次のように機能します。

value |> functionに変換されるためfunction(value)amt |> (m => v fun m)に等しくなりv fun amtます。

opt.cata(fun, v)に変換します

opt match {
  case Some(value) => fun(value) 
  case None => v
}

またはopt.map(fun).getOrElse(v)

cataとの Scalaz 定義を参照してください|>

より対称的なソリューションは次のようになります。

amt |> (m => floor.cata(m max, m)) |> (m => cap.cata(m min, m))

編集:申し訳ありませんが、今はおかしくなっていますが、ポイントフリーバージョンも欲しかった. 新作cataXはカレー。最初のパラメーターは二項関数を取ります。2 番目は値です。

class CataOption[T](o: Option[T]) {
  def cataX(fun: ((T, T) => T))(v: T) = o.cata(m => fun(m, v), v)
}
implicit def option2CataOption[T](o: Option[T]) = new CataOption[T](o)

o一致する場合Someは、 の値oと 2 番目のパラメーターが適用された関数を返します。o一致Noneする場合は、2 番目のパラメーターのみを返します。

さあ、いくぞ:

amt |> floor.cataX(_ max _) |> cap.cataX(_ min _)

たぶん、彼らはすでに Scalaz にこれを持っています…?

于 2010-11-10T15:04:56.363 に答える
15

scalaz バージョンほど簡潔ではありませんが、依存関係はありません。

List(floor.getOrElse(Double.NegativeInfinity), cap.getOrElse(Double.PositiveInfinity), amt).sorted.apply(1)
于 2010-11-10T15:38:29.450 に答える
5
于 2010-11-11T21:51:33.340 に答える
5

I'll start with this:

def restrict(floor : Option[Double], cap : Option[Double], amt : Double) : Double = {
  val flooring = floor.map(f => (_: Double) max f).getOrElse(identity[Double] _)       
  val capping  = cap.map(f => (_: Double) min f).getOrElse(identity[Double] _)         
  (flooring andThen capping)(amt)                                                      
}                                                                                    

But I have the feeling I'm missing some opportunity here, so I may not be finished.

于 2010-11-10T15:37:15.110 に答える
4

これはどう?

//WRONG
def restrict(floor : Option[Double], cap : Option[Double], amt : Double) : Double = 
   (floor.getOrElse(amt) max amt) min cap.getOrElse(amt)

[編集]

2 回目の試行:

def restrict(floor : Option[Double], cap : Option[Double], amt : Double) : Double = 
   floor.map(f => f max _).getOrElse(identity[Double] _)(
     cap.map(c => c min _).getOrElse(identity[Double] _)(amt))

私の好みには少し「ぎこちない」ように見えますが、テストには合格します:-)

【2回目の編集】

最初のバージョンも「修復」できます。

def restrict(floor: Option[Double], cap: Option[Double], amt: Double): Double =
  (floor.getOrElse(-Double.MaxValue) max amt) min cap.getOrElse(Double.MaxValue)
于 2010-11-10T14:44:20.913 に答える
4

きれいでもなく、短くもなく、確かに速くもありません! しかし、より一般的でより「機能的」で、より構成可能です。

編集:コードを完全に汎用的にしました:)

def optWith[T](a: Option[T], b: T)(op:(T,T)=>T) =
  a map (op(b,_)) getOrElse b

def optMin[T:Numeric](a: Option[T]) =
  (b:T) => optWith(a, b)(implicitly[Numeric[T]].min)

def optMax[T:Numeric](a: Option[T]) =
  (b:T) => optWith(a, b)(implicitly[Numeric[T]].max)

def restrict[T,FT,CT](x:T, floor:Option[FT], ceil:Option[CT])
  (implicit ev:Numeric[T], fv:FT=>T, cv:CT=>T) =
  optMin(ceil map cv) compose optMax(floor map fv) apply(x)

更新 2 : このバージョンもあり、Numeric

def optWith[T](a: Option[T])(op:(T,T)=>T) =
  (b:T) => a map (op(b,_)) getOrElse b

def restrict[T,FT,CT](x:T, floor:Option[FT], ceil:Option[CT])
  (implicit n:Numeric[T], fv:FT=>T, cv:CT=>T) =
  optWith(ceil map cv)(n.min) compose optWith(floor map fv)(n.max) apply(x)

型シグネチャを気に入っていただければ幸いです :)

UPDATE 3 :境界で同じことを行うものを次に示します

def optWith[T, V <% T](op:(T,T)=>T)(a: Option[V]) =
  (b:T) => a map (op(b,_)) getOrElse b

def restrict[T:Numeric, FT <% T, CT <% T]
(floor:Option[FT], ceil:Option[CT], amt:T) = {
  val n = implicitly[Numeric[T]]; import n._
  optWith(min)(ceil) compose
  optWith(max)(floor) apply(amt)
}

他に何もないとしても...これは、インポートパラメータが良いこと(tm)である理由を非常に明確に示しています。以下が有効なコードであると想像してください。

def optWith[T, V <% T](op:(T,T)=>T)(a: Option[V]) =
  (b:T) => a map (op(b,_)) getOrElse b

def restrict[import T:Numeric,FT <% T,CT <% T]
(floor:Option[FT], ceil:Option[CT], amt:T) = {
  optWith(min)(ceil) compose
  optWith(max)(floor) apply(amt)
}

更新 4 : ここでソリューションを逆さまにします。これは、将来の拡張のためのいくつかの興味深い可能性を提供します。

implicit def optRhs[T:Ordering](lhs:T) = new Object {
  val ord = implicitly[Ordering[T]]; import ord._

  private def opt(op: (T,T)=>T)(rhs:Option[T]) =
    rhs map (op(lhs,_)) getOrElse lhs

  def max = opt(ord.max) _
  def min = opt(ord.min) _
}

def restrict[T : Ordering](floor:Option[T], cap:Option[T], amt:T) =
  amt min cap max floor 

運が良ければ、他の誰かが私からより良いソリューションを構築するように促します. それが、これらのことが通常うまくいく方法です...

于 2010-11-10T15:03:30.107 に答える
2

これは、Scalazでは通常のScalaよりもそれほど簡単ではありません。

def restrict(floor: Option[Double], cap: Option[Double], amt: Double) =
  floor.map(amt max).orElse(Some(amt)).map(x => cap.map(x min).getOrElse(x)).get

_maxに追加minし、パラメーターがどこに行くのかがわかりやすくなる場合は追加します。)

ただし、演​​算子の機能を理解すれば、Scalazは少し読みやすくなります。

于 2010-11-10T19:20:39.010 に答える
2

質問Optionでオプションのパラメーターを示すためにを使用するように求められた場合、通常、欠落しているパラメーターを表すためのより自然な方法があることがわかりました。そこで、ここでインターフェイスを少し変更し、デフォルトの引数を使用して関数を定義し、名前付きパラメーターを使用して関数を呼び出します。

def restrict(amt:Double,
            floor:Double = Double.NegativeInfinity,
            cap:Double=Double.PositiveInfinity):Double =
    (amt min cap) max floor

次に、電話をかけることができます:

restrict(6)
restrict(6, floor = 7)
restrict(6, cap = 5)

同じ原理の別の例。

于 2010-11-14T04:25:49.887 に答える
2

これはケンブルームの答えに基づいています

sealed trait Constrainer { def constrain(d : Double) : Double }

trait Cap extends Constrainer
trait Floor extends Constrainer
case object NoCap extends Cap { def constrain(d : Double) = d }
case object NoFloor extends Floor { def constrain(d : Double) = d }
implicit def d2cap(d : Double) = new Cap { def constrain(amt : Double) = d min amt }
implicit def d2floor(d : Double) = new Floor { def constrain(amt : Double) = d max amt }

def restrict(amt : Double, cap : Cap = NoCap, floor: Floor = NoFloor) : Double = {
  cap.constrain(floor.constrain(amt))
  //or (cap.constrain andThen floor.constrain) amt
}

最終的には次のようなコードを記述します。

restrict(amt, cap = 5D)
restrict(amt, floor = 0D)

私はそれがかなり素晴らしいと思います、そしてそれがハックであるということであるケンの解決策(私の意見では)の問題に悩まされていません!

于 2010-11-14T18:17:38.763 に答える
1

これは、ランデイの最初の答えを修正する別の方法です

def restrict(floor : Option[Double], cap : Option[Double], amt : Double) : Double = {
  val chopBottom = (floor.getOrElse(amt) max amt) 
  chopBottom min cap.getOrElse(chopBottom)
}
于 2010-11-14T04:15:36.560 に答える
0

マッピング、フォールド、Double。{Min / Max} Valueなどを使用せずに、プレーンなScalaと匿名ラムダを使用した簡単なソリューション:

def restrict(floor : Option[Double], cap : Option[Double], amt : Double) : Double =
  ((x:Double) => x min cap.getOrElse(x))(amt max floor.getOrElse(amt))
于 2011-04-17T21:12:40.933 に答える
0

retronymDebilskiの両方に触発された別の回答を追加します。基本的には、 cap と floor を関数 (Double => Double存在する場合は ) に変換し、それらを介して恒等関数を構成で折り畳むことになります。

def restrict(floor: Option[Double], cap: Option[Double], amt: Double) = {
  (identity[Double] _ /: List(floor.map(f => (_: Double) max f), cap.map(c => (_: Double) min c)).flatten){ _ andThen _ }(amt)
}
于 2010-11-15T08:34:50.853 に答える
0

amt私はマッチケースを使った最初の解決策が一番好きです - 事実を除けば、その意味が理解できずamount(ドイツでは「amt」は「オフィス」を意味します) cap、頭にかぶるものとしてしか知りませんでした ...

ここで、内部メソッドを使用した、まったく刺激のないソリューションを次に示します。

def restrict(floor : Option[Double], cap : Option[Double], amt : Double) : Double = {
  def restrict (floor: Double, cap: Double, amt: Double) =
    (floor max amt) min cap
  var f = floor.getOrElse (amt)            
  val c = cap.getOrElse (amt)   
  restrict (f, c, amt) 
}
于 2011-08-06T03:11:21.217 に答える