【问题标题】:Scala class wrapping a partially applied constructor - how to use it to create API methods?包装部分应用的构造函数的 Scala 类 - 如何使用它来创建 API 方法?
【发布时间】:2013-08-15 09:56:52
【问题描述】:

我正在尝试创建一个简单的 api 来处理时间间隔。 (我知道joda time,我不想重新发明它。这是一个练习)。

我想要实现的是:

(1)

assert(from("20:30").to("20:50") == Interval("20:30", "20:50") )
//same thing, but without implicit defs
assert(from(Time(20, 30)).to(Time(20, 50)) == Interval(Time(20, 30), Time(20, 50)))


(2)
assert(from("20:30").forMinutes(10) == from("20:30").to("20:40"))


我已经设法实现(1),如下所示:(忽略toString,Ordered trait,a.s.o)

case class Time(hour: Int, minute: Int)

case class Interval(start: Time, end: Time)

object Interval {
   case class HalfInterval(half: Time => Interval) {
      def to(time: Time): Interval = half(time)
      def forMinutes(minutes: Int): Interval = ??? 
    }
   def from(start: Time): HalfInterval = HalfInterval(Interval(start, _))
}

object Time {
  def apply(hourMinute: String): Time = {
    val tries = hourMinute.split(":").map(s => Try(s.toInt))
    tries match {
      case Array(Success(hour), Success(minute)) => Time(hour, minute)
      case _ => throw new IllegalArgumentException
    }
  }
  implicit def stringToTime(hourMinute: String) = Time(hourMinute)
}


但是,我不知道如何实现(2)(即:Interval.forMinutes)。

def forMinutes(minutes: Int): Interval = {
  val time = ?? // Based on what Time object could I construct a new Time object here?
  half(time)
}

我似乎无法理解这一点。
Time => Interval 上的这个“HalfInterval”包装器是否有意义?
我根据经验设计了它——只是为了让from(..).to(..) 调用按计划工作——而不是考虑到一些功能概念模型。
有没有更好的方法来实现这个api?

谢谢

【问题讨论】:

    标签: scala functional-programming closures implicits function-literal


    【解决方案1】:

    这就是我会做的:

    object Interval {
       case class HalfInterval(start: Time) {
          def to(end: Time): Interval = Interval(start, end)
          def forMinutes(minutes: Int): Interval = to(getEnd(start, minutes))
          private def getEnd(start: Time, minutes: Int) = ??? 
        }
       def from(start: Time): HalfInterval = HalfInterval(start)
    }
    

    getEnd() 将分钟参数添加到 start.minute,除以 60,将结果添加到 start.hours,其余部分添加到分钟,然后您构建结束时间。 (如果你去第二天,可能会做小时模数 24)。

    编辑:HalfInterval 应该是值类,但不要担心。

    【讨论】:

      【解决方案2】:

      我同意 Luciano 的观点,即您的 HalfInterval 有点开箱即用。

      但由于工程师喜欢开箱即用的东西,你必须先组装它才能使用它,这是另一个角度。

      你的HalfInterval 说,给我一个Interval 工厂,我会给你证明Interval 是必需的。

      这里我将参数类型更改为 Any,但在这种情况下它可能是 Either[Int, Time],其中 int 表示未来的分钟数,时间是结束时间。更一般地说,它可能是一种标记性状,比 Any 更安全。

      要回答您的问题,开始时间从何而来?您会看到from 中的闭包捕获了start

      import scala.language.implicitConversions
      import util._
      
      case class Time(hour: Int, minute: Int) {
        def +(delta: Int) = Time(hour, minute + delta) // TODO overflow minutes
      }
      
      case class Interval(start: Time, end: Time)
      
      object Interval {
         case class HalfInterval(half: Any => Interval) {
            def to(time: Time): Interval = half(time)
            def forMinutes(minutes: Int): Interval = half(minutes)
          }
         def from(start: Time) = HalfInterval((arg: Any) => arg match {
           case delta: Int => Interval(start, start + delta)
           case end: Time  => Interval(start, end)
           case _          => throw new IllegalArgumentException
         })
      }
      
      object Time {
        def apply(hourMinute: String): Time = {
          val tries = hourMinute.split(":").map(s => Try(s.toInt))
          tries match {
            case Array(Success(hour), Success(minute)) => Time(hour, minute)
            case _ => throw new IllegalArgumentException
          }
        }
        implicit def stringToTime(hourMinute: String) = Time(hourMinute)
      }
      
      object Test extends App {
        import Interval._
        assert(from("20:30").to("20:50") == Interval("20:30", "20:50") )
        assert(from("20:30").forMinutes(10) == from("20:30").to("20:40"))
      }
      

      一个倒置的公式,其中签名更奇怪,但代码更有意义:

      object Interval {
         case class HalfInterval(f: (Time=>Time) => Interval) {
            def to(end: Time): Interval = f(_ => end)
            def forMinutes(minutes: Int): Interval = f(_ + minutes)
          }
         def from(start: Time) = HalfInterval((end: Time=>Time) => Interval(start, end(start)))
      }
      

      toforMinutes 都知道如何根据开始时间确定结束时间。

      这使得添加例如更容易until(end: Time), forSeconds(secs: Int).

      【讨论】:

      • 这太好了,谢谢。让我明白我在 forMinutes 方法中哪里出了问题。
      猜你喜欢
      • 1970-01-01
      • 2019-05-22
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-15
      相关资源
      最近更新 更多