2010-01-28 4 views
1

je peux mettre en œuvre un def avec un val où l'def ne prend aucun argument:Mettre en œuvre une définition avec des paramètres N comme val de type fonction personnaliséeN

trait T { def foo: Int } 
class C(val foo: Int) extends T 

Pourquoi cela peut-il pas être étendu à la mise en œuvre d'un def prenant N args à un val qui est un FunctionN? Je veux de mettre en œuvre quelque chose comme:

def expensiveOperation(p: Int => Boolean) : List[Int] 

Avec fonction paresseux val. Quelque chose comme:

val expensiveOperation = { 
    val l = //get expensive list 
    l.filter _ //partially applied function 
} 

Je sais que cette syntaxe ne semble pas fonctionner sur 2.8. Y at-il quelque chose qui me manque, pourquoi ne puis-je pas mettre en œuvre un def en prenant les paramètres comme val fonctionnelle?

+0

Je suis confus. Votre val ressemble à 'Function0', pas à' Function1'. Quel paramètre est-il censé prendre? En outre, il n'y a pas de court-circuit dans le val, alors pourquoi voulez-vous le faire de cette façon? Et s'il y a un court-circuit, pourquoi ne pas utiliser 'Stream' au lieu de' List'? –

+0

Je ne comprends pas la question. Pouvez-vous expliquer plus clairement ce que vous essayez de faire, plutôt que de savoir comment vous essayez de le faire. Actuellement vous nous dites seulement la partie "Comment". –

+0

my 'val' est une fonction1 car il s'agit essentiellement d'un pointeur vers la méthode' List.filter' (qui prend comme paramètre un 'Function1').Ma question est que je peux avoir un 'val' implémenter un' def' où 'def' ne prend pas d'arguments mais je ne vois pas pourquoi cela ne peut pas être étendu à' def's avec args? –

Répondre

2

Maintenant, après les modifications, je pense que je comprends ce que vous recherchez. Mais vous ne pouvez pas faire ce que vous voulez parce que les signatures de type ne correspondent pas. Dans les deux cas, vous ne fournissez rien et vous récupérez un Int (5 dans ce cas). Génial!

def expensive(p: Int => Boolean): List[Int] 

Maintenant, vous fournissez quelque chose. Mais un val est juste un objet stocké quelque part. Vous pouvez fournir quelque chose à l'objet que l'étiquette fait référence à, mais ce n'est pas la même chose que de fournir quelque chose à l'étiquette «x».

est ici la définition que vous devez utiliser si vous voulez un val la contourner:

def expensive: (Int=>Boolean)=>List[Int] 

Maintenant, vous avez quelque chose que vous appelez sans paramètres et il retourne quelque chose qui peut prendre une Int => Fonction booléenne et vous donner une liste [Int] en retour. C'est exactement ce que vous obtenez avec le val - dans les deux cas, vous avez le nom de quelque chose qui retourne un objet qui a la fonctionnalité que vous voulez.

(Scala, vals sont effectivement mises en œuvre en tant que champs cachés avec des méthodes de lecture qui ne prennent pas de paramètres et de retour tout ce qui est dans le champ caché. Il est donc vraiment une méthode comme la définition est.)

+0

Mais puis-je implémenter le' def' avec un 'def' qui prend les paramètres? La réponse est bien sûr une entreprise "NON"! Je comprends votre argument, qui est sans doute correct dans ses détails, mais les deux éléments * devraient * être équivalents. Le seul problème logique que je prévoyais était celui de l'arité variable; ie 'varargs' –

+0

Ils sont logiquement équivalents en supposant que (1) vous ne faites rien avec l'objet retourné en dehors de son appel, et (2) vous ne vous souciez pas des détails d'implémentation (par exemple, qui pourraient être pertinents pour la performance) ça ne vous dérange pas qu'il y ait un niveau supplémentaire d'indirection. Cela pourrait être un argument que le compilateur devrait transformer chaque méthode en une fonction, ou que vous perdiez le getter normal pour un val qui remplace un def avec des arguments et le remplacez par un getter spécialisé qui appelle aussi apply (et donc correspond à la signature de type du def). –

0

Edit: d'accord, je n'ai pas compris ce que vous vouliez dire. Mais si vous aviez signifié ce que je pensais ...

Vous pouvez, en utilisant la syntaxe abrégée, créez un val qui applique une opération:

val expensive = (p: (Int) => Boolean) => { 
    val l = List(1,2,3,4) 
    l filter p 
} 
scala> expensive(_<3) 
res1: List[Int] = List(1,2) 

Mais cela ne met pas en cache en fait la liste, ce qui est Je pense que tu veux. La raison en est que cette syntaxe raccourcie met tout after => dans la méthode apply de Function1. Vous voulez sans doute la liste à enregistrer comme ceci:

val expensive = new Function1[Int=>Boolean,List[Int]] { 
    lazy val l = List(1,2,3,4) 
    def apply(p: (Int) => Boolean) = { l filter p } 
} 

