斯卡拉功能性编程体操 [英] Scala functional programming gymnastics

查看:142
本文介绍了斯卡拉功能性编程体操的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图尽可能少的代码和尽可能的功能做到以下几点:

  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 方法时,我发现 cataX 不是别的一个简单而简单的折叠。使用它,我们可以得到一个纯粹的scala解决方案,而无需额外的库。



所以,这里是:

 ((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)



请参阅 cata |>



一个更对称的解决方案是:

  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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