oxb*_*kes 10 functional-programming scala scalaz
我试图在尽可能少的代码和尽可能功能的情况下执行以下操作:
def restrict(floor : Option[Double], cap : Option[Double], amt : Double) : Double
Run Code Online (Sandbox Code Playgroud)
显然以下工作:
= (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
}
Run Code Online (Sandbox Code Playgroud)
我真的希望有更优雅的东西,并且会接受使用Scalaz库!您可以假设以下情况属实:
floor.forall( f => cap.forall( _ > f))
Run Code Online (Sandbox Code Playgroud)
如果有人有兴趣,这里有一些测试代码:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
Deb*_*ski 16
编辑2:
在考虑这种cataX
方法时,我发现这cataX
只不过是一个简单而简单的折叠.使用它,我们可以获得纯scala解决方案,而无需任何其他库.
所以,这里是:
( (amt /: floor)(_ max _) /: cap)(_ min _)
Run Code Online (Sandbox Code Playgroud)
这是一样的
cap.foldLeft( floor.foldLeft(amt)(_ max _) )(_ min _)
Run Code Online (Sandbox Code Playgroud)
(并不是说这一点更容易理解).
我认为你不能比这更短.
无论好坏,我们也可以使用scalaz解决它:
floor.map(amt max).getOrElse(amt) |> (m => cap.map(m min).getOrElse(m))
Run Code Online (Sandbox Code Playgroud)
甚至:
floor.cata(amt max, amt) |> (m => cap.cata(m min, m))
Run Code Online (Sandbox Code Playgroud)
作为一个"普通"的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
}
Run Code Online (Sandbox Code Playgroud)
或opt.map(fun).getOrElse(v)
.
更对称的解决方案是:
amt |> (m => floor.cata(m max, m)) |> (m => cap.cata(m min, m))
Run Code Online (Sandbox Code Playgroud)
编辑:对不起,现在变得很奇怪了,但我想要一个无点版本.新的cataX
是咖喱.第一个参数采用二进制函数; 第二个是价值.
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)
Run Code Online (Sandbox Code Playgroud)
如果o
匹配,Some
我们返回值为o
和应用第二个参数的函数,如果o
匹配,None
我们只返回第二个参数.
现在我们开始:
amt |> floor.cataX(_ max _) |> cap.cataX(_ min _)
Run Code Online (Sandbox Code Playgroud)
也许他们已经在Scalaz中有这个...?
Mil*_*bin 15
不像scalaz版本那么简洁,但另一方面,没有依赖,
List(floor.getOrElse(Double.NegativeInfinity), cap.getOrElse(Double.PositiveInfinity), amt).sorted.apply(1)
Run Code Online (Sandbox Code Playgroud)
我将从这开始:
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)
}
Run Code Online (Sandbox Code Playgroud)
但我觉得我在这里错过了一些机会,所以我可能没有完成.
这不仅仅是为了简洁,而是表明如果你转向cap
和floor
进入功能,组合会变得多么容易.
scala> val min = (scala.math.min _).curried
min: (Int) => (Int) => Int = <function1>
scala> val max = (scala.math.max _).curried
max: (Int) => (Int) => Int = <function1>
scala> def orIdentity[A](a: Option[A])(f: A => A => A): (A => A) = a ? f | identity
orIdentity: [A](a: Option[A])(f: (A) => (A) => A)(A) => A
scala> val cap = 5.some; val floor = 1.some
cap: Option[Int] = Some(5)
floor: Option[Int] = Some(1)
scala> val ffloor = orIdentity(floor)(max)
ffloor: (Int) => Int = <function1>
scala> val fcap = orIdentity(cap)(min)
fcap: (Int) => Int = <function1>
scala> val capAndFloor = fcap ? ffloor
capAndFloor: (Int) => Int = <function1>
scala> (0 to 8).toSeq ? (capAndFloor)
res0: Seq[Int] = Vector(1, 1, 2, 3, 4, 5, 5, 5, 5)
Run Code Online (Sandbox Code Playgroud)
从scalaz,我使用MA#?
的仿函数图,无论是作为使用的方式Option.map
和Function1.andThen
; 并且OptionW#|
这是一个别名Option.getOrElse
.
UPDATE
这就是我要找的东西:
scala> import scalaz._; import Scalaz._
import scalaz._
import Scalaz._
scala> val min = (scala.math.min _).curried
min: (Int) => (Int) => Int = <function1>
scala> val max = (scala.math.max _).curried
max: (Int) => (Int) => Int = <function1>
scala> def foldMapEndo[F[_]: Foldable, A](fa: F[A], f: A => A => A): Endo[A] =
| fa.foldMap[Endo[A]](a => f(a))
foldMapEndo: [F[_],A](fa: F[A],f: (A) => (A) => A)(implicit evidence$1: scalaz.Foldable[F])scalaz.Endo[A]
scala> val cap = 5.some; val floor = 1.some
cap: Option[Int] = Some(5)
floor: Option[Int] = Some(1)
scala> val capAndFloor = List(foldMapEndo(floor, max), foldMapEndo(cap, min)) ?
capAndFloor: scalaz.Endo[Int] = scalaz.Endos$$anon$1@4352d1fc
scala>(0 to 10).toSeq.map(capAndFloor)
res0: Seq[Int] = Vector(1, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5)
Run Code Online (Sandbox Code Playgroud)
scalaz.Endo[A]
是一个包装器A => A
,两个方向都有隐式转换.有一个Monoid
定义的实例Endo[A]
,Monoid#plus
链接函数,并Monoid#zero
返回标识函数.如果我们有一个List
的Endo[A]
,我们可以归纳列表并导致单个值,可以用来作为A => A
.
MA#foldMap
将给定函数映射到Foldable
数据类型,并使用a减少为单个值Monoid
.foldMapEndo
这是一个方便的.这种抽象允许您轻松地从证明Option
s中的cap和floor 到任何可折叠类型,例如a List
.
val capAndFloor = Seq(foldMapEndo(List(1, 2, max), foldMapEndo(cap, min)).collapsee
capAndFloor: scalaz.Endo[Int] = scalaz.Endos$$anon$1@76f40c39
Run Code Online (Sandbox Code Playgroud)
另一种重构可能会导致:
val capAndFloor = Seq((cap, min), (floor, max)).foldMap { case (a, f) => foldMapEndo(a, f) }
capAndFloor: scalaz.Endo[Int] = scalaz.Endos$$anon$1@25b85c8e
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2167 次 |
最近记录: |