0

Je suis un développeur Scala débutant ayant des problèmes avec un exercice de codage (5.12) pris par le livre "Programmation fonctionnelle en Scala" par Paul Chiusano.Motif correspondant dans la fonction anonyme dans Scala

J'ai cette fonction ici, appelé déplier, qui prend un état initial et une fonction pour produire un courant avec les états suivants:

def unfold[A,S](z: S)(f: S => Option[(A,S)]): Stream[A] = f(z) match { 
    case Some((h,t)) => h #:: unfold(t)(f) 
    case _ => Stream.empty 
} 

Par exemple, avec cette fonction, on peut créer un infini flux d'objets, par exemple

def constant[A](a: A): Stream[A] = unfold(a)(_ => Some(a,a))

Maintenant, je veux créer la séquence de Fibonacci, je tape:

def fibs: Stream[Int] = unfold((0,1))((a,b) => Some(a,(b,a+b)))

Je reçois ces erreurs:

  • manquants type de paramètre b

  • Expression de type Certains [((Int, Int), (Nothing, String))] ne se conforme pas à l'option de type attendu [(A _, (Int, Int))]

Si je le cas mot-clé dans la fonction anonyme est passé à se dérouler, comme:

{ case (a,b) => Some(a,(b,a+b))}

tout va bien. Donc, ma question est: quelle est la différence entre les deux implémentations? Est-ce quelque chose à propos de l'inférence de type que je ne comprends pas?

+0

Possible duplicate de [Quelle est la différence formelle dans Scala entre accolades et parenthèses, et quand devraient-ils être utilisés?] (Http://stackoverflow.com/questions/4386127/what-is -le-formel-difference-in-scala-entre-accolades-et-parenthèses-et-quand) – soote

Répondre

2
{ (a,b) => Some(a, (b, a+b)) } 

est une fonction qui prend deux arguments - a et b et renvoie une option (d'un tuple). Ce n'est pas ce dont tu as besoin. Vous voulez définir une fonction qui prend un seul argument (tuple). Une façon de le faire serait quelque chose comme ceci:

{ tuple => Some(tuple._1, (tuple._2, tuple._1 + tuple._2)) } 

que vous pourriez développer en quelque chose comme ceci:

{ tuple => 
    val a = tuple._1 
    val b = tuple._2 
    // Can also do it shorter, with automatic unapply here: 
    // val (a,b) = tuple 
    // this is already pretty similar to what you have with `case`, right? 
    Some(a, (b, a+b)) 
} 

Cela semble long, mais scala a une construction de syntaxe spéciale qui vous permet de déconstruire complexe paramètres à des fonctions anonymes « à la volée », en utilisant une syntaxe similaire à la correspondance de motif (il appelle effectivement unapply sur l'entrée, même en correspondance de motif ferait):

{ case(a, b) => Some(a, (b, a+b)) } 

Ceci est exactement le même que ci-dessus.

+0

Le compilateur n'appelle pas ici «unapply», il connaît le type exact, 'match' fonctionne de la même manière. On dirait que "unapply" est seulement appliqué aux objets extracteur. –

+0

@VictorMoroz vous avez raison, les tuples sont manipulés spécialement. J'avais un cas générique en tête quand j'ai mentionné 'unapply', vous pouvez utiliser un objet extracteur dans ce contexte. – Dima

0

Il semble que vous avez atteint l'une des règles sur la façon dont le compilateur interprète votre code:

A normal function of one arg: 

scala> val g = (x: Int) => x + 1 
g: Int => Int = <function1> 

The data type for a tuple of ints is Tuple2(Int, Int): 
scala> (3,4) 
res3: (Int, Int) = (3,4) 

scala> val a: Tuple2[Int, Int] = (3,4) 
a: (Int, Int) = (3,4) 

But this does not work: 

scala> val f = ((a,b): (Int, Int)) => a+b 
<console>:1: error: not a legal formal parameter. 
Note: Tuples cannot be directly destructured in method or function parameters. 
     Either create a single parameter accepting the Tuple1, 
     or consider a pattern matching anonymous function: `{ case (param1, param1) => ... } 
val f = ((a,b): (Int, Int)) => a+b 
      ^
This works: 
scala> val f = (x: Tuple2[Int, Int]) => x._1 + x._2 
f: ((Int, Int)) => Int = <function1> 

Voici ce que vous pensiez que vous pourriez faire:

val f = ((a, b): (Int, Int)) => Some(a, (b, a + b)) 

puis (dans le contexte de unfold) le (0,1) se comprend comme le type (Int, Int), ainsi quand vous appelez unfold vous pouvez omettre la déclaration de type et écrivez juste ((a, b)) => Some(a, (b, a + b)). Mais cela ne fonctionne pas parce que "les Tuples ne peuvent pas être directement déstructurés dans les paramètres de méthode ou de fonction". La fonction f ci-dessus ne compile pas seule, en dehors de votre unfold. Au lieu de cela, cela compile: val g = (x: Tuple2[Int, Int]) => Some(x._1, (x._2, x._1 + x._2))