Item和Part之间存在一种隐含的关系。至少需要将一个Item分解成Part对象,然后进行重构时需要进行反向操作。
因此,对于"hello": String
,您需要使f("hello")
返回('h': Char, "ello": String)
,并且您需要反向函数g('h', "ello")
返回"hello"
。
因此,只要遵循一些规则,任何具有这两个函数的两种类型都可以使用。我认为这些规则很容易直观理解。这更或多或少是如何使用head
和tail
递归地分解列表并使用::
重新构建它。
您可以使用上下文绑定为通常的类型提供这些函数。
(编辑)
实际上,我无法真正使用上下文绑定,因为有两个类型参数,但这就是我想要的:
trait R[Item, Part] {
def decompose(item: Item): (Part, Item)
def recompose(item: Item, part: Part): Item
def empty: Item
}
class NotATrie[Item, Part](item: Item)(implicit rel: R[Item, Part]) {
val brokenUp = {
def f(i: Item, acc: List[Part] = Nil): List[Part] = {
if (i == rel.empty) acc
else {
val (head, tail) = rel.decompose(i)
f(tail, head :: acc)
}
}
f(item)
}
def rebuilt = (rel.empty /: brokenUp)( (acc, el) => rel.recompose(acc, el) )
}
然后我们提供一些隐式对象:
implicit object string2R extends R[String, Char] {
def decompose(item: String): (Char, String) = (item.head, item.tail)
def recompose(item: String, part: Char): String = part + item
def empty: String = ""
}
implicit object string2RAlt extends R[String, Int] {
def decompose(item: String): (Int, String) = {
val cp = item.codePointAt(0)
val index = Character.charCount(cp)
(cp, item.substring(index))
}
def recompose(item: String, part: Int): String =
new String(Character.toChars(part)) + item
def empty: String = ""
}
val nat1 = new NotATrie[String, Char]("hello")
nat1.brokenUp
nat1.rebuilt
val nat2 = new NotATrie[String, Int]("hello\ud834\udd1e")
nat2.brokenUp
nat2.rebuilt
Item <% {def decons(i: Item) => (Part, Item)} with {def cons(p: Part, i: Item) => Item}
。我知道这可能在语法上有误,但这是否是正确的想法? - Dylanobject R { ... }
中,编译器应该在伴生对象中搜索隐式定义。 - huynhjl