et qu'il n'y a pas un grand raccourci que je connaisse.

Edit: Si vous êtes heureux de créer la liste en dehors de ce bloc, alors il y a un raccourci (voir également le commentaire):

val expensive = List(1,2,3,4).filter _ 

scala> expensive(_ < 3) 
res6: List[Int] = List(1, 2) 
+0

Mais est-ce une * implémentation * valide du 'def' que j'ai donné? –

+0

(La réponse est ** NON **) - Je peux facilement mettre en cache mon op cher en déclarant un 'val' - ma question était à propos de cette mise en œuvre d'un' def', ce n'est pas –

+0

(Peu importe, je juste vu vos modifications ci-dessus - ce commentaire ne s'applique pas.) –

1

vous pouvez toujours juste en avant à votre val:

trait T { 
    def expensiveOperation(p: Int => Boolean) : List[Int] 
} 

class C extends T { 
    def expensiveOperation(p: Int => Boolean): List[Int] = { 
     expensiveOperationVal(p) 
    } 
    val expensiveOperationVal = { p: (Int=>Boolean) => 
    // ... lazy stuff 
    List(1,2,3) 
    } 
} 

Et, bien que cela ne répond pas à votre question, il ressemble à moins que votre code // ... get expensive list dépend du prédicat p, alors vous pourriez juste faire quelque chose de similaire à:

class C extends T { 
    def expensiveOperation(p: Int => Boolean): List[Int] = { 
     myExpensiveList filter p 
    } 
    lazy val myExpensiveList = { 
    val l = // ... expensive stuff 
    l 
    } 
} 
+0

Ouais - c'est exactement ce que j'ai fait, mais il semble une honte d'un point de vue "principe d'accès unifié". Ce n'est pas plus "joli" une solution que de simplement tenir la liste et d'appeler 'filter' quand vous avez besoin de –

2

A val doesn Ne prenez pas d'arguments, car il est calculé et stocké dans un champ. Mais je pourrais vous renvoyer aux nombreux articles que j'ai publiés ces deux derniers jours sur les listes de diffusion. Ou plutôt, considérons-le d'un point de vue Java, puisque Scala est compatible avec Java au niveau jvm, et il doit sûrement obéir aux règles jvm. Commençons par la première classe:

abstract class X { 
    def expensiveOperation(p: Int => Boolean) : List[Int] 
} 

Maintenant, nous allons l'étendre:

abstract class Y extends X { 
    override val expensiveOperation: ((Int) => Boolean) => List[Int] 
} 

Ainsi, à partir de Java, nous savons que la classe X a la méthode expensiveOperation, qui reçoit Function1[Int, Boolean] et retourne List[Int].

Maintenant nous allons à la classe Y. Naturellement, il doit définir la même méthode, mais il doit également définir le getter expensiveOperation, qui ne reçoit aucun argument et renvoie Function1[Function1[Int, Boolean],List[Int]].

Cela pourrait être réalisable, aussi longtemps que cette méthode supplémentaire n'existait pas en X aussi. Alors définissons-le:

class Z extends Y { 
    override val extensiveOperation = new Function1[Function1[Int, Boolean], List[Int]] { 
    def apply(p: Int => Boolean) = List range (1, 10) filter p 
    } 
} 

Comment cela est-il défini? Est-ce que Scala copie le corps de apply en tant que corps pour expensiveOperation (celui qui reçoit un paramètre, pas le getter)? Cela pourrait encore être réalisable. Essayons quelque chose d'autre, si:

class W(f: ((Int) => Boolean) => List[Int]) extends Y { 
    override val extensiveOperation = f 
} 

Maintenant, comment pouvons-nous remplacer les paramètres de réception extensiveOperation? Je suppose que nous pourrions écrire comme ceci:

override def extensiveOperation(p: Int => Boolean) = extensiveOperation.apply(p) 

C'est réalisable. Mais personnellement, je pense que c'est un peu compliqué. Ma suggestion: rédiger un court SID, et obtenir un accord sur une liste de diffusion Scala. Sans code pour l'implémenter, cependant, je ne pense pas qu'il a beaucoup de chance d'être adopté - Scala doit suivre chaque fonction - tapé val pour savoir s'il est en train de surcharger un def ou non.

+0

Mon travail a été un peu (lire: beaucoup) frénétique au cours des derniers jours (lire: mois) et je n'ai pas suivi les lits postaux! Cela me semble encore quelque chose qui * pourrait * être équivalent mais je ne suis pas un concepteur de langage et n'ai jamais écrit de compilateur dans ma vie! Donc, je ne suis pas sûr qu'il y aura une implémentation de référence venant de moi ... –

+0

Je suis en train de rattraper les listes de diffusion. Comme c'est étrange qu'il y ait une énorme discussion sur un sujet auquel je pensais justement en même temps! –

Questions connexes