假设我有一个Pos
类型(位置)。为了获得类型安全性,列/行不表示为Int
,而是表示Col
(列)和Row
:
case class Pos(col: Col, row: Row) {
def +(other: Pos): Pos = Pos(col + other.col, row + other.row)
}
可以添加两个位置,分别包括对行和行的求和。
类型Col
和Row
的定义如下所示:
object Row {
def apply(value: Int) = new Row(value)
val zero = new Row(0)
}
object Col {
def apply(value: Int) = new Col(value)
val zero = new Col(0)
}
class Row(val value: Int) extends AnyVal {
def +(other: Row): Row = Row(this.value + other.value)
}
class Col(val value: Int) extends AnyVal {
def +(other: Col): Col = Col(this.value + other.value)
}
这一切都很好,但我有重复自己的感觉。定义几乎相同。
我可以做些什么来概括它们吗?
答案 0 :(得分:2)
如果你引入Scalaz并为Monoid
和Row
创建Col
个实例,你可能不会减少你的样板,但它会缩短你的零定义并附加一些:
case class Col(i: Int) extends AnyVal
case class Row(i: Int) extends AnyVal
implicit object rowMonoid extends Monoid[Row] {
def zero = Row(0)
def append(a: Row, b: => Row) = Row(a.i |+| b.i)
}
implicit object colMonoid extends Monoid[Col] {
def zero = Col(0)
def append(a: Col, b: => Col) = Col(a.i |+| b.i)
}
并且Monoids
是可组合的,因此如果您将Rows
和Cols
存储在地图或元组等中,则可以只编写它们,而不会触及单个元素:< / p>
val pt1 = (Row(4), Col(15))
val pt2 = (Row(14), Col(5))
val res = pt1 |+| pt2
println(res) // (Row(18),Col(20))
我认为,如果使用并经常添加Row
和Col
,我认为简化用法可以为您节省更多代码而不是担心缩减定义。
答案 1 :(得分:1)
您可以为Both Row和Col类定义一个共同特征:
trait Element {
val value : Int
def init(value: Int): Element
def +(other: Element) = init(value + other.value)
}
然后使用案例类,以便您利用伴随对象的apply方法:
case class Row(value: Int) extends Element {
def init(v: Int) = Row(v)
}
case class Col(value: Int) extends Element {
def init(v: Int) = Col(v)
}
所以现在你可以像这样添加它们:
case class Pos(col: Element, row: Element) {
def +(other: Pos): Pos = Pos(col + other.col, row + other.row)
}
val p1 = Pos(Col(1), Row(2))
val p2 = Pos(Col(1), Row(2))
p1 + p2 //res2: Pos = Pos(Col(2),Row(4))
但是,这允许创建仅包含行的位置
val p3 = Pos(Row(2), Row(3))
p1 + p3 //res3: Pos = Pos(Col(3),Row(5))
因此,第二步是绑定您的Element
类型的+
方法。
trait Element[T <: Element[_]] {
val value : Int
def init(value: Int): Element[T]
def +(other: Element[T]) = init(value + other.value)
}
case class Row(value: Int) extends Element[Row] {
def init(v: Int) = Row(v)
}
case class Col(value: Int) extends Element[Col] {
def init(v: Int) = Col(v)
}
case class Pos(col: Element[Col], row: Element[Row]) {
def +(other: Pos): Pos = Pos(col + other.col, row + other.row)
}
您得到的是,现在一行只应添加行类型的元素,而Col应该只添加Col类型的元素。您仍然可以添加两个职位:
val p1 = Pos(Col(1), Row(2))
val p2 = Pos(Col(1), Row(2))
p1 + p2 //res0: Pos = Pos(Col(2),Row(4))
但这不会编译:
val p3 = Pos(Row(2), Row(3))
答案 2 :(得分:0)
您可以在特质中使用类型变量
像这样的东西
trait TableElement{
type T
def +(t:T):T
}