2009-12-17 2 views
12

Donc, je suis en train d'apprendre de nouvelles choses en C# & Python. Il s'avère que les deux lanuages ​​supportent des méthodes imbriquées (comme le fait C#).Méthodes imbriquées? Pourquoi sont-ils utiles?

Python:

def MyMethod(): 

    print 'Hello from a method.' 

    def MyInnerMethod(): 
     print 'Hello from a nested method.' 

    MyInnerMethod() 

C# (en utilisant les nouvelles fonctionnalités de .NET 3.5): *

static void Main() 
{ 
    Console.WriteLine("Hello from main."); 

    Func<int, int> NestedMethod = 
     (x) => 
     { 
      Console.WriteLine("In nested method. Value of x is {0}.", x); 
      return x; 
     }; 

    int result = NestedMethod(3); 
} 

Alors pourquoi les méthodes imbriquées si important? Qu'est-ce qui les rend utiles?


** Le code C# n'a pas été testé. Ne hésitez pas à modifier si elle ne compile pas. *

+0

En fait, la syntaxe de l'exemple C# (la syntaxe de délégué anonyme) n'est pas nouvelle à 3.5; c'était avec nous en 2.0. Si vous aviez utilisé une fonction lambda, cela aurait été la syntaxe C# 3. –

+3

PS - J'aime votre musique. –

+0

@Erik: Merde, je savais que je bousillerais quelque part. ;-) –

Répondre

16

d'abord, rends compte que je ne peux pas vous donner une liste complète. Si vous deviez vous demander «pourquoi les tournevis sont-ils utiles?», Je parlerais des vis et des couvercles de boîtes de peinture, mais manquerais leur valeur dans l'inspection des termites. Lorsque vous demandez: «Pourquoi les fonctions imbriquées sont-elles utiles?», Je peux vous parler de la portée, des fermetures et des points d'entrée.

Premièrement, l'imbrication peut être une alternative aux classes. J'ai récemment écrit un code de rendu qui prenait un nom de fichier pour un code de balisage spécialisé et renvoyait un bitmap. Cela conduit naturellement à des fonctions nommées grab_markup_from_filename() et render_text_onto_image() et autres. L'organisation la plus propre était un point d'entrée nommé generate_png_from_markup_filename(). Le point d'entrée a fait son travail en utilisant des fonctions imbriquées pour accomplir sa tâche. Il n'y avait pas besoin d'une classe, car il n'y avait pas d'objet avec état. Mon alternative était de créer un module avec des méthodes privées cachant mes aides, mais ce serait plus désordonné.

def generate_png_from_markup_filename(filename): 
    def grab_markup_from_filename(): 
     ... 
    ... # code that calls grab_markup_from_filename() to make the image 
    return image 

Deuxièmement, j'utilise l'emboîtement pour les fermetures. L'exemple le plus courant est celui de la création de décorateurs. L'astuce est que je renvoie une référence à une fonction interne, de sorte que la fonction interne et la valeur du paramètre externe ne sont pas collectées.

def verify_admin(function_to_call): 
    def call_on_verify_admin(*args, **kwargs): 
     if is_admin(global.session.user): 
      return function_to_call(*args, **kwargs) 
     else: 
      throw Exception("Not Admin") 
    return call_on_verify_admin # the return value of verify_admin() 

utiliser cette façon:

def update_price(item, price): 
    database.lookup(item).set_field('price', price) 
update_price = verify_admin(update_price) 

ou, de façon plus concise:

@verify_admin 
def update_price(item, price): 
    database.lookup(item).set_field('price', price) 

Et oui, il devrait être difficile à tracer. Rappelez-vous que "def" est juste une autre déclaration. Donc, voici deux endroits où les classes imbriquées sont utiles. Il y en a plus. C'est un outil.

7

méthodes emboîtés sont utiles car il y a de nombreux cas où vous souhaitez passer un comportement autour, plutôt que données. Dans de nombreux cas, il est plus clair et plus facile de suivre, en plus d'être plus facile et plus rapide à écrire, de simplement définir ce comportement dans le corps de votre méthode, là où vous allez l'utiliser.

En .NET, LINQ en est un bon exemple. Supposons que vous vouliez créer une liste où chaque valeur est le double de sa valeur dans la liste originale:

list.Select(i => i*2) 

i => i*2 est équivalent à:

Func<int,int> double = delegate(int x) { return x*2; } 

Ceci est beaucoup plus simple et plus claire que la création d'un fonction séparée. En outre, la fonction que vous déclarez n'a peut-être pas de sens ailleurs (c'est-à-dire en dehors de la portée de votre méthode). En C#, vous pouvez même faire référence à des variables déclarées dans votre corps de méthode - ce que vous ne pouvez pas faire avec une fonction non imbriquée.

Bien sûr, vous pouvez aussi abuser de fonctions imbriquées ...

+0

Je ne connaissais pas les délégués où l'on appelait "méthodes imbriquées". –

+0

En substance, vous utilisez un délégué pour créer une méthode imbriquée, n'est-ce pas? –

+0

oui ... 'méthodes imbriquées' n'est probablement pas le bon terme, mais c'est comme ça que l'op le décris ... –

4

méthodes Nested vous permettent également de contrôler la portée de l'appelant ainsi que vous permettant d'accéder aux membres créés en interne sans qu'ils soient exposés à d'autres fonctions dans un portée similaire (par exemple à l'intérieur d'une classe)

3

Essayez cette Pythonish exemple pour un aperçu pseudocode à un cas d'utilisation de cette technique:

def MyMethod(base): 
    base = expensivePreparation(base) 
    def MyInnerMethod(some_modifier): 
     doSomethingCoolWith(base, some_modifier) 

    return MyInnerMethod 

process = MyMethod(some_obj) 
process(arg1) 
process(arg2) 
0

Beaucoup de réponses utiles ici déjà, mais voilà.

  • classes internes: pas! Bon essai, mais trop de travail. Les fonctions/processus imbriqués sont beaucoup plus rapides et précis. Les classes de King Java [1] avec une méthode [2] sont vraiment nulles, et les fonctions imbriquées (ou procédures) contournent un tel non-sens syntaxique.
  • décomposition fonctionnelle: cassez une longue routine en plus petites, qui sont cachées à d'autres endroits où elles ne sont pas utilisées. Pascal a soutenu cela il y a des éternités, et je suis à peu près sûr que ce n'était pas une nouvelle invention même alors (début des années 70).
  • fermetures: mentionnées ailleurs, mais utiles. utiliser des variables locales dans la fonction d'enceinte pour initialiser la fonction générée, et le retour d'une fonction personnalisée (ou utiliser le code généré)

1: http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html "King Java"

2: http://en.wikipedia.org/wiki/Function_object#In_Java « objet Function/foncteur "

1

Le long des points mentionnés ci-dessus, ceux-ci peuvent être traités comme Fonctions d'ordre supérieur, fonctions qui prennent ou renvoient elles-mêmes des fonctions.

Découvrez cet exemple:

 static void Main(string[] args) 
    { 

     Func<int, int> NestedMethod = delegate(int x)  
     {    Console.WriteLine("In nested method. Value of x is {0}.", x);   
      return x;   
     }; 

     HigerOrderTest(NestedMethod)(); 
    } 

    private static Action HigerOrderTest(Func<int, int> highFunction) 
    { 
      var sam = highFunction(3); 

      Action DoIt =() => 
      { 
       Console.WriteLine("Output is {0}.", sam);  
      }; 
      return DoIt; 
    } 
+0

Même si c'est valable, c'est bizarre, mais cool. +1 – Kredns

4

méthodes emboîtées augmentent la localité de votre code qui est une bonne chose que vous n'avez pas polluer les classes avec de petites méthodes privées utilisées que dans un seul endroit.

0

Vous êtes peut-être déjà au courant de l'utilité des fonctions passant à d'autres fonctions, par exemple, fournir une routine sort() avec une fonction de comparaison, et les exemples évidents de map() et filter() et ainsi de suite. De même, il est souvent utile de recevoir des fonctions d'une autre fonction. L'utilisation la plus courante est pour closures, mais currying est également commun.

Un avantage principal d'une fermeture est de pouvoir créer une nouvelle fonction à l'exécution qui contient toutes les informations temporelles requises pour son exécution à ce moment-là. Dans les implémentations de langage qui ne supportent pas les fermetures, une telle gestion d'état est généralement effectuée, sans doute moins élégamment, via des classes: des variables d'instance d'objet sont définies, puis lues plus tard si nécessaire.Les fermetures fournissent un moyen d'associer l'état à l'action. Donc, en tant que base de compréhension, il peut être utile de considérer les fonctions imbriquées comme un moyen d'écrire des fonctions sur place pour effectuer des actions très spécifiques où les détails de ces actions seront connus au moment de l'exécution, mais vous seulement vouloir exécuter cette action à un moment ultérieur.

Il y a d'autres choses qui peuvent être faites avec des fonctions imbriquées, mais vous devez commencer quelque part.

Questions connexes