Scala functional programming gymnastics

后端 未结 13 675
悲&欢浪女
悲&欢浪女 2021-02-01 09:36

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 : D         


        
相关标签:
13条回答
  • 2021-02-01 10:17

    Not prettier, not much shorter, and certainly not faster! But it is more composable more generic and more "functional":

    EDIT: made the code fully generic :)

    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)
    

    UPDATE 2: There's also this version, taking better advantage of 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)
    

    I hope you like type signatures :)

    UPDATE 3: Here's one that does the same with bounds

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

    If nothing else... this shows quite clearly why import parameters would be a Good Thing(tm). Imagine if the following was valid code:

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

    UPDATE 4: Turning the solution upside down here. This one offers some more interesting possibilities for future extension.

    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 
    

    With any luck, I'll inspire someone else to build a better solution out of mine. That's how these things usually work out...

    0 讨论(0)
  • 2021-02-01 10:17

    This isn't really much easier in Scalaz than in regular 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
    

    (Add _ after max and min if it makes you feel better to see where the parameter goes.)

    Scalaz is a little easier to read, though, once you understand what the operators do.

    0 讨论(0)
  • 2021-02-01 10:17

    I'm adding another answer which was inspired by both retronym and Debilski - basically it amounts to converting the cap and floor to functions (Double => Double, if they are present) and then folding the identity function through them with composition:

    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)
    }
    
    0 讨论(0)
  • 2021-02-01 10:19

    Straightforward solution with plain Scala and anonymous lambda, without any mappings, folds, Double.{Min/Max}Value, and so on:

    def restrict(floor : Option[Double], cap : Option[Double], amt : Double) : Double =
      ((x:Double) => x min cap.getOrElse(x))(amt max floor.getOrElse(amt))
    
    0 讨论(0)
  • 2021-02-01 10:21

    Not quite as terse as the scalaz version, but on the other hand, no dependencies,

    List(floor.getOrElse(Double.NegativeInfinity), cap.getOrElse(Double.PositiveInfinity), amt).sorted.apply(1)
    
    0 讨论(0)
  • 2021-02-01 10:26

    This is another way to fix Landei's first answer

    def restrict(floor : Option[Double], cap : Option[Double], amt : Double) : Double = {
      val chopBottom = (floor.getOrElse(amt) max amt) 
      chopBottom min cap.getOrElse(chopBottom)
    }
    
    0 讨论(0)
提交回复
热议问题