telefono telefonica sirve saber quien que para online nombre name llamadas llama identificador gratis fijo con como aplicacion activar scala functional-programming lazy-evaluation callbyname

telefonica - ¿La clase de caso Scala prohíbe los parámetros de llamada por nombre?



identificador de llamadas telefonica (2)

Quiero implementar una lista infinita:

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

El problema es que la call-by-name no está permitida.

He oído que se debe a que el parámetro constructor de val o var no está permitido para la call-by-name . Por ejemplo:

class A(val x: =>Int) //error: `val'' parameters may not be call-by-name

Pero la contradicción es que el parámetro constructor normal sigue siendo val , a pesar de private . Por ejemplo:

class A(x: =>Int) // pass

Así que la pregunta:

  • ¿Es realmente el problema de val o var ?
    • Si eso. Dado que el punto de llamada por nombre es aplazar el cálculo, ¿por qué no se podría aplazar el cálculo o la inicialización de val o var ?
  • ¿Cómo sortear la clase cass para implementar una lista infinita?

No hay contradicción: la class A(x: => Int) es equivalente a la class A(private[this] val x: => Int) y no la class A(private val x: => Int) . private[this] marca un valor instancia-privado, mientras que un modificador privado sin más especificación permite acceder al valor desde cualquier instancia de esa clase.

Desafortunadamente, la definición de una case class A(private[this] val x: => Int) tampoco está permitida. Supongo que es porque las clases de casos necesitan acceso a los valores de constructor de otras instancias, porque implementan el método equals .

Sin embargo, podría implementar las características que una clase de caso proporcionaría manualmente:

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) }

Para verificar este código, puedes probar:

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") } }

Lamentablemente, no sé con seguridad por qué están prohibidos los miembros val y var de call by name. Sin embargo, hay al menos un peligro: piense en cómo las clases de casos implementan toString ; Se toString método toString de cada valor de constructor. Esto podría (y en este ejemplo) conduciría a que los valores se llamen a sí mismos infinitamente. Puede verificar esto agregando t.toString a MyNode ''s toString -method.

Edit: Después de leer el comentario de Chris Martin: La implementación de equals también planteará un problema que probablemente sea más grave que la implementación de toString (que se usa principalmente para la depuración) y hashCode (que solo conducirá a mayores tasas de colisión si es posible) t tener en cuenta el parametro). Tienes que pensar cuidadosamente acerca de cómo implementar equals para ser significativo.


Tampoco he encontrado por qué están prohibidos exactamente los parámetros de by-name en las clases de casos. Supongo que la explicación debe ser bastante elaborada y compleja. Pero Runar Bjarnason en su libro " Programación funcional en Scala " proporciona un buen enfoque para manejar este obstáculo. Él usa el concepto de "thunk" junto con la memorización. Aquí hay un ejemplo de implementación de 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: _*)) } }

Como puede ver, en lugar de un parámetro por nombre regular para el constructor de datos de clase de caso, utilizan lo que llaman "thunk", una función de cero argumentos () => T Luego, para hacer esto transparente para el usuario, declaran un constructor inteligente en el objeto complementario que le permite proporcionar parámetros por nombre y hacerlos memorizados.