我试图建立一种可以颠倒的关系。 例如,北方的相反可能是南方。 左派的反面可能是对的。 我想用case类来表示我的关系。 我发现了一个类似的解决方案,它在这里使用了大小写对象,但这不是我想要的。
这是我的非功能代码:
case class Relationship(name: String, opposite:Relationship) def relationshipFactory(nameA:String, nameB:String): Relationship = { lazy val x:Relationship = Relationship(nameA, Relationship(nameB, x)) x } val ns = relationshipFactory("North", "South") ns // North ns.opposite // South ns.opposite.opposite // North ns.opposite.opposite.opposite // South可以更改此代码,以便:
它不会崩溃 我可以按需求创建这些东西。I'm trying to model a relationship which can be reversed. For example, the reverse of North might be South. The reverse of Left might be Right. I'd like to use a case class to represent my relationships. I found a similar solution that uses case Objects here, but it's not quite what I want, here.
Here's my non-functional code:
case class Relationship(name: String, opposite:Relationship) def relationshipFactory(nameA:String, nameB:String): Relationship = { lazy val x:Relationship = Relationship(nameA, Relationship(nameB, x)) x } val ns = relationshipFactory("North", "South") ns // North ns.opposite // South ns.opposite.opposite // North ns.opposite.opposite.opposite // SouthCan this code be changed so that:
It dosen't crash I can create these things on demand as pairs.最满意答案
如果你真的想用循环依赖来构建不可变对象的图形,你必须声明为def ,并且(最好)向混合中抛出另一个懒惰的val:
abstract class Relationship(val name: String) { def opposite: Relationship } object Relationship { /** Factory method */ def apply(nameA: String, nameB: String): Relationship = { lazy val x: Relationship = new Relationship(nameA) { lazy val opposite = new Relationship(nameB) { def opposite = x } } x } /** Extractor */ def unapply(r: Relationship): Option[(String, Relationship)] = Some((r.name, r.opposite)) } val ns = Relationship("North", "South") println(ns.name) println(ns.opposite.name) println(ns.opposite.opposite.name) println(ns.opposite.opposite.opposite.name)如果你在这个循环依赖循环上运行几百万轮,你可以很快说服自己没有任何不好的事情发生:
// just to demonstrate that it doesn't blow up in any way if you // call it hundred million times: // Should be "North" println((1 to 100000000).foldLeft(ns)((r, _) => r.opposite).name)它确实打印“北”。 它不适用于案例类,但您可以随时添加自己的提取器,因此这样可行:
val Relationship(x, op) = ns val Relationship(y, original) = op println(s"Extracted x = $x y = $y")它为x和y打印“北”和“南”。
但是,更明显的做法是仅保存关系的两个组件,并添加opposite的方法来构造相反的对。
case class Rel(a: String, b: String) { def opposite: Rel = Rel(b, a) }实际上,这已经在标准库中实现了:
scala> val rel = ("North", "South") rel: (String, String) = (North,South) scala> rel.swap res0: (String, String) = (South,North)If you really want to build graphs of immutable objects with circular dependencies, you have to declare opposite as def, and (preferably) throw one more lazy val into the mix:
abstract class Relationship(val name: String) { def opposite: Relationship } object Relationship { /** Factory method */ def apply(nameA: String, nameB: String): Relationship = { lazy val x: Relationship = new Relationship(nameA) { lazy val opposite = new Relationship(nameB) { def opposite = x } } x } /** Extractor */ def unapply(r: Relationship): Option[(String, Relationship)] = Some((r.name, r.opposite)) } val ns = Relationship("North", "South") println(ns.name) println(ns.opposite.name) println(ns.opposite.opposite.name) println(ns.opposite.opposite.opposite.name)You can quickly convince yourself that nothing bad happens if you run a few million rounds on this circle of circular dependencies:
// just to demonstrate that it doesn't blow up in any way if you // call it hundred million times: // Should be "North" println((1 to 100000000).foldLeft(ns)((r, _) => r.opposite).name)It indeed prints "North". It doesn work with case classes, but you can always add your own extractors, so this works:
val Relationship(x, op) = ns val Relationship(y, original) = op println(s"Extracted x = $x y = $y")It prints "North" and "South" for x and y.
However, the more obvious thing to do would be to just save both components of a relation, and add opposite as a method that constructs the opposite pair.
case class Rel(a: String, b: String) { def opposite: Rel = Rel(b, a) }Actually, this is already implemented in the standard library:
scala> val rel = ("North", "South") rel: (String, String) = (North,South) scala> rel.swap res0: (String, String) = (South,North)更多推荐
发布评论