斯卡拉功能性编程体操 [英] Scala functional programming gymnastics
问题描述
我试图尽可能少的代码和尽可能的功能做到以下几点:
def restrict(floor :Option [Double],cap:Option [Double],amt:Double):Double
显然,以下工作:
=(floor - > cap)match {
case(None,None)=> amt
case(Some(f),None)=>最大
的情况(无,一些(c))=> (某些(f),某些(c))=> (f max amt)min c
}
我真的希望有更优雅的东西,将接受使用 Scalaz 库! :
forall(_> f))
如果任何人有兴趣,这里是一些测试代码:
object比较{
密封特质Cf {
def restrict(floor:Option [Double],cap:Option [Double],amt:Double):Double
}
def main(args:Array [String ]){
val cf:Cf = // TODO - 你的impl在这里!
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 ==一些(3),一些(5),2,3)
跑步测试(一些(3),一些(5),3,3)
runtest(一些(3),一些(5),4,4)
runtest(一些(3),一些(5),5,5)
runtest(一些(3),一些(5),6,5)
runtest(一些(3),无,2,3)
runtest(一些(3), (一些(3),无,4,4)
runtest ,无,6,6)
跑步测试(无,一些(5),2,2)
跑步测试(无,一些(5),3,3)
跑步测试(无,一些(5),4,4)
runtest(无,一些(5),5,5)
runtest(无,一些(5),6,5)
runtest(None,None,2,2)
runtest(None,None,3,3)
runtest(None,No
runtest(None,None,5,5)
runtest(None,None,6,6)
}
}
编辑2 :
在考虑 cataX
方法时,我发现
所以,这里是:
((amt /:floor)(_ max _)/:cap)(_ min _)
这与
cap.foldLeft(floor.foldLeft(amt)( _ _ _ _ _ _ _ _ _ _ _ _ _ $ _ $>
(这不一定容易理解)。
我认为你不能比这更短。
无论好还是坏,我们都可以使用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 |>函数
转换为函数(值)
,因此 amt |> (m => v fun m)
等于 v fun amt
。
opt.cata(fun,v)
转换为
opt match {
case Some(value)=>有趣(价值)
案例无=> v
}
或 opt.map(fun).getOrElse (v)
。
一个更对称的解决方案是:
amt |> (m => floor.cata(m max,m))|> (m => cap.cata(m min,m))
编辑:对不起,现在越来越奇怪了,但我也想要一个免费的版本。新的 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)
o
匹配一些
我们返回值为 o
和第二个参数,如果 o
匹配 None
我们只返回第二个参数。
我们继续:
amt |> floor.cataX(_max _)|> cap.cataX(_ min _)
也许他们已经在Scalaz中有这个...?
I am trying to do the following in as little code as possible and as functionally as possible:
def restrict(floor : Option[Double], cap : Option[Double], amt : Double) : Double
Obviously the following works:
= (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
}
I was really hoping for something more elegant and will accept use of the Scalaz library! You can assume that the following is true:
floor.forall( f => cap.forall( _ > f))
If anyone is interested, here is some test code:
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)
}
}
解决方案 Edit 2:
While thinking about the cataX
method, I figured out that cataX
is nothing else than a plain and simple fold. Using that, we can get a pure scala solution without any additional libraries.
So, here it is:
( (amt /: floor)(_ max _) /: cap)(_ min _)
which is the same as
cap.foldLeft( floor.foldLeft(amt)(_ max _) )(_ min _)
(not that this is necessarily easier to understand).
I think you can’t have it any shorter than that.
For better or worse, we can also solve it using scalaz:
floor.map(amt max).getOrElse(amt) |> (m => cap.map(m min).getOrElse(m))
or even:
floor.cata(amt max, amt) |> (m => cap.cata(m min, m))
As a ‘normal’ Scala programmer, one might not know about the special Scalaz operators and methods used (|>
and Option.cata
). They work as follows:
value |> function
translates to function(value)
and thus amt |> (m => v fun m)
is equal to v fun amt
.
opt.cata(fun, v)
translates to
opt match {
case Some(value) => fun(value)
case None => v
}
or opt.map(fun).getOrElse(v)
.
See the Scalaz definitions for cata
and |>
.
A more symmetric solution would be:
amt |> (m => floor.cata(m max, m)) |> (m => cap.cata(m min, m))
Edit: Sorry, it’s getting weird now, but I wanted to have a point-free version as well. The new cataX
is curried. The first parameter takes a binary function; the second is a value.
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)
If o
matches Some
we return the function with the value of o
and the second parameter applied, if o
matches None
we only return the second parameter.
And here we go:
amt |> floor.cataX(_ max _) |> cap.cataX(_ min _)
Maybe they already have this in Scalaz…?
这篇关于斯卡拉功能性编程体操的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!