1

我正在尝试创建一个简单的 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,aso)

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

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

谢谢

4

2 回答 2

3

这就是我要做的:

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 应该是值类,但不要担心。

于 2013-08-13T17:31:50.953 回答
1

我同意卢西亚诺的观点,即你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)))
}

两者都to知道forMinutes如何从开始时间确定结束时间。

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

于 2013-08-14T05:41:10.813 回答