Wei*_*Lin 24 functional-programming scala lazy-evaluation callbyname
我想实现一个无限的列表:
abstract class MyList[+T]
case object MyNil extends MyList[Nothing]
case class MyNode[T](h:T,t: => MyList[T]) extends MyList[T]
//error: `val' parameters may not be call-by-name
问题是call-by-name不允许的.
我听说这是因为val或者var不允许使用构造函数参数call-by-name.例如:
class A(val x: =>Int) 
//error: `val' parameters may not be call-by-name
但矛盾的是,正常的构造函数的参数仍然是val,尽管private.例如:
class A(x: =>Int) 
// pass
所以问题是:
val还是var?
call-by-name或val计算(或初始化)被推迟?Kul*_*mpa 15
没有矛盾:class A(x: => Int)相当于class A(private[this] val x: => Int)和不相同class A(private val x: => Int).private[this]标记值为instance-private,而没有进一步指定的private-modifier允许从该类的任何实例访问该值.
不幸的是,case class A(private[this] val x: => Int)也不允许定义a .我假设这是因为case-classes需要访问其他实例的构造函数值,因为它们实现了该equals方法.
不过,您可以实现案例类手动提供的功能:
abstract class MyList[+T]
class MyNode[T](val h: T, t: => MyList[T]) extends MyList[T]{
  def getT = t // we need to be able to access t 
  /* EDIT: Actually, this will also lead to an infinite recursion
  override def equals(other: Any): Boolean = other match{
    case MyNode(i, y) if (getT == y) && (h == i) => true
    case _ => false
  }*/
  override def hashCode = h.hashCode
  override def toString = "MyNode[" + h + "]"
}
object MyNode {
  def apply[T](h: T, t: => MyList[T]) = new MyNode(h, t)
  def unapply[T](n: MyNode[T]) = Some(n.h -> n.getT)
}
要检查此代码,您可以尝试:
def main(args: Array[String]): Unit = {
  lazy val first: MyNode[String] = MyNode("hello", second)
  lazy val second: MyNode[String] = MyNode("world", first)
  println(first)
  println(second)
  first match {
    case MyNode("hello", s) => println("the second node is " + s)
    case _ => println("false")
  }
}
不幸的是,我不确定为什么禁止使用call-by-name val和var成员.然而,至少存在一个危险:考虑案例类如何实现toString; 在toString每一个构造函数值的-方法被调用.这可能(并且在这个例子中)会导致值无限地调用自己.您可以通过添加检查这个t.toString来MyNode的toString-方法.
编辑:阅读克里斯马丁的评论后:执行equals也会造成一个问题,可能比实施toString(主要用于调试)和hashCode(如果你不能采取的话只会导致更高的碰撞率)更严重参数考虑在内).你必须仔细考虑如何实现equals有意义的.
我也没有发现为什么在案例类中完全禁止使用按名称命名的参数。我想解释应该非常详尽和复杂。但是Runar Bjarnason在他的《Scala中的函数编程》一书中提供了一种解决此障碍的好方法。他使用“ thunk”的概念和记忆。这是Stream实现的示例:
sealed trait Stream[+A]
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]
object Stream {
 def cons[A](hd: => A, tl: => Stream[A]): Stream[A] = {
  lazy val head = hd
  lazy val tail = tl
  Cons(() => head, () => tail)
 }
 def empty[A]: Stream[A] = Empty
 def apply[A](as: A*): Stream[A] =
  if (as.isEmpty) empty else cons(as.head, apply(as.tail: _*))
 }
}
如您所见,它们代替了case类数据构造函数的常规by-name参数,而是使用所谓的“ thunk”(零参数的函数)() => T。然后,为使用户透明,他们在伴随对象中声明了一个智能构造函数,该构造函数允许您提供一个按名称的参数并使它们成为备忘录。
| 归档时间: | 
 | 
| 查看次数: | 3800 次 | 
| 最近记录: |