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

MATLAB Discussion :

Ajouter une propriété lors de l'appel d'une fonction.


Sujet :

MATLAB

  1. #1
    Modérateur

    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Août 2014
    Messages
    1 295
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2014
    Messages : 1 295
    Points : 2 385
    Points
    2 385
    Par défaut Ajouter une propriété lors de l'appel d'une fonction.
    Bonjour à toutes et à tous

    J'ai créé une fonction qui effectue tout un tas de trucs et qui à la fin me sort un tableau de données. J'aimerai pouvoir ajouter une "propriété"à ma fonction afin de laisser à l'utilisateur le choix d'uniquement récupérer le tableau de données ou bien d'en plus tracer les courbes correspondantes.

    Je pensais à un truc comme par exemple quand on utilise

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    plot(.....,'LineWidth',3)
    dans mon cas j'aimerai pouvoir faire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    maFonction(X1,X2,.....,'Affichage','On')
    Avec Affichage en mode Off de façon standard.

    Une idée de la façon de procédé? Je pourrais tout simplement ajouter une autre variable d'entrée et selon sa valeur faire un affichage ou non. Ou alors faire une recherche dans toute mes variables d'entrées avec un strcmp, regarder si il y a dans ces variables d'entrée un 'Affichage' et regarder ce que contient la variable suivante; mais je ne sais pas du tout si c'est comme cela que cela fonctionne dans les autres fonctions ou si il existe bel est bien une notion de "propriété" dans les fonctions. Et j'aimerai apprendre à faire des fonctions un peu plus... proprement.

    En vous remerciant par avance!
    Si vous cherchez des réponses sur ce forum il faudra avant tout expliquer clairement votre problème et exposer la démarche que vous avez entreprise pour le résoudre. Fournissez une base de travail et de réflexion à vos interlocuteurs!

  2. #2
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Bonjour,

    Il n'existe malheureusement pas en Matlab de keyword argument pour les fonction, comme en R ou en Python.

    La solution avec varargin contenant éventuellement un 'identifiant' suivi d'une valeur est l'approche courante. C'est à la fonction de renseigner la valeur par défaut si l'identifiant est absent.

    C'est une solution que je trouve peu satisfaisante, surtout en terme de lisibilité, comparer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    f(x,'mode',mon_mode) % Matlab
    f(x, mode= mon_mode) % Keyword en R ou Python
    Il y a des '' inutiles et un séparateur (=) qui manque cruellement.

    Pour améliorer la lisibilité, j'utilise parfois
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    f(x,@mode,mon_mode)
    Je trouve ca plus lisible, mais le @fonction_inexistante n'est pas vraiment documenté, donc ça cassera peut être sur la prochaine release.

    Dans tous les cas, la fonction doit effectivement parcourir varargin et comparer (isequaln >> strcmp) les valeurs aux flags. C'est relativement inefficace comparer aux keywords arguments d'autres langages. C'est également dangereux si les flags peuvent être des inputs normaux de la fonctions.

    Tu peux aussi passer par une structure d'option qui sera un unique argument.

    Enfin, plutôt que de laisser la fonction gérer ou non un mode graphique en interne, je préfère que la fonction me renvoie un handle @plot_me du traitement graphique qu'il me suffit d'appeler ensuite si je veux le dessin.

  3. #3
    Rédacteur/Modérateur

    Avatar de Jerome Briot
    Homme Profil pro
    Freelance mécatronique - Conseil, conception et formation
    Inscrit en
    Novembre 2006
    Messages
    20 302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Freelance mécatronique - Conseil, conception et formation

    Informations forums :
    Inscription : Novembre 2006
    Messages : 20 302
    Points : 53 166
    Points
    53 166
    Par défaut
    [Edit] Ooops j'ai mal lu la question, ma solution n'est pas bonne… Je laisse mon message malgré tout, ça pourra servir à d'autres

    Citation Envoyé par Gooby Voir le message
    J'aimerai pouvoir ajouter une "propriété"à ma fonction afin de laisser à l'utilisateur le choix d'uniquement récupérer le tableau de données ou bien d'en plus tracer les courbes correspondantes.
    Dans ce cas précis, tu peux tester le nombre d'argument de sortie avec nargout à la fin du code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if nargout == 0
       % Affichage
    end
    Par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    function X = test
     
    X = rand(1,5);
     
    if nargout==0   
        figure
        plot(X);    
    end
    Le comportement sera différent si tu appelles la fonction :

    ou

    Ingénieur indépendant en mécatronique - Conseil, conception et formation
    • Conception mécanique (Autodesk Fusion 360)
    • Impression 3D (Ultimaker)
    • Développement informatique (Python, MATLAB, C)
    • Programmation de microcontrôleur (Microchip PIC, ESP32, Raspberry Pi, Arduino…)

    « J'étais le meilleur ami que le vieux Jim avait au monde. Il fallait choisir. J'ai réfléchi un moment, puis je me suis dit : "Tant pis ! J'irai en enfer" » (Saint Huck)

  4. #4
    Modérateur
    Avatar de le fab
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2005
    Messages
    1 882
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 882
    Points : 3 432
    Points
    3 432
    Par défaut
    Citation Envoyé par VV33D Voir le message
    Pour améliorer la lisibilité, j'utilise parfois
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    f(x,@mode,mon_mode)
    Je trouve ca plus lisible, mais le @fonction_inexistante n'est pas vraiment documenté, donc ça cassera peut être sur la prochaine release.
    salut VV

    tu peux expliciter un peu stp, je comprend pas trop ton truc

    merci

    sinon pour répondre à la question, tu as en effet la solution varargin avec un parseur maison
    tu as aussi la solution toute simple de rajouter un flag en dernier argument, et de tester sa présence avec nargin et de le mettre à false en cas d'absence :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    function y=f(a1,a2,...an,monflag)
    narginchk-n,n+1);
    if nargin<n+1
      monflag=false;
    end
    si tu veux utiliser plusieurs attributs pour ta sortie graphique, j'utilise souvent aussi un dernier argument de type structure contenant tout ce dont j'ai besoins

  5. #5
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    salut VV tu peux expliciter un peu stp
    Et bien c'est un simple parseur maison, mais au lieu d'utiliser des strings en tant que flags, j'utiliser des @mode_name (qui est certes codé comme un string en matlab). Je trouve que ca améliore la lisibilté de mettre le @ devant le nom d'un mode. Par contre, je suis à la merci de ce que Matlab fait en interne lorsqu'il crée une fonction anonyme.

    tu as aussi la solution toute simple de rajouter un flag en dernier argument
    Très mauvaise idée dès que plusieurs modes différents sont envisagés.

    Dans tous les cas, je pense que séparer la couche graphique de la couche fonctionnelle est une bonne idée. Par exemple, le traitement graphique nécessite parfois des arguments (type de graphique, de couleur, de legende etc), et c'est quand même dommage de préciser ca à une fonction qui de base fait des calculs (et ca alourdit encore plus sa 'signature'!). Je préfère donc un truc du genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    [resultat, plot_me]= f(x) % f renvoie un handle vers son traitement graphique
    plot_me('r',{'FontSize',6}); % que j'utilise ensuite si besoin.
    Un exemple pipo de ce genre de fonctions
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    function [y,plot_me]= f(x)
    y= cos(x);
     
         function plotter_(lspec, txt_args)
            plot(x,y,lspec);
            text(x(1),y(1),'text',txt_args{:});
         end
    plot_me= @plotter_;
    end
    De cette manière la signature de f et le traitement de ses arguments reste simple, et les arguments du traitement graphique vont vers une sous-fonction différente.

    Par ailleurs, je hais qu'une fonction puisse avoir un comportement différent en fonction de nargout. C'est un obstacle quasi insurmontable pour une programmation fonctionnelle propre en matlab, puisqu'il est impossible de décorer une fonction f de manière générique, vu qu'on ne peut connaitre le nargout à utiliser. Dit autrement, nargout(f)> 0 n'est pas une garantie d'un nombre d'output fixe. J'en ai eu la désagréable expérience avec la builtin set récemment).

  6. #6
    Membre régulier
    Homme Profil pro
    Thésard
    Inscrit en
    Mars 2013
    Messages
    54
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Thésard
    Secteur : Santé

    Informations forums :
    Inscription : Mars 2013
    Messages : 54
    Points : 78
    Points
    78
    Par défaut
    Bonjour Gooby,

    Comme les messages précédents, tu peux soit ajouter un argument de plus à ta fonction et tester le dernier argument (ou le n-ième)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     function [output1, output2] = nomFonction(argument1, argument2, argument3, flag)
    if nargin == 4
    <div style="margin-left:40px">flag = 1;</div>else
    <div style="margin-left:40px">flag = 0;</div>%... Suite de ton code
    La seconde solution, que j'utilise, car un peu plus flexible, c'est d'utiliser varargin (nombre d'argument entrants variables) et de tester avec nargin.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function [output1, output2] = nomFonction(varargin)
    if nargin==4
    <div style="margin-left:40px">flag = varagin{4};</div>else
    <div style="margin-left:40px">flag = 0;</div>%.. suite de ton code
    A savoir, que tu peux également ignorer une sortie d'une fonction en utilisant le tilde (~)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    % j appelle la fonction en ignorant le second argument de sortie : 
    [val1, ~] = nomFonction(arg1, arg2, arg3);
    En espérant avoir aidé,

    Betaplus

  7. #7
    Modérateur

    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Août 2014
    Messages
    1 295
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2014
    Messages : 1 295
    Points : 2 385
    Points
    2 385
    Par défaut
    Merci à tous pour les solutions proposées, je laisse encore le sujet non résolu si vous avez des choses à ajouter.
    Si vous cherchez des réponses sur ce forum il faudra avant tout expliquer clairement votre problème et exposer la démarche que vous avez entreprise pour le résoudre. Fournissez une base de travail et de réflexion à vos interlocuteurs!

  8. #8
    Modérateur
    Avatar de le fab
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2005
    Messages
    1 882
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 882
    Points : 3 432
    Points
    3 432
    Par défaut
    Citation Envoyé par VV33D Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    f(x,'mode',mon_mode) % Matlab
    f(x, mode= mon_mode) % Keyword en R ou Python
    Il y a des '' inutiles et un séparateur (=) qui manque cruellement.
    une petite proposition :
    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
    function z = test( x,y,varargin )
    % paramètres
    a = 0;
    b = 0;
     
    %% affectation keyword
    for ii=1:numel(varargin)
        % si une affectation type keyword dans les arguments optionnels
        if ischar(varargin{ii}) && strfind(varargin{ii},'=')
            % on recherche les 2 cotés de l'affectation
            idx = strfind(varargin{ii},'=');
            varx = strtrim(varargin{ii}(1:idx-1));
            vary = strtrim(varargin{ii}(idx+1:end));
     
            % traitement si affectation d'un numerique détecté
            if ~isnan(str2double(vary))
                vary = str2double(vary);
            else
                vary = evalin('caller',vary);
            end
     
            % affectation du keyword
            assignhere(varx,vary);
        end
    end
     
    %% calcul
    z = x+y+a;
    disp(b)
     
    %% fonction d'assignation locale
    function assignhere(x,y)
    assignin('caller',x,y);
    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
    >> v = 1:3;
    >> test(2,3)
         0
    ans =
         5
    
    >> test(2,3,'a = 3')
         0
    ans =
         8
    
    >> test(2,3,'a = v','b = ''toto''')
    toto
    ans =
         6     7     8
    à améliorer probablement
    et il reste des " inutiles ...

  9. #9
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Euh... désolé, mais quelle horreur, et ce dans beaucoup de cas. Si les args optionnels...
    * Sont construits à partir de variable matlab (transmettre ses args à une autre fonction), il faut faire du num2str et concaténer dans tous les sens.
    * Sont des non entiers (floats, cell, vecteur ... ). Je vais quand même pas appeler eval ou reparser un string pour ca, ou arrondir la valeur de mon float.
    * sont des strings -> double couche de ' en plus. Si c'est strings contiennent le caractère =, c'est encore plus illisible et error-prone.
    * sont des instances des classes utilisateurs pour lesquels je ne peux pas reconstruire l'objet à parti de son str (en supposant d'ailleurs que j'arrive à le transformer en sting).
    * si des arguments classiques de la fonction sont de strings contenant = !

    De plus, c'est très lourd (O(nargin) + evalincaller), même si j'admet que toutes les solutions envisagées font également for sur les arguments afin d'identifier les mots-clef. C'est d'ailleurs toute la différence avec R ou Python, ou un dictionnaire permet un accès en O(log(nargin)) aux valeurs.

    Il me semble que la syntaxe
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    f(x,y,@(key1)value1,@(key2)value2)
    est préférable (je peux récupérer la valeur directement quelle que soit sont type en appelant l'option, et le nom de l'option par regexp). Toutefois, cela interdit empêcherait surement des fhandle en arguments simple de la fonction à moins peut etre de faire @(IDENTIFIANT D'OPTION,key1) value1.

    A la limite, je préfère une userclass OPTIONS surchargeant subsref et qui permette d'écrire.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    f(x,y,OPTIONS.new.param1{value1}.param2{value2})
    new est une instance statique de la classe (malheureusement, un nouvel OPTIONS() ne pourrait être suivi d'une séquence d'indexation sans l'assigner à une variable d'abord), et subsref traite d'un coup la séquence des .{} pour renvoyer une structure ou un dictionnaire. En interne, la fonction n'aurait qu'à vérifier le type du dernier arg (ou de tous si vous préférez) pour identifier ses options.

    Une syntaxe alternative du type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    OPTIONS():'key1':value1:'key2':value2
    est aussi envisageable mais moins agréable (moins lisible, et colon ne peut pas traiter la séquence d'un coup contrairement à subsref - cela dit le fait que subsref traite la séquence d'un coup jusqu'au 1er () n'est pas vraiment documenté).

  10. #10
    Modérateur
    Avatar de le fab
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2005
    Messages
    1 882
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 882
    Points : 3 432
    Points
    3 432
    Par défaut

    bah, c'était juste pour m'amuser
    en général j'utilise soit une structure contenant ce dont j'ai besoin (pour les options de graphiques) soit des arguments par paire et une parseur maison

    j'ai regarder un peu tes handles de fonction inexistantes et je suis pas super convaincu :
    je préfère rester dans la philosophie matlab avec des arguments par paire, je trouve que ça change pas grand chose à la lisibilité

  11. #11
    Membre éprouvé
    Avatar de ol9245
    Homme Profil pro
    Chercheur
    Inscrit en
    Avril 2007
    Messages
    985
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Avril 2007
    Messages : 985
    Points : 1 158
    Points
    1 158
    Billets dans le blog
    1
    Par défaut addparamvalue
    ce que tu demandes est prévu dans matlab.
    Il faut utiliser la classe inputParser
    puis la fonction AddParamValue
    puis la fonction parse

    voici un bout de code à moi qui t'éclairera :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function W = build_work(E, varargin)
    % 'first_image', 'last_image', 'blocksize', 'verbosity'
    p = inputParser ;
    p.addParamValue('first_image', 1,   @(x)validateattributes(x, {'numeric'}, {'scalar', 'positive', 'integer', 'nonempty'}))
    p.addParamValue('last_image', 1e10, @(x)validateattributes(x, {'numeric'}, {'scalar', 'positive', 'integer', 'nonempty'}))
    p.addParamValue('blocksize', 10,    @(x)validateattributes(x, {'numeric'}, {'scalar', 'positive', 'integer', 'nonempty'}))
    p.addParamValue('verbosity', 9,     @(x)validateattributes(x, {'numeric'}, {'scalar', 'nonnegative', 'integer', 'nonempty'}))
     
    p.parse(varargin{:}) ;
    W = p.Results ;
    "La vraie grandeur se mesure par la liberté que vous donnez aux autres, et non par votre capacité à les contraindre de faire ce que vous voulez." Larry Wall, concepteur de Perl.

  12. #12
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Je tenais juste à signaler que ces fonctions utilitaires aident à réaliser le parsing. Elles sont certes pratiques et lisibles (le code de traitement des args est lisible), mais ne résolvent pas tous les problèmes évoqués dans ce thread:
    - Si varargin contient des arguments optionnels qui ne sont pas des modes/paramètres (en particulier des strings), risque de confusion.
    - Syntaxe d'appel toujours désagréable, pour les raisons déjà évoquées.
    - Complexité toujours en O(nargin) + utilisation de sous fonctions (copie des arguments => problèmes OutOfMemory possible)
    - Toujours aucun moyen d'interroger la fonction de l'extérieur sur ses arguments par défaut.

Discussions similaires

  1. Utilisation d'une variable lors de l'appel de la fonction
    Par guy16 dans le forum Général Python
    Réponses: 18
    Dernier message: 12/05/2015, 15h47
  2. Réponses: 3
    Dernier message: 08/10/2014, 15h48
  3. Réponses: 5
    Dernier message: 06/01/2006, 11h41
  4. [POO] Problème lors de l'appel d'une propriété d'un objet.
    Par akecoocoo dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 24/08/2005, 08h51
  5. [Language]problème lors de l'appel d'une méthode
    Par Samanta dans le forum Langage
    Réponses: 6
    Dernier message: 18/05/2005, 13h03

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