2 votes

Appel de fonction Scala sans "." (point) vs en utilisant "." (point)

Quelqu'un peut-il m'aider à comprendre ce qui se passe ici. J'ai cette définition pour générer des nombres premiers :

def primes: Stream[Long] = {
    2 #:: 3 #:: 5 #:: 7 #::Stream.iterate(11L)(_ + 2) filter {
        n => primes takeWhile (p => p*p <= n) forall (n % _ != 0)
    }
}

def primes: Stream[Long] = {
    2 #:: 3 #:: 5 #:: 7 #::Stream.iterate(11L)(_ + 2) filter {
        n => primes takeWhile (p => p*p <= n) forall (n % _ != 0)
    }
}

Comme vous pouvez le voir, les deux définitions sont exactement similaires, sauf que la seconde n'a pas de . avant filter, tandis que la première en a un.

Le problème est que l'exécution de la première fonctionne comme prévu et nous donne des nombres premiers, mais la seconde génère une java.lang.StackOverflowError. Quelqu'un pourrait-il éclairer ma lanterne ? Que se passe-t-il dans chaque cas?

Version de Scala : 2.11.6

Version de Java : 1.8.0_121

Voici le programme complet que j'ai utilisé pour tester chacun :

object Main {

    def primes: Stream[Long] = {
        2 #:: 3 #:: 5 #:: 7 #::Stream.iterate(11L)(_ + 2) filter {
            n => primes takeWhile (_ <= sqrt(n)) forall (n % _ != 0)
        }
    }

    def primes2: Stream[Long] = {
        2 #:: 3 #:: 5 #:: 7 #::Stream.iterate(11L)(_ + 2).filter {
            n => primes2 takeWhile (p => p*p <= n) forall (n % _ != 0)
        }
    }

    def main(args: Array[String]): Unit = {
        println(primes.take(args.head.toInt).force)
    }
}

8voto

sepp2k Points 157757

La notation sans . a la même précédence que celle de n'importe quel infix personnalisé. Ainsi, le premier s'applique uniquement à Stream.iterate(11L)(_ + 2) - le deuxième l'applique à 2 #:: 3 #:: 5 #:: 7 #::Stream.iterate(11L)(_ + 2).

La raison pour laquelle le premier fonctionne est que les éléments 2, 3, 5 et 7 sont déjà dans primes lorsque le filtre s'exécute, donc lorsque le filtre tente d'utiliser primes, ces éléments y sont déjà.

Dans le deuxième code, ce n'est pas le cas car le filtre s'applique également à ces éléments, ce qui signifie qu'ils n'apparaîtraient pas dans primes avant que le filtre ne retourne vrai pour eux. Mais le filtre a besoin d'obtenir des éléments de prime avant de pouvoir renvoyer quelque chose, donc il se perd dans une récursion infinie en essayant d'atteindre un élément.

2voto

nmat Points 2434

Vous avez besoin de parenthèses :

def primes: Stream[Long] = {
  2 #:: 3 #:: 5 #:: 7 #::(Stream.iterate(11L)(_ + 2) filter {
    n => primes takeWhile (p => p*p <= n) forall (n % _ != 0)
  })
}

En règle générale, j'utilise généralement des points partout. C'est plus facile à lire et rend ce genre de bogues plus difficile à apparaître.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X