0

对于我正在从事的项目,我想创建一个代表资源数量的类。将来它可能会成为不同资源类型计数的集合(因此不将 Resources 类本身编码为 Value 类),但现在单个(匿名)资源类型就足够了。

但是,此资源计数不应为负数,因此我想将其可以具有的值限制为自然数集(即非负整数)。为此,我正在考虑创建一个名为 Nat 的新值类。

我想要实现的几个语义点:

  • 如果您尝试从负值创建 Nat,您应该会抛出异常。
  • 如果您尝试将 Int(或另一个 Nat)添加到现有 Nat,它应该可以工作,如果传入的 int 是一个足够大的负数,则将值截断为零 - 不会抛出异常!

这意味着除了之外def +(nat: Nat),我还想要某种形式的def +(int: Int),否则传递给的 Int+将首先转换为 Nat,这可能会导致异常。但是,因为 Nat 是一个 Value 类,所以这两个方法在擦除后将具有相同的签名,所以这不起作用。

我也尝试过def +(int: RichInt),希望隐式转换优先,但 RichInt 也是一个 Value 类,因此会出现同样的问题。

我确实发现的一种解决方法是使用混入 RichInt 的特性之一,特别是 OrderedProxy。现在 Int 将被隐式转换为 RichInt 并作为 OrderedProxy 传递给此方法(在这种形式下,它不被识别为 Value 类),而不是被转换为 Nat,我得到了我想要的语义。

因此,到目前为止,我的代码如下所示:

import runtime.{IntegralProxy, OrderedProxy}

class Nat private(val self: Int) extends AnyVal with IntegralProxy[Int]
{
  protected def num = scala.math.Numeric.IntIsIntegral
  protected def ord = scala.math.Ordering.Int

  import Nat._
  def isZero = (this == Zero)
  def +(nat: Nat): Nat = Nat(self + nat.self)
  def +(int: OrderedProxy[Int]): Nat = trunc(self + int.self)
  def -(nat: Nat): Nat = trunc(self - nat.self)
  def -(int: OrderedProxy[Int]): Nat = trunc(self - int.self)
  def -%(nat: Nat) = (this - nat).self match { // Create a tuple with the reduced count of the minuend, plus any remainder from the subtrahend if the minuend is now zero.
    case 0 => (Zero, (nat - this))
    case nonZero => (Nat(nonZero), Zero)
  }
}

object Nat
{
  val NEG_PARAM_MSG = "Cannot assign negative value"

  val Zero: Nat = Nat(0)

  def apply(value: Int): Nat = value match {
      case cnt if (cnt < 0) => throw new RuntimeException(NEG_PARAM_MSG)
      case 0 => Zero
      case cnt => new Nat(cnt)
    }

  def apply(value: Long): Nat = apply(value.toInt)

  def trunc(value: Int): Nat = value match {
      case cnt if (cnt <= 0) => Zero
      case cnt => new Nat(cnt)
    }

  def trunc(value: Long): Nat = trunc(value.toInt)
}

trait ResourcesComponent
{
  import Nat._

  sealed case class Resources(count: Nat)
  {
    import Resources._

    require(count != Zero || hasNone)

    def hasNone = (this == none)
    def +(res: Resources) = Resources(count + res.count)
    def -(res: Resources) = Resources(count - res.count)
    def -%(res: Resources) = (count - res.count).self match { // Similar to -% for Nat, but convert to a tuple of Resources - is there a better (eg. '.map'-like) way to do this?
      case 0 => (none, Resources(res.count - count))
      case leftOver => (Resources(leftOver), none)
    }
  }

  object Resources
  {
    val NEG_RES_MSG = "Cannot assign negative resources"

    def apply(value: OrderedProxy[Int]) = value.self match {
        case cnt if (cnt < 0) => throw new RuntimeException(NEG_RES_MSG)
        case 0 => none
        case cnt => new Resources(Nat(cnt))
      }

    object none extends Resources(Zero)
    {
      override def hasNone = true
      override def +(res: Resources) = res
      override def -(res: Resources) = none
      override def -%(res: Resources) = (none, res)
    }
  }
}

正如我所说,它似乎有效,但周围的工作感觉有点笨拙。有什么改进的建议吗?

4

1 回答 1

1

The solution is simple: also truncate to zero when constructing Nats from negative integers. Apart from being simpler, the solution will be more consistent. I don't see why aNat + -1 would work differently than aNat + Nat(-1) (including the case of both throwing the same exception). In fact, the programming language itself is telling you that this consistency problem exists, by forcing you into a complex, unnatural construct.

If you really want to make this difference between Ints and Nats, then don't try to trick the language (and other developers!). Be honest with it and define a completely different operator for Ints. Not just an overload. Suggested name: safeAdd, intAdd, or similar.

于 2013-08-16T06:13:58.720 回答