IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

VBA Access Discussion :

Optimisation d'une fonction récursive


Sujet :

VBA Access

  1. #1
    Futur Membre du Club
    Inscrit en
    Mai 2010
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Mai 2010
    Messages : 8
    Points : 6
    Points
    6
    Par défaut Optimisation d'une fonction récursive
    Bonjour,
    Dans un soucis de durée d'exécution, j'aimerais avoir des conseils pour optimiser le code VBA de la fonction récursive ci-dessous.

    Je vais tenter de donner toutes les informations nécessaires :
    (modèle des relations en pièce jointe)

    Dans le bâtiment industriel il existe un certain nombre de tableaux de distribution électrique qui comportent des organes de coupures : disjoncteurs, fusibles, et autres interrupteurs.
    Chaque tableau alimente un ou plusieurs tableaux, qui alimentent d'autres tableaux, et ainsi de suite, jusqu'aux machines ou aux servitudes (on appelle servitude, tout élément pouvant faire entrave au confort, au travail, ou à la sécurité du personnel).
    Chaque organe de coupure est donc un père ou un fils d'un autre organe, jusqu'aux extrémités.
    La distribution impose que 2 organes peuvent être mutuellement pères et fils (couplages) et qu'un organe peut avoir plusieurs pères.
    D'où l'existence des tables de jointure qui répertorient toute relation existant entre deux organes (on considère une source comme un organe).

    But : lorsqu'on coupe un ou des organes de coupure, savoir toutes les servitudes qui ne sont plus alimentées. L'idée de la fonction récursive et de partir de chaque servitude et de remonter père après père afin de trouver un chemin susceptible de l'alimenter un courant.

    NOTE : Le code fonctionne correctement

    Evénement sur le bouton "calcul"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    Private Sub btn_Calcul_Click()
     
    DoCmd.SetWarnings False
     
    'réinitialise toutes les alims à la valeur 0
    Dim strSql As String
    strSql = "UPDATE Organes SET Organes.Alimentation = NULL"
    DoCmd.RunSQL strSql
     
    Dim qdf As DAO.QueryDef
    Dim rcs As Recordset
     
    'définit la valeur d'alim des organes directement liés les sources
    Set qdf = CurrentDb.QueryDefs("QRY_SetAlimAmont")
    qdf.Execute
    'rafraichit la liste des ID des servitudes
    Set qdf = CurrentDb.QueryDefs("QRY_DelNonPere")
    qdf.Execute
    Set qdf = CurrentDb.QueryDefs("QRY_NonPere")
    qdf.Execute
    Set qdf = Nothing
     
    Dim val As Integer
    Set rcs = CurrentDb.OpenRecordset("tbl_TempFilsLst")
     
    'récupération des ID des servitudes et appel de la fonction récursive
    'rcs.MoveFirst
    While Not rcs.EOF
    val = rcs.Fields(0).Value
    Calcul (val)
    rcs.MoveNext
    Wend
     
    Set rcs = Nothing
    Set qdf = Nothing
     
    DoCmd.SetWarnings True
     
    End Sub
    Module "Calcul_Alim"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    Option Compare Database
    Option Explicit
     
    'Fonction récursive de calcul des Alimentations des servitudes
    Function Calcul(ByVal ident As Integer) As Boolean
     
    Dim strNameTable As String
    Dim IsNoErr As Boolean
    Dim tbl As DAO.TableDef
    Dim rst As DAO.Recordset
     
    'crée une table temporaire pour afficher les pères de l'organe ident
    strNameTable = "tbl_TempPereLst" & ident
    IsNoErr = CreerTable(strNameTable)
     
    'si la table a été créée, continuer la procédure, sinon terminer la procédure
    If IsNoErr = False Then GoTo Err
     
    'insère les pères dans la table nouvellement créée
    Dim strSql2 As String
     
    strSql2 = "INSERT INTO " & strNameTable & " (Pere, Etat, Alimentation)"
    strSql2 = strSql2 + " SELECT tj_Peres.Pere AS Pere, Organes.Etat AS Etat, Organes.Alimentation AS Alimentation"
    strSql2 = strSql2 + " FROM tj_Peres INNER JOIN Organes ON tj_Peres.Pere=Organes.ID"
    strSql2 = strSql2 + " WHERE tj_Peres.Fils = " & ident & ";"
    DoCmd.RunSQL strSql2
    Set rst = CurrentDb.OpenRecordset(strNameTable)
     
    Dim cpt As Integer
    Dim ptr As Boolean 'définira la valeur de l'alimentation de l'ID de l'organe en cours
    Dim val As Integer
     
    'calcule l'alimentation de chaque père et affecte sa valeur au ptr
    'calcul du ptr : l'organe est alimenté si au moins un de ses pères est alimenté et enclenché
    Do While Not rst.EOF
        If (rst.Fields(1) = True And rst.Fields(2) = True) Then
            ptr = True 'ptr = true si l'état et l'alimentation du père = true
            Exit Do 'et on sort de la boucle
        ElseIf (rst.Fields(1) = True And rst.Fields(2) = False) Then 'si l'état  = true et l'alimentation = false
            val = rst.Fields(0).Value 'on rappelle la fonction Calcul avec l'ID du père en paramètre
            ptr = Calcul(val) 'et on affecte au ptr la valeur de la fonction
            If ptr = True Then Exit Do 'si ptr = true, on sort de la boucle
        Else: ptr = False 'si l'état du père est false, le ptr reste false et on passe au père suivant
        End If
    rst.MoveNext
    Loop
     
    Calcul = ptr
     
    'affecte la valeur de ptr (true/false) dans le champ Alimentation de la table Organes pour l'ID en cours
    Dim qdf As DAO.QueryDef
    Set qdf = CurrentDb.QueryDefs("QRY_SetAlim")
    With qdf
        .Parameters("VALEUR") = ident
        .Parameters("STATE") = ptr
        .Execute
    End With
     
    'libère les variables
    Set qdf = Nothing
    Set rst = Nothing
     
    'supprime la table temporaire
    Dim strSql As String
    strSql = "DROP TABLE " & strNameTable
    DoCmd.RunSQL strSql
     
    Err:
    End Function
     
     
    'Fonction de création de la table temporaire
    Function CreerTable(nomtable As String) As Boolean
    On Error GoTo Err
     
    Dim oDb As DAO.Database
    Dim oNouvelleTable As DAO.TableDef
    Dim oChamp As DAO.Field
    Dim oIndex As DAO.Index
    'Instancie la base de données
        Set oDb = CurrentDb
    'Crée la nouvelle table
        Set oNouvelleTable = oDb.CreateTableDef(nomtable)
    'Crée le champ Pere
        Set oChamp = oNouvelleTable.CreateField("Pere", dbLong)
    'Ajoute le champ à la table
        oNouvelleTable.Fields.Append oChamp
    'Crée le champ Etat et l'ajoute
        oNouvelleTable.Fields.Append oNouvelleTable.CreateField("Etat", dbBoolean)
    'Crée le champ Alimentation et l'ajoute
       oNouvelleTable.Fields.Append oNouvelleTable.CreateField("Alimentation", dbBoolean)
    'Ajoute la table à la base de données
        oDb.TableDefs.Append oNouvelleTable
     
    'Libère les variables
    oDb.Close
    Set oIndex = Nothing
    Set oChamp = Nothing
    Set oNouvelleTable = Nothing
    Set oDb = Nothing
     
    CreerTable = True 'la fonction renvoie true si elle a créé la table
     
    Err: 'la fonction renvoie false si elle n'a pas pu créer la table (table existante)
    End Function
    Requête "QRY_SetAlim"
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    PARAMETERS VALEUR Long, STATE Bit;
    UPDATE Organes SET Organes.Alimentation = [STATE]
    WHERE (((Organes.ID)=[VALEUR]));


    Je peux d'ores-et-déjà soulever une question :
    L'idéal serait d'initialiser toutes les alims à un état indéterminé et d'affecter ensuite la valeur true ou false à l'organe en cours de calcul.
    On peut (puisque cela fonctionne) dans une requête, faire "SET Alimentation = NULL" pour un type booléen. Mais je n'ai pas l'impression qu'Access sait gérer le type correctement puisque les alimentations sont mises à la valeur "0" (False).
    De ce fait, lorsque l'on tombe sur le cas d'organe père avec Etat = True et Alimentation = False, on ne sait donc pas si cet organe a déjà été calculé ou pas. D'où la bidouille au niveau de la boucle ! On est donc obligé de rappeler à chaque fois la fonction lorsque ce cas se présente.
    Quelqu'un sait donc comment affecter réellement la valeur NULL à un booléen dans une table ? Ou il faut passer par une autre solution comme changer le type en integer, ou remplir une table "organes calculés" (qu'on consulterait à chaque passage dans la boucle)

  2. #2
    Membre confirmé Avatar de Tonioyo
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2008
    Messages
    343
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2008
    Messages : 343
    Points : 518
    Points
    518
    Par défaut
    Bonjour et bienvenue sur developpez.com,

    Pour avoir trois états différents, il faut autre chose qu'un booléen. probablement un type Single qui prendra les valeurs de 1 à 3. Mais ce qui prends du temps c'est la récursivité.

    Attention je n'ai pas testé le code publié ci-dessous, j'ai peut-être comis des erreurs de frappe.

    Si j'ai bien compris, le coeur de votre soucis se situe dans cette partie de code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    'calcule l'alimentation de chaque père et affecte sa valeur au ptr
    'calcul du ptr : l'organe est alimenté si au moins un de ses pères est alimenté et enclenché
    Do While Not rst.EOF
        If (rst.Fields(1) = True And rst.Fields(2) = True) Then
            ptr = True 'ptr = true si l'état et l'alimentation du père = true
            Exit Do 'et on sort de la boucle
        ElseIf (rst.Fields(1) = True And rst.Fields(2) = False) Then 'si l'état  = true et l'alimentation = false
            val = rst.Fields(0).Value 'on rappelle la fonction Calcul avec l'ID du père en paramètre
            ptr = Calcul(val) 'et on affecte au ptr la valeur de la fonction
            If ptr = True Then Exit Do 'si ptr = true, on sort de la boucle
        Else: ptr = False 'si l'état du père est false, le ptr reste false et on passe au père suivant
        End If
    rst.MoveNext
    Loop
    Tout d'abord je souhaite attirer votre attention sur un effet de bord, il s'agit de l'écriture du code. Je ne remet absolument pas en cause le bien fondé de ce code ni la manière dont il à été écrit, je souhaite juste apporter un peu de lumière sur l'amélioration de celui-ci.

    Une des premières règle à respecter serrait de mettre toutes les déclarations de variables en début de fonction ou de procédure, cela va nous montrer que certaine variable sont inutile (comme à la fin de la fonction Calcul: Supprime la table temporaire).
    Encore une petite astuce, l'esprit humain à des difficulté à comprendre la négation, il serait plus efficace d'écrire IsErr à la place de IsNoErr et d'inverser la logique de cette variable dans la fonction.
    Un autre indice, si une fonction ou une procédure fait plusieurs "chose" (dans le sens action) il ne faut pas hésiter à la découper par exemple, j'ai pour habitude de détacher une requete SQL en la méttant dans une fonction avec un nom qui lui est propre. De cette manière le code deviendra beaucoup plus facile à lire et donc à maintenir et éclaircira à coup sûr la route vers la solution au problème.

    (Le livre que je mentionne dans ma signature donne pleins de suggestions de bons procédés comme ceux que j'ai cité.)

    Ceci ne vas pas fondamentalement optimiser le code mais va nous montrer la voie.

    La gestion des erreurs n'est pas correcte, si l'on souhaite arrêter une fonction il existe l'instruction Exit Function, de même pour une procédure Exit Sub.

    Par défaut une variable déclarée Boolean prends la valeur False.

    Du coup dans la boucle récursive pourquoi ne pas la couper en deux ? Il semblerai que les instructions If contenues dans celle-ci constituent des requetes de séléction. Peut-être serrait-il possible de faire deux fonctions récursives.

    En gardant l'unicité de la boucle on se retrouve avec ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    Option Compare Database
    Option Explicit
     
    'Fonction récursive de calcul des Alimentations des servitudes
    Function Calcul(ByVal ident As Integer) As Boolean
    On Error GoTo Err_Calcul
     
      'Variables de type objet
      Dim rst As DAO.Recordset
      Dim qdf As DAO.QueryDef
     
      'Variables de type primitif
      Dim strNameTable As String
      Dim ptr As Boolean 'définira la valeur de l'alimentation de l'ID de l'organe en cours
     
     
      'crée une table temporaire pour afficher les pères de l'organe ident
      strNameTable = "tbl_TempPereLst" & ident
     
      'si la table a été créée, continuer la procédure, sinon terminer la procédure
     
      'Attention il serrait vraiment judicieux d'inverser le retour de la fonction creerTable() avec un Not
      If Not CreerTable(strNameTable) Then Exit Function
     
      'insère les pères dans la table nouvellement créée
      DoCmd.RunSQL insererPeres(strNameTable, ident)
      Set rst = CurrentDb.OpenRecordset(strNameTable)
     
      'calcule l'alimentation de chaque père et affecte sa valeur au ptr
      'calcul du ptr : l'organe est alimenté si au moins un de ses pères est alimenté et enclenché
      Do While Not rst.EOF
          If (rst.Fields(1) = True And rst.Fields(2) = True) Then
              Calcul = True 'ptr = true si l'état et l'alimentation du père = true
              affecter ident, True
              Exit Function 'et on sort de la fonction apres avoir fait l'affectation
          ElseIf (rst.Fields(1) = True And rst.Fields(2) = False) Then 'si l'état  = true et l'alimentation = false
              'on rappelle la fonction Calcul avec l'ID du père en paramètre
              'et on affecte au ptr la valeur de la fonction
              If Calcul(rst.Fields(0).Value) Then
                affecter ident, ptr
                Calcul = True
                Exit Function 'si ptr = true, on sort de la fonction apres avoir fait l'affectation
              End If
          End If
      rst.MoveNext
      Loop
     
      affecter ident, ptr
     
      'libère les variables
      Set qdf = Nothing
      Set rst = Nothing
     
      'supprime la table temporaire
      DoCmd.RunSQL "DROP TABLE " & strNameTable
      Exit Function
     
    Err_Calcul:
      MsgBox Err.Description, vbCritical, "Erreur n°" & Err.Number
      'libère les variables en cas d'erreur
      Set qdf = Nothing
      Set rst = Nothing
    End Function
     
    Function insererPeres(ByRef strNameTable As String, ByRef ident As Integer) As String
      insererPeres = "INSERT INTO " & strNameTable & " (Pere, Etat, Alimentation)"
      insererPeres = insererPeres + " SELECT tj_Peres.Pere AS Pere, Organes.Etat AS Etat, Organes.Alimentation AS Alimentation"
      insererPeres = insererPeres + " FROM tj_Peres INNER JOIN Organes ON tj_Peres.Pere=Organes.ID"
      insererPeres = insererPeres + " WHERE tj_Peres.Fils = " & ident & ";"
    End Function
     
    Private Sub affecter(ByRef ident As Integer, ByRef ptr As Boolean)
      'affecte la valeur de ptr (true/false) dans le champ Alimentation de la table Organes pour l'ID en cours
        With CurrentDb.QueryDefs("QRY_SetAlim")
          .Parameters("VALEUR") = ident
          .Parameters("STATE") = ptr
          .Execute
      End With
    End Sub

    A partir de là on peu couper en deux la boucle récursive, qui apparement n'est qu'à moitié récursive.

    Attention je ne connais le nom des champs, je les ai donc remplacés par Champ1 et Champ2.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    Option Compare Database
    Option Explicit
     
    'Fonction récursive de calcul des Alimentations des servitudes
    Function Calcul(ByVal ident As Integer) As Boolean
    On Error GoTo Err_Calcul
     
      'Variables de type objet
      Dim rst As DAO.Recordset
      Dim qdf As DAO.QueryDef
     
      'Variables de type primitif
      Dim strNameTable As String
      Dim ptr As Boolean 'définira la valeur de l'alimentation de l'ID de l'organe en cours
     
     
      'crée une table temporaire pour afficher les pères de l'organe ident
      strNameTable = "tbl_TempPereLst" & ident
     
      'si la table a été créée, continuer la procédure, sinon terminer la procédure
     
      'Attention il serrait vraiment judicieux d'inverser le retour de la fonction creerTable() avec un Not
      If Not CreerTable(strNameTable) Then Exit Function
     
      'insère les pères dans la table nouvellement créée
      DoCmd.RunSQL insererPeres(strNameTable, ident)
      'Boucle avec premières conditions
      Set rst = CurrentDb.OpenRecordset("SELECT * FROM " & strNameTable & "WHERE Champ1 = True AND Champ2 = True;")
     
      While Not rst.EOF
        If (rst.Fields(1) = True And rst.Fields(2) = True) Then
          Calcul = True 'ptr = true si l'état et l'alimentation du père = true
          affecter ident, True
          Exit Function 'et on sort de la fonction apres avoir fait l'affectation
        End If
        rst.MoveNext
      Wend
     
     
      'Boucle avec secondes conditions
      Set rst = CurrentDb.OpenRecordset("SELECT * FROM " & strNameTable & "WHERE Champ1 = True AND Champ2 = False;")
     
      'calcule l'alimentation de chaque père et affecte sa valeur au ptr
      'calcul du ptr : l'organe est alimenté si au moins un de ses pères est alimenté et enclenché
      While Not rst.EOF
        If (rst.Fields(1) = True And rst.Fields(2) = False) Then 'si l'état  = true et l'alimentation = false
          'on rappelle la fonction Calcul avec l'ID du père en paramètre
          'et on affecte au ptr la valeur de la fonction
          If Calcul(rst.Fields(0).Value) Then
            affecter ident, ptr
            Calcul = True
            Exit Function 'si ptr = true, on sort de la fonction apres avoir fait l'affectation
          End If
        End If
        rst.MoveNext
      Wend
     
      affecter ident, ptr
     
      'libère les variables
      Set qdf = Nothing
      Set rst = Nothing
     
      'supprime la table temporaire
      DoCmd.RunSQL "DROP TABLE " & strNameTable
      Exit Function
     
    Err_Calcul:
      MsgBox Err.Description, vbCritical, "Erreur n°" & Err.Number
      'libère les variables en cas d'erreur
      Set qdf = Nothing
      Set rst = Nothing
    End Function
     
    Function insererPeres(ByRef strNameTable As String, ByRef ident As Integer) As String
      insererPeres = "INSERT INTO " & strNameTable & " (Pere, Etat, Alimentation)"
      insererPeres = insererPeres + " SELECT tj_Peres.Pere AS Pere, Organes.Etat AS Etat, Organes.Alimentation AS Alimentation"
      insererPeres = insererPeres + " FROM tj_Peres INNER JOIN Organes ON tj_Peres.Pere=Organes.ID"
      insererPeres = insererPeres + " WHERE tj_Peres.Fils = " & ident & ";"
    End Function
     
    Private Sub affecter(ByRef ident As Integer, ByRef ptr As Boolean)
      'affecte la valeur de ptr (true/false) dans le champ Alimentation de la table Organes pour l'ID en cours
      With CurrentDb.QueryDefs("QRY_SetAlim")
          .Parameters("VALEUR") = ident
          .Parameters("STATE") = ptr
          .Execute
      End With
    End Sub
    Ensuite je sortirai les deux boucles dans deux fonctions / procédures séparées. il y aurait une méthode récursive et une méthode avec une boucle. Du coup la méthode récursive contiendrai moins d'opérations et serrai un peu plus efficace à l'éxécution.

    Cordialement,
    loi de LeBlanc : Plus tard signifie jamais. extrait de Coder proprement Auteur:Robert C. Martin

  3. #3
    Futur Membre du Club
    Inscrit en
    Mai 2010
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Mai 2010
    Messages : 8
    Points : 6
    Points
    6
    Par défaut
    Merci pour votre aide.
    J'ai testé votre code mais il comporte un certain nombre d'erreurs. Tout d'abord le temps d'exécution qui devrait diminuer pour une optimisation, augmente énormément (de 35 à 85s avec ma base actuelle). Certains résultats de calculs ne sont pas correct et la méthode ne supprime pas toutes les tables une fois son exécution terminée.
    Attention je ne dis pas que vous êtes un mauvais codeur, mais les erreurs proviennent peut être de la compréhension du sujet (peut être n'ai je pas été assez clair).
    J'ai donc repris votre code et l'ai adapté :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    Option Compare Database
    Option Explicit
     
    'Fonction récursive de calcul des Alimentations des servitudes
    Function Calcul(ByVal ident As Integer) As Boolean
    On Error GoTo Err_Calcul
     
      'Variables de type objet
      Dim rst As DAO.Recordset
      Dim qdf As DAO.QueryDef
     
      'Définit le nom de la table des pères pour l'organe en cours
      Dim strNameTable As String
      strNameTable = "tbl_TempPereLst" & ident
     
      'si la table a été créée, continuer la procédure, sinon terminer la procédure
      If Not CreerTable(strNameTable) Then Exit Function
     
      'insère les pères dans la table nouvellement créée
      DoCmd.RunSQL insererPeres(strNameTable, ident)
     
      'Boucle avec premières conditions
      Set rst = CurrentDb.OpenRecordset("SELECT * FROM " & strNameTable & " WHERE Etat = True AND Alimentation = True;")
     
        If rst.RecordCount <> 0 Then
          Calcul = True 'ptr = true si l'état et l'alimentation du père = true
          affecter ident, True
            'libère les variables
             Set qdf = Nothing
             Set rst = Nothing
            'supprime la table temporaire
            DoCmd.RunSQL "DROP TABLE " & strNameTable
          Exit Function 'et on sort de la fonction apres avoir fait l'affectation
        End If
     
     
      'Boucle avec secondes conditions
      Set rst = CurrentDb.OpenRecordset("SELECT * FROM " & strNameTable & " WHERE Etat = True AND Alimentation = False;")
     
      While (Not rst.EOF And Not Calcul)
          'on rappelle la fonction Calcul avec l'ID du père en paramètre
          'on appelle ensuite la fonction affecter avec le résultat du calcul en paramètre
          affecter ident, Calcul(rst.Fields(0).Value)
          rst.MoveNext
      Wend
     
      'libère les variables
      Set qdf = Nothing
      Set rst = Nothing
     
      'supprime la table temporaire
      DoCmd.RunSQL "DROP TABLE " & strNameTable
      Exit Function
     
    Err_Calcul:
      MsgBox Err.Description, vbCritical, "Erreur n°" & Err.Number
      'libère les variables en cas d'erreur
      Set qdf = Nothing
      Set rst = Nothing
      DoCmd.RunSQL "DROP TABLE " & strNameTable
    End Function
    Le temps d'exécution descend à 15 secondes, ce qui est un gain considérable. Par contre, il subsiste encore quelques erreurs dans le résultat des alimentations.

    A partir de là je peux ensuite changer le type du champ Alimentation et adapter la fonction.

  4. #4
    Membre confirmé Avatar de Tonioyo
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2008
    Messages
    343
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2008
    Messages : 343
    Points : 518
    Points
    518
    Par défaut
    Bonjour,

    Vous avez très bien fait de reprendre et corriger le code que j'ai écrit car j'étais totalement aveugle lors de son écriture et n'avait pas la structure de la base ni celle ci pour le tester.

    Pour augmenter encore l'efficacité de la méthode récursive, il faut faire appel à la recherche opérationnelle. Il s'agit de mathématiques appliqués à l'informatique, et la partie qui, à mon avis, concerne la méthode récursive est la recherche dans les arbres. Il existe deux techniques, par profondeur d'abord et par largeur d'abord. Tout ceci est très lié à la théorie des graphes. Je pense que la théorie des graphes devrait beaucoup vous intéresser car elle s'applique à tout ce qui constitue un réseau (informatique ou électrique).


    Si il reste des erreurs dans le code il est possible qu'il manque un ou plusieurs critère, et donc une ou plusieurs boucle supplémentaire à poser. Je pense surtout si le nombre d'états possible pour l'alimentation change.
    loi de LeBlanc : Plus tard signifie jamais. extrait de Coder proprement Auteur:Robert C. Martin

Discussions similaires

  1. Optimisation d'une fonction récursive
    Par lawwek dans le forum Scheme
    Réponses: 1
    Dernier message: 10/05/2009, 01h56
  2. Optimisation d'une fonction
    Par BNS dans le forum C++
    Réponses: 7
    Dernier message: 15/12/2007, 22h25
  3. Réponses: 6
    Dernier message: 27/06/2007, 16h44
  4. [fonction d'Ackermann] Écrire une fonction récursive
    Par java+ dans le forum Mathématiques
    Réponses: 5
    Dernier message: 19/06/2007, 01h14
  5. Réponses: 6
    Dernier message: 24/05/2007, 17h18

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo