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

Langage Perl Discussion :

avoir une fonction min() avec des objets


Sujet :

Langage Perl

  1. #1
    Membre habitué

    Homme Profil pro
    Statisticien
    Inscrit en
    Novembre 2010
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Statisticien

    Informations forums :
    Inscription : Novembre 2010
    Messages : 122
    Points : 134
    Points
    134
    Par défaut avoir une fonction min() avec des objets
    Bonjour,

    J'essaye de retrouver dans un tableau de point (objet Moose), le point le plus à gauche.
    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
     
    #Point.pm
    use v5.10;
    package Point;
    use Moose;
    has 'x', is => 'rw', required => 1;
    has 'y', is => 'rw', required => 1;
    return 1;
     
    #!/usr/bin/perl
    use v5.10;
    use Point;
    use Data::Dumper;
    use List::Util qw(first max maxstr min minstr reduce shuffle sum);
    sub min_o (&@){
        my $code_ref = shift;
        return reduce {$code_ref->($a) < $code_ref->($b) ? $a : $b} @_;
    }
     
    my @pts;
    push(@pts,Point->new(x=>1,y=>1),Point->new(x=>0,y=>0));
    say Dumper reduce {$a->x < $b->x ? $a : $b} @pts;
    say Dumper min_o {$_->x} @pts;
    Le "reduce" fonctionne bien, mais comme parfois je cherche le point le plus à droite, ou ceci ou cela, je me disais que je pourrais créer un fonction min_o (o pour objet), ou je passe en fonction anonyme le critère de recherche du minima, dans mon cas, la réel $_->x, mais ça pourrait être un calcul plus compliqué qui renvoie un réel.

    Cela permettrait d'écrire à chaque fois
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    min_o {fonction_renvoyant_un_réel} @pts
    mais ça ne marche pas.

    Bon, je débute dans les fonctions anonymes et je me suis inspiré (un grand merci) des 3 articles "La programmation fonctionnelle en Perl"

    une idée ?

  2. #2
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    je ne comprends pas très bien.

    Déjà, la fonction:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    sub min_o (&@){
        my $code_ref = shift;
        return reduce {$code_ref->($a) < $code_ref->($b) ? $a : $b} @_;
    }
    marche-t-elle ou pas?

    Si oui, il me semble comprendre que tu voudrais tu voudrais remplacer le bout de code actuellement passé à reduce (c'est-à-dire {$code_ref->($a) < $code_ref->($b) ? $a : $b}) par une code-ref pour rendre ta fonction min_o plus générique. C'est cela? Si oui, montre ton code, parce que je ne vois pas trop où est le problème.

  3. #3
    Membre habitué

    Homme Profil pro
    Statisticien
    Inscrit en
    Novembre 2010
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Statisticien

    Informations forums :
    Inscription : Novembre 2010
    Messages : 122
    Points : 134
    Points
    134
    Par défaut
    Bonjour,

    En effet, en te lisant, je vois que je n'ai pas été clair.

    La fonction "reduce" ligne 22 fonctionne.
    Mais j'aimerais la réduire à celle de la ligne 23 qui ne fonctionne pas.
    La j'ai le message d'erreur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Can't call method "x" on an undefined value at ./essai.pl line 13. (23 dans mon post)
    Visiblement, Perl ne comprend pas que j'ai envie dans min_o de remplacer
    $code_ref->($a)
    par vu que code_ref vaut $_->x
    $a->x

    en faisant différents tests, j'ai l'impression qu'il ne passe pas bien la fct anonyme $_->x.

    Mon code est intégralement repris dans mon premier post. Juste Point.pm pour le 1er partie et essai.pl (par exemple :) pour la deuxième.

  4. #4
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Je ne suis toujours pas sûr de bien comprendre, mais j'ai essayé ceci à la ligne de commande bash:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    perl -e '
    use Data::Dumper;
    use List::Util qw(reduce);
    sub min_o (&@){
        my $code_ref = shift;
        return reduce {$code_ref->($a) < $code_ref->($b) ? $a : $b} @_;
    }
    my @toto = ( {c => 3}, {c => 6}, {c => 2}, {c => 9}, {c => 7}, {c => 1}, {c => 17});
    print Dumper min_o {$_->{c}} @toto;
    '
    $VAR1 = {
              'c' => 1
            };
    Donc, ça a l'air de marcher, mais je ne suis pas 100% sûr que cela corresponde à ce que tu désires faire.

  5. #5
    Membre habitué

    Homme Profil pro
    Statisticien
    Inscrit en
    Novembre 2010
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Statisticien

    Informations forums :
    Inscription : Novembre 2010
    Messages : 122
    Points : 134
    Points
    134
    Par défaut
    j'obtiens 17... que ce soit en ligne de commande que dans un .pl (je ne comprend pas pourquoi d'ailleurs)

    Qu'est ce que je veux faire...

    my $pt_gauche= reduce {$a->x < $b->x ? $a : $b} @pts;

    est satisfaisant. Cela me permet de trouver le point le plus à gauche, si je veux trouver le point le plus à droite,je dois écrire

    my $pt_droit= reduce {$a->x > $b->x ? $a : $b} @pts;

    et le point le plus haut (y inversé vu perl/TK) et le plus bas

    my $pt_haut= reduce {$a->y < $b->y ? $a : $b} @pts;
    my $pt_bas= reduce {$a->y > $b->y ? $a : $b} @pts;

    je préfèrerais
    my $pt_gauche= min_o {$_->x} @pts;
    my $pt_droite= max_o {$_->x} @pts;
    my $pt_haut= min_o {$_->y} @pts;
    my $pt_bas= max_o {$_->y} @pts;

    car la fonction générique de min en orienté objet (ou avec des records) est pour deux objets
    f(a)<f(b)?a:b
    f(x) retournant un réel, un point (x1,y1) ne pouvant pas être plus petit qu'un autre point (x2,y2), on doit, dans le cadre du programme, résumer le point à un réel que l'on peut comparer.

    Cela me permet d'écrire une bonne fois pour toute min_o (et max_o, ...) et de les appeler avec le critère que je désire. Cela pourrait être par exemple, quel est le point le plus proche de l'origine, et j'écris alors
    my $pt_proche_origine= min_o {sqrt($_->x*$_->x+$_->y*$_->y} @pts;

  6. #6
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Désolé, je n'ai pas copié-collé le code du bon essai (j'étais sur une tablette dans le train entre la manif de Paris et mon domicile quand j'ai écrit mon post précédent, et donc sans souris, et je m'emmêle parfois les pinceaux dans les copier-coller sans souris). Essaie avec ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    use Data::Dumper;
    use List::Util qw(reduce);
    sub min_o (&@){
        my $code_ref = shift;
        return reduce {$code_ref->($a) < $code_ref->($b) ? $a : $b} @_;
    }
    my @toto = ( {c => 3}, {c => 6}, {c => 2}, {c => 9}, {c => 7}, {c => 1}, {c => 17});
    print Dumper min_o {$_[0]->{c}} @toto;
    Ce qui donne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $VAR1 = {
              'c' => 1
            };
    Tu peux même l'écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    use Data::Dumper;
    use List::Util qw(reduce);
    sub min_o (&@){
        my $code_ref = shift;
        return reduce {$code_ref->($a) < $code_ref->($b) ? $a : $b} @_;
    }
    my @toto = ( {c => 3}, {c => 6}, {c => 2}, {c => 9}, {c => 7}, {c => 1}, {c => 17});
    print Dumper min_o {shift->{c}} @toto;
    Cela dit, si tu as lu mes articles, tu ne seras pas très étonné que je serais enclin à transformer la fonction min_o en une fonction plus générique prenant en entrée une code_ref et le tableau, en faisant en sorte que la code_ref définisse le comportement attendu, qu lieu qu'il soit codé en dur dans min_o:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    use Data::Dumper;
    use List::Util qw(reduce);
    sub generic_o (&@){
        my $code_ref = shift;
        return reduce {$code_ref->($a, $b)} @_;
    }
    my @toto = ( {c => 3}, {c => 6}, {c => 2}, {c => 9}, {c => 7}, {c => 1}, {c => 17});
    print Dumper generic_o {my ($f, $g) = @_; $f->{c} < $g->{c} ? $f : $g} @toto;
    De cette façon generic_o permet de trouver le min, le max, la moyenne, ou toute valeur intéressante.

    Pour trouver le max, une ligne à changer dans la fonction appelante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    print Dumper generic_o {my ($f, $g) = @_; $g->{c} < $f->{c} ? $f : $g} @toto;
    Cela dit, tu programmes en Moose avec des objets, et je n'ai rien contre, et j'ai défendu dans mes articles un modèle de programmation fonctionnelle, il n'y a pas de raison d'opposer les deux modèles, mais il faut reconnaître que si on les mélange trop, ça peut devenir un peu difficile à comprendre. Passer des fonctions de rappel à des objets ou des méthodes de classes rend la situation un peu compliquée.

  7. #7
    Membre habitué

    Homme Profil pro
    Statisticien
    Inscrit en
    Novembre 2010
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Statisticien

    Informations forums :
    Inscription : Novembre 2010
    Messages : 122
    Points : 134
    Points
    134
    Par défaut
    Donc ca fonctionne, mais je ne comprends pas vraiment pourquoi.

    Je comprends bien qu'il faut passer $_[0] ou shift, comme on récupère le 1er paramètre dans un "sub", mais pourquoi $_ fonctionne dans grep ?

    Et je ne comprends pas pourquoi tu crée generic_o. reduce (ligne 22 du 1er post) fonctionne comme il faut avec des objets. Mon idée en effet est de réécrire en partie List::Util orienté objet, et dans ce cas ci, min et max (les autres suivront). J'ai bien cherché sur le Net, j'imagine que ça existe, mais j'ai pas du trouver les bon mots-clefs sans doute, et puis c'est chouette à faire :-)

    Un grand merci en tout cas.

  8. #8
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par alainbb Voir le message
    Je comprends bien qu'il faut passer $_[0] ou shift, comme on récupère le 1er paramètre dans un "sub", mais pourquoi $_ fonctionne dans grep ?
    Avec grep, si tu boucles directement sur les valeurs du tableau, c'est tout simple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    $ perl -E 'my @a = qw /toto tutu titi tato/; say join " ", grep {/u/}@a;'
    tutu
    Mais si tu boucles sur les indices:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    $ perl -E 'my @a = qw /toto tutu titi tato/; say join " ", grep {/u/} 0..$#a;' # FAUX
    ne marche évidemment pas. Il fait quelque chose du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    $ perl -E 'my @a = qw /toto tutu titi tato/; say join " ", grep {$a[$_] =~ /o/} 0..$#a;'
    0 3
    qui m'affiche bien les indices des éléments reconnaissant l'expression régulière.

    C'est à peu près la même chose.

    Citation Envoyé par alainbb Voir le message
    Et je ne comprends pas pourquoi tu crée generic_o. reduce (ligne 22 du 1er post) fonctionne comme il faut avec des objets. Mon idée en effet est de réécrire en partie List::Util orienté objet, et dans ce cas ci, min et max (les autres suivront). J'ai bien cherché sur le Net, j'imagine que ça existe, mais j'ai pas du trouver les bon mots-clefs sans doute, et puis c'est chouette à faire :-)
    En fait, tout dépend du découpage exact de tes modules et de ton code utilisateur. Si tu veux faire un module qui crée les fonctions min et max, dans ce cas il est normal, effectivement, de coder en dur ces fonctions dans le module, en utilisant reduce pour faire le gros du boulot. Moi je pensais, d'après ce que j'avais compris à tes premières explications, à rendre le module plus général pour un utilisateur lui permettant de prendre une fonction de rappel, mais ce n'est pas le cas si tu veux faire un List::Util pour des objets.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 10/12/2008, 02h10
  2. Réponses: 2
    Dernier message: 14/06/2008, 18h03
  3. Réponses: 8
    Dernier message: 07/04/2008, 12h02
  4. Réponses: 1
    Dernier message: 05/06/2007, 17h14
  5. Réponses: 5
    Dernier message: 28/04/2006, 14h40

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