2010-04-11 20 views
7

Qu'est-ce qui ne va pas, la méthode suivante?Utilisation de fonctions d'évaluation paresseuses dans varargs

def someMethod(funcs: => Option[String]*) = { 
... 
} 
+0

Le nom du paramètre formel, 'funcs', est hautement suspect. Les paramètres de nom de fichier, lorsqu'ils sont implémentés avec un thunk, ne sont pas ouvertement des fonctions. –

+0

Ce ne sont pas non plus ce qu'on appelle communément des arguments évalués paresseusement. C'est-à-dire que si vous utilisez la valeur plusieurs fois, l'expression donnée en tant que paramètre réel est évaluée plusieurs fois. Cela a des conséquences sur les performances et si cette expression a des effets secondaires, ils se multiplieront également. –

+0

Cela peut être une solution: http://stackoverflow.com/a/34373967/2825964 –

Répondre

5

Cela fait "fonctionne" sous 2.7.7 si vous ajoutez parens:

scala> def someMethod(funcs: => (Option[String]*)) = funcs 
someMethod: (=> Option[String]*)Option[String]* 

sauf qu'il ne fonctionne pas réellement à l'exécution:

scala> someMethod(Some("Fish"),None) 
    scala.MatchError: Some(Fish) 
at scala.runtime.ScalaRunTime$.boxArray(ScalaRunTime.scala:136) 
at .someMethod(<console>:4) 
at .<init>(<console>:6) 
at .<clinit>(<console>) ... 

En 2.8, il refuse de vous laisser spécifier X * en tant que sortie d'une fonction ou d'un paramètre de nom, même si vous pouvez le spécifier en tant qu'entrée (r21230, post-Bêta 1):

scala> var f: (Option[Int]*) => Int = _ 
f: (Option[Int]*) => Int = null 

scala> var f: (Option[Int]*) => (Option[Int]*) = _ 
<console>:1: error: no * parameter type allowed here 
     var f: (Option[Int]*) => (Option[Int]*) = _ 

Mais si vous essayez de convertir une méthode, il fonctionne:

scala> def m(oi: Option[Int]*) = oi 
m: (oi: Option[Int]*)Option[Int]* 

scala> var f = (m _) 
f: (Option[Int]*) => Option[Int]* = <function1> 

scala> f(Some(1),None) 
res0: Option[Int]* = WrappedArray(Some(1), None) 

Il est donc pas tout à fait cohérente.

Dans tous les cas, vous pouvez éventuellement obtenir ce que vous voulez en passant dans un tableau, puis envoyer ce tableau à quelque chose qui prend des arguments répétés:

scala> def aMethod(os: Option[String]*) { os.foreach(println) } 
aMethod: (os: Option[String]*)Unit 

scala> def someMethod(funcs: => Array[Option[String]]) { aMethod(funcs:_*) } 
someMethod: (funcs: => Array[Option[String]])Unit 

scala> someMethod(Array(Some("Hello"),Some("there"),None)) 
Some(Hello) 
Some(there) 
None 

Si vous voulez vraiment (facilement) passer un tas des arguments évalués paresseusement, alors vous avez besoin d'un peu d'infrastructure qui, pour autant que je sache, n'existe pas bien dans la bibliothèque (c'est le code pour 2.8, voir comme source d'inspiration pour une stratégie similaire en 2.7):

class Lazy[+T](t:() => T, lt: Lazy[T]) { 
    val params: List[() => T] = (if (lt eq null) Nil else t :: lt.params) 
    def ~[S >: T](s: => S) = new Lazy[S](s _,this) 
} 
object Lz extends Lazy[Nothing](null,null) { 
    implicit def lazy2params[T : Manifest](lz: Lazy[T]) = lz.params.reverse.toArray 
} 

Maintenant, vous pouvez facilement créer un tas de param Les noms qui sont évalués paresseusement:

scala> import Lz._ // To get implicit def 
import Lz._ 

scala> def lazyAdder(ff: Array[()=>Int]) = { 
    | println("I'm adding now!"); 
    | (0 /: ff){(n,f) => n+f()} 
    | } 
lazyAdder: (ff: Array[() => Int])Int 

scala> def yelp = { println("You evaluated me!"); 5 } 
yelp: Int 

scala> val a = 3 
a: Int = 3 

scala> var b = 7 
b: Int = 7 

scala> lazyAdder(Lz ~ yelp ~ (a+b)) 
I'm adding now! 
You evaluated me! 
res0: Int = 15 

scala> val plist = Lz ~ yelp ~ (a+b) 
plist: Lazy[Int] = [email protected] 

scala> b = 1 
b: Int = 1 

scala> lazyAdder(plist) 
I'm adding now! 
You evaluated me! 
res1: Int = 9 
3

Les arguments répétés de manière évidente ne sont pas disponibles pour les paramètres de type nom.

+0

Mais, juste pour les coups de pied, essayez ceci: 'def f (oi: Option [Int] *) = oi' dans le REPL. Intéressant, hein? –

+0

@Rex_Kerr: Je suppose. De quoi parlez-vous? –

+1

Vous pouvez retourner Option [Int] * à partir d'une méthode, et vous pouvez utiliser (f _) pour en faire une fonction. Mais essayez de trouver une syntaxe qui vous permet de représenter le type. Il n'est donc pas clair pour moi si les arguments répétés ne sont pas disponibles pour les paramètres by-name, ou si la syntaxe ne vous permet pas d'exprimer le type que vous voulez (ou les deux). –