在Scala的letrec?(结为一体的不变方式)
假设我有这样一个愚蠢的小案例类:在Scala的letrec?(结为一体的不变方式),scala,letrec,tying-the-knot,Scala,Letrec,Tying The Knot,假设我有这样一个愚蠢的小案例类: case class Foo(name: String, other: Foo) 我如何不可变地定义a和b,使得a.other是b,而b.other是a?scala是否提供了一些解决问题的方法?我想这样做: val (a, b): (Foo, Foo) = (Foo("a", b), Foo("b", a)) // Doesn't work. 可能性 在哈斯克尔,我会这样做: data Foo = Foo { name :: String, other :
case class Foo(name: String, other: Foo)
我如何不可变地定义a
和b
,使得a.other
是b
,而b.other
是a
?scala是否提供了一些解决问题的方法?我想这样做:
val (a, b): (Foo, Foo) = (Foo("a", b), Foo("b", a)) // Doesn't work.
可能性 在哈斯克尔,我会这样做:
data Foo = Foo { name :: String, other :: Foo }
a = Foo "a" b
b = Foo "b" a
其中对a
和b
的绑定包含在同一let
表达式中,或位于顶层
或者,在不滥用Haskell的自动letrec功能的情况下:
(a, b) = fix (\ ~(a', b') -> Foo "a" b', Foo "b" a')
注意惰性模式,
~(a',b')
,这很重要。您希望Foo
保持不变,但Scala中的惰性在声明站点上。如果不改变它,Foo
不可能是非严格的,Haskell中指出的模式只起作用,因为那里的Foo
是非严格的(也就是说,Foo“a”b
不会立即计算b
)
否则,解决方案基本相同,允许使用必要的箍环,以使所有内容都不严格:
class Foo(name: String, other0: => Foo) { // Cannot be case class, because that mandates strictness
lazy val other = other0 // otherwise Scala will always reevaluate
}
object Foo {
def apply(name: String, other: => Foo) = new Foo(name, other)
}
val (a: Foo, b: Foo) = (Foo("a", b), Foo("b", a))
我想知道现在有多少搜索引擎会开始为“婚礼”寻找这些问题……这个问题或多或少是重复的。此外,如果可以使用case类,
toString
将递归forever@LuigiPlinge该解决方案会影响类本身的定义。我想看看Foo
未缓存的解决方案toString
确实会永远重复。啊,你说得对。定义data Foo=Foo{name::!String,other::!Foo}
会导致Haskell解决方案无法工作。感谢您的评论“否则Scala将始终重新计算”。很高兴知道!