Beaucoup de code. mais si vous avez compris la première partie. cela devrait aller.
Expression Booléenne
Nous définissons l’interface suivante.
1234567891011121314151617
/** * Une expression Booléenne */interfaceBoolExpression{publicfunctionaccept(VisitorBoolExpression$v);}/** * Une classe abstraite. */abstractclassUnaryimplementsBoolExpression{publicfunctionaccept(VisitorBoolExpression$v){return$v->visit($this);}}
Pour faire l’algèbre booléen j’ai besoin de False et de True
// Or et And sont des mots réservés en Php.classBinaryOrextendsBinary{}classBinaryAndextendsBinary{}classBinaryNandextendsBinary{}classBinaryNorextendsBinary{}
$memory=newMemory();$memory->write('i',10);$ve=newVisitorBoolPrint($memory);// une expression$expression=newTrue();// appelle le visiteurvar_dump($expression->accept($ve))// affiche true;$expression=newNot(newFalse());var_dump($expression->accept($ve))// affiche !false;$expression=newBinaryAnd(newNot(newFalse()),newBinaryOr(newTrue(),newFalse()));var_dump($expression->accept($ve))//Affiche (!false&&(true||false));
Les comparaisons
Nous pouvons rajouter le ==, !=, > , < !
ajoutons de nouveau objet. les object prennent en entrée des expressions mais sortent des boléens.
1234567891011121314151617181920212223242526
classBinaryComparaisonextendsUnary{private$left;private$right;publicfunction__construct(Expression$left,Expression$right){$this->left=$left;$this->right=$right;}publicfunctiongetLeft(){return$this->left;}publicfunctiongetRight(){return$this->right;}}classEqualextendsBinaryComparaison{}classNotEqualextendsBinaryComparaison{}//Greater Than EqualclassGteextendsBinaryComparaison{}// Lesser Than EqualclassLteextendsBinaryComparaison{}// Lesser ThanclassLtextendsBinaryComparaison{}// Greater ThanclassGtextendsBinaryComparaison{}
Pour mon visiteur je vais utiliser mon visiteur d’expression du post précédent.
Le visiteur booléen utilise un autre visiteur pour évaluer une expression.
Un exemple d’utilisation
123456789
$memory=newMemory();$memory->write('i',10);// une visiteur d'expression$ve=newVisitorEvaluation($memory);// un visiteur pour les expressions booléennes$vb=newVisitorBoolEvaluation($memory,$ve);// une expression$expression=newNot(newEqual(newConstant(10),newAddition(newConstant(5),newVariable('i'))));var_dump($expression->accept($vb));// affiche bool(true)
si je reprend mon autre visiteur VisitorToPhp avec le visitorBoolPrint
1234567
$memory=newMemory();$memory->write('i',10);$ve=newVisitorToPhp($memory);$vb=newVisitorBoolPrint($memory,$ve);// une expression$expression=newNot(newEqual(newConstant(10),newAddition(newConstant(5),newVariable('i'))));var_dump($expression->accept($vb));//affiche "!(10==(5+$i))"
Une conclusion.
Dans le premier post : On a vu le visiteur pour évaluer/afficher des expressions.
dans le second post : le visiteur pour les expressions booléennes et les comparaisons. Celui-ci utilise le premier visiteur pour faire les calculs.
dans un prochain post, je vais montrer un troisième visiteur visitorInstruction pour évaluer des instructions d’un langage très simple. Mais cela est un peu long à écrire. Il y a un peu de théorie et des figures à faire.
/** * Interface Context */interfaceContext{// write a value in memorypublicfunctionwrite($name,$value);// get a value from the memorypublicfunctionread($name);//return all the valuepublicfunctiongetAll()}/** * A Memory */classMemoryimplementsContext{private$memory=array();// write a value in memorypublicfunctionwrite($name,$value){$this->memory[$name]=$value;return$this;}// get a value from the memorypublicfunctionread($name){return$this->memory[$name];}publicfunctiongetAll(){return$this->memory;}}
Il ne nous reste plus qu’à implémenter la variable.
On peux rajouter plein d’autre expression. L’avantage est qu’il suffit de rajouter une méthode ->interpret(..) pour chaque objet.
mais si on change le cahier des charges…
Changeons le cahier des charges. Je souhaite transformer mon Expression en chaine de caractères. Je peux m’en sortir en surchargeant la méthode __tostring
Par exemple :
12
$expression=newAddition(newAddition(newConstant(3),newConstant(4)),newConstante(4));$expression->__toString()// me donne ((3 + 4) + 4);
12345678910
// pour la constantepublicfunction__toString(){return$this->value;}// pour l'additionpublicfunction__toString(){// this->left->__toString()return'('.$this->left.' + '.$this->right.')';}
Rechangeons le cahier des charges : je veux la traduction en Php
12
$expression=newAddition(newAddition(newVariable('i'),newConstant(4)),newConstante(4));$expression->__toPhp()// me donne (($i + 4) + 4);
je suis un peu bloqué, je dois rajouter à chaque fois une méthode dans chaque Object. Je perd un peu de la simplicité du pattern..
Visiteur Pattern à la rescousse !
Je vais définir une méthode accept(Visitor $visitor)
en pratique. On appelle la méthode accept. Celle-ci appelle la methode visit($this). la méthode visit détermine la fonction à appeller.
Si c’est une constante alors visistConstant() celle-ci résout la valeur. pour une addition c’est un plus compliqué on ré-appelle récursivement accept sur chaque partie de l’addition.
Voici comment s’en servir
123456789
// j'ai besoin d'une mémoire$memory=newMemory();$memory->write('i',10);// j'ai besoin d'un visiteur$ve=newVisitorEvaluation($memory);// une expression$expression=newAddition(newConstant(10),newVariable('i'));// appelle le visiteurecho$expression->accept($ve);// 20
On se rend compte qu’il n’y a plus de logique dans mes objet. Tout est sous-traité dans le visiteur.
L’avantage de cette méthode est qu’il est très simple de changer le visiteur sans changer la logique.
$memory=newMemory();$memory->write('i',10);$ve=newVisitorEvaluation($memory);// une expression$expression=newAddition(newConstant(10),newVariable('i'));// appelle le visiteur evaluation simpleecho$expression->accept($ve);// 20// evaluation conversion php $php=newVisitorToPhp($memory);echo$expression->accept($php);// (10 + $i)$js=newVisitorToJs($memory);echo$expression->accept($js);// (10 + i)// j'ai rajouté une méthode translate qui est un raccourciecho$php->translate($expression);// $i = 10;echo(10+$i)echo$js->translate($expression);// var i = 10;console.log(10+i)
Les limitations du visiteur pattern
toute la logique est sur le visiteur. s’il y a un beaucoup de type d’expression (dans notre cas Addition, Constant, Variable, Abso, Multiplication ..) c’est autant de ligne à rajouter dans celui-ci.
rajouter un type, oblige à le ré-implementer partout.
Les avantages du visiteur pattern.
On peut parfaitement imaginer un type document, et lui ajouter un visiteur toJson, toPdf, toEbook, toHtml. sans jamais changer le modèle.
Nous continuerons avec le visiteur pattern dans un prochain post. Nous ajouterons un visiteur pour les expressions booléenes. puis nous ajouterons un visiteur pour des instructions. nous allons créer un mini-langage..
Ce projet vient des notes que j’avais prise quand j’étais au CNAM sur le cours de Design-Pattern en Java. J’avais adoré!
Sous Vi quand on appuie sur : on a la ligne de commande
Tout le monde connaît
12
:wq // quitter et enregistrer
:q! // quitter sans enregistrer et sans confirmation !
Mais en pratique il existe plein de commandes.
Par exemple :
1
:1,10d
efface la ligne 1 à 10 (d = delete)
1
:1,10m 10
bouge les lignes de 1 à 10 de 10 ligne (ici m = move)
le “pattern” est toujours le même
1
: (début, fin)action
mouvements
traduction
1,10
entre la ligne 1 et la ligne 1 à 10
.,10
. signifie la ligne actuelle
10,$
$ signifie la dernière ligne
/mot1/,/mot2/
entre le mot1 et le mot2
., +5
entre la ligne actuelle (.) et les 5 lignes suivantes
%
tout le fichier
quelques actions
racourcci
traduction
d
comme delete
j
comme join
sort
trier (sort) les lignes
w
pour enregistrer
y
comme yank
le plus connu substitute
vous avez souvent vu cette syntaxe dans les commits s/mot1/mot2
ici s signifie substitute.
par exemple
1
:%s/mot1/mot2/g
va remplacer le mot 1 par le mot 2
le g active le flag global et remplace si le mot apparait deux fois.
par exemple
1234567
$mot1 = $mot1 + 1;
// s/mot1/mot2
$mot2 = $mot1 + 1 ; // on ne change que le premier mot
// s/mot1/mot2/g
$mot2 = $mot2 + 1 // tout les mots
Encore un peu plus loin
la commande suivante permet de grouper les mots
1
:g/mot/ #donne toute les lignes contenant mot
g ici signifie group
On peut chaîner les differentes actions
par exemple
12345
:g/pattern/s/mot/mot2/g # toutes les lignes qui contiennent le pattern, remplace mot1 par mot2.
:g/pattern/d # efface toute les lignes qui contiennent le pattern suivant
:g/pattern/p # 'print toutes lignes qui contienne le pattern suivant
la derniere ligne est la plus connue. pattern est le plus souvent une regex donc la traduction g/regex/p ->donne la commande grep sous linux.
en faite, toutes les commandes que j’ai données proviennent de sed. mais ce n’est pas un hasard. vi est l’abbreviation de Visual Interaction of Sed. un sed interactif.
J’espère que cela vous fera apprecier sed comme vi. on peut rester très longtemps sur toutes les commandes.
j’avais expliqué dans un précédent articles les mouvements en mode normal sont
1234567891011121314
Action + Nombre de fois + Mouvement
Exemple:
d5w # *d*elete *5* word
yG # copier jusqu'à la fin du fichier (G)
=4j # indenter (=) 4 lignes vers le bas
di( # efface entre les parenthèses *d*elete *i*nside (
Dans le mode commande
: début, fin action
:%d # efface tout le fichier
:1,10y # copie dans le presse-papier la ligne 1 à 10
:%s/include_one/require_once/gc
Il me reste à vous parler des buffers et des macros. et on aura presque fait le tour de la magie de vi.
une commande de la vrai vie
1
:%s/\s\+$//
sur tout le fichier (%) remplace(s) un ou plus(+) espaces (\s) à la fin de la ligne ($) par du vide. cette commande supprime les espaces vides à la fin des lignes..
find -name '*.php'| xargs -p 4 grep "ma chaine de caractère"| wc -l
Cela ne se voit pas mais je viens de faire un map/reduce
je prend tout les fichiers php
je lance un grep avec le résultat que je passe à travers le pipe.
puis je compte le nombre de résultats.
1
[filter] -> [map] -> [reduce]
ici l’astuce tient dans une astuces xargs -p 4 qui ordonne à xargs d’utiliser 4 processeurs en parrallèle ! Donc qui me permet d’aller quatre fois plus vite. On comprend l’interet du map/reduce. Je peux dispatcher le travail sur plusieurs instances(ici processeurs). On retrouve ce fonctionnement dans beaucoup de logiciels actuels. L’avantage ici est que j’utilise linux et que je n’ai installé aucun programme et en multiprocesseur je fais pleinenement confiance au kernel de mon linux. Il existe aussi un programme parallel mais je ne m’en suis jamais servi.
En php ?
Il existe bien entendu les fonctions array_map, array_filter, array_reduce mais il ne sont pas spécialement plus rapide qu’une boucle foreach. Il existe un map pour les Collection de doctrine.
je vais montrer que l’on peux écrires toute les opérations possibles avec ‘array_reduce’.
functionmysum($array){returnarray_reduce($array,function($acc,$item){return$acc+=$item;});}functionmymult($array){returnarray_reduce($array,function($acc,$item){return$acc*=$item;},1);}functionmymax($array){returnarray_reduce($array,function($acc,$item){returnmax($acc,$item);});}// le min est un peu moins simplefunctionmap($function,$array){returnarray_reduce($array,function($acc,$input)use($function){$acc[]=$function($input);return$acc;});}functionfilter($function,$array){returnarray_reduce($array,function($acc,$input)use($function){if($function($input)){$acc[]=$input;}return$acc;});}functionmycount($array){returnarray_reduce($array,function($acc,$input){return++$acc;},0);}$a=[1,2,3,4,5];var_dump(mysum($a));// 15var_dump(mymult($a));// 120var_dump(mymax($a));// 5var_dump(mycount($a));// 5var_dump(map(function($input){return2*$input;},$a));// [2,4,6,8,10]var_dump(filter(function($input){return($input>4);},$a));// [6]
Map et Reduce sont des fonctions un peu particulières, elle prenent comme argument des fonctions. On parle de fonctions d’ordre supérieure, parfois de functor (mais çà c’est une autre histoire ..)
Comprendre les raccourcis claviers Vim : la méthode des deux tableaux
Aujourd’hui nous allons essayer de comprendre les raccourcis de vi/vim. Je vous demande de prendre deux feuilles. Je vais essayer de vous monter comment se combinent les touches.
sur la feuille 1 dessinez deux colonnes: touche description.
Titre de la feuille 1 : Mouvement
Tire de la feuille 2 : Action deux colonnes : touche et description.
Lancez vim et ouvrez un fichier texte existant.
Les mouvements :
w signifie word
appuyez sur w vous passez au mot suivant.
Ajouter à votre tableau mouvement
w word passe au mots suivant
b signifie back. Vous allez au mot précédent
Vous ajoutez a la feuille b back
e signifie end va à la fin du mot
les directions
J’ai un peu peur de vous perdre ici.
Dans le temps les clavier n’avait pas de touche de direction. Les touches sont h, j, k, l pour respectivement gauche, bas, haut, droite
Moyen mnémotechnique j va vers le bas, k va vers le haut.
Vous pouvez utiliser les flèches. Mais un des avantage d’utiliser les j et k est que vos doigts ne quitte jamais le milieu du clavier.
Vous avez maintenant 7 lignes à votre tableau
Un quiz !
Que se passe t’il si je tape 3w ou 5j ?
Réponse je me déplace de 3 mots ou 5 ligne vers le bas
modifions notre tableau en rajoutant le (n).
Se déplacer dans le fichier.
gg vous ramène au début du fichier (g pour go)
G vous emmène à la fin du fichier
50G vous emmène à la ligne 50 (variante :50 marche aussi)
Vous rajoutez les 3 lignes dans votre tableau.
Début de ligne, fin de ligne
si vous connaissez vos Regex ^a vous donnes tout les occurrences qui commencent par a et a$ qui finissent par a^ début du texte sur la ligne actuelle
$ fin de la ligne
0 colonne 0 (tout début de la ligne)
A la recherche du mot perdu
pour chercher de vi on utilise la touche /, vous mettez le mot que vous souhaitez, appuyez sur n pour suivant ou N pour précédent.
nous avons le tableau final
Touche
Description
w
word mot suivant
(n)w
n word (ex: 3w 3 mots)
b
back mots précédent
e
end fin du mot
h,j, k,l
gauche, bas, haut, droite
(n)j
3 fois bas
gg
début du fichier
G
fin du fichier
20G
ligne 20
^
Début de ligne
$
fin de la ligne
/mot
cherche mot. n pour suivant N pour précédent
Nous avons la feuille 1.
Un demi conclusion :
Il existe plus de mouvement.
mais le but ici n’est pas d’être exhaustif. J’ai besoin des mouvements pour introduire la feuille suivante.
Les Actions.
d signifie delete (en fait c’est un couper)
Petit quizz : que se passe t’il si j’appuie sur les touches suivantes ? d3w
une traduction delete 3words. J’efface trois mots, Vous comprenez pourquoi j’ai absolument besoin de la table d’avant. Car en fait et c’est une des vérités qui vous aidera dans Vi/Vim. Un combinaison dans Vi c’est action + mouvement.
Je donne les autres actions
ypour yank pour copier yw copie un mot. y3j copie 3 lignes. pour coller on utilise la touche p comme paste
c pour change. Vous souhaitez changer un mot. avant vous appuyer sur i pour vous effacer le mot, puis vous rajouter le votre. Si vous taper cw comme change word. vi supprime le mot et met directement en mode insertion.
> et < déplace à droite et à gauche le texte <4j déplace 4 ligne à gauche.
= re-indente le texte, c’est un pictogramme le deux lignes du égale sont alignées. Pour ré-indenter tout le texte gg=G soit gg va au début du fichier =G ré-indente jusqu’à la fin du fichier.
Table 2
Touche
Description
d
delete
c
change efface et passe en mode insertion
y
yank (copier) p pour paste coller
>
décale le texte à droite
<
décale à droite
=
reindente le code
v
visual selectionne le texte y copie d coupe etc ..
Conclusion
Pour faire une combinaison de touche il suffit de prendre une lettre de la table 2 + un mouvement de la table 1.
par exemple je veux effacer 5 lignes:
delete 5 lines -> d5j
je veux changer un mot
change word -> cw
Recopier les deux tableaux. L’astuce est vraiment d’avoir les feuilles sous les yeux.
Les touches sont les mêmes pour man et surtout less
Un peu de philosophie.
Je n’ai pas utilisé une seul fois la touche controle et alt.
En 1 sens, je n’utilise pas la souris. Je veux indenter le texte je dis indente le texte=G et pas sélectionner le texte, puis bouton droit ou un raccourci clavier. En un sens Vi est plus direct.
J’espère que cela démystifie un peu l’utilisation du clavier sur vi
Teaser
il existe d’autre mouvement ! Je ferai une version deluxe, l’idée ici est de comprendre la combinaison de mouvements. une touche apprise c’est de dizaine de nouvelles possibilités
par exemple di(i signifie inside. donc delete inside parenthèse. Efface le texte entre les parenthèses.
Faq
j’appris que pour effacer une ligne c’est dd ? je ne le vois pas dans le tableau.
en fait
dd efface une ligne.
yy copie la ligne.
cc change la ligne.
Il existe un mouvement qui s’appelle _ qui représente la ligne actuelle (c’est d’ailleurs le pictogramme d’une ligne)
donc en fait si on tape d_ on efface la ligne actuelle (essayez !). Mais la plupart du temps c’est un peu compliqué à taper pour une opération plutôt courante (supprimer une ligne) . Donc il a été décidé que dd, yy , cc
sont les raccourcis de d_, y_ et c_.
Il y a en fait une multitude de raccourcis
par exemple:
c$ donne C(Change tout la ligne à partir du curseur).
d$ donne D(Efface toute la ligne à partir du Curseur).
$ apt-get install npm
E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?
$ sudo !!
!! est la dernière commande exécutée
Une variante
12
mkdir log/
cd !$
!$ est la dernière commande sans le premier argument ici log/
Vi/Vim
Bon il y a beaucoup de commandes relativement sympa et complexes mais je donne celle que tout le monde demande
Enregistrer un fichier en lecture seule
1
:w !sudo tee %
Dite oui pour recharger la page. Et dite oui si vous voulez quitter la page sans enregistrer.
D’ailleurs si vous utilisez tout le temps :wq vous pouvez essayer
:w enregistre sans quitter
:q quitte mais avec warning si vous n’avez pas sauvegarder
:q! quitte sans warning et sans sauver
:x raccourci de :wq. Il existe aussi le raccourci clavier ZZ comme pour aller dormir. quitte et enregistre.
:e fichier.txt ouvre le fichier (l’auto complétion avec tab fonctionne..)
:tabe fichier.txt ouvre dans un nouvel onglet (vim seulement). on peux se ballader d’onglet en onglet avec les touches gt et gTg est le diminutif de go et t pour tab. ou avec ctrl+<pageUp> et ctrl+<pageDown> ou encore tout simplement cliquer avec la souris (astuce suivante)
Active la souris
1
set mouse=a
une astuce est d’ajouter un .vimrc et d’ajouter cette ligne dans ce fichier de config.
le vimrc
Vous en avez besoin !! Il existe deux versions de vi: vi et vim vim démarre par défaut en mode vi compatible (ce qui n’est plus tout à fait vrai).Certaines fonctionnalités sont coupées. Dont les flèches de directions (et ca vous voulezles flèches haut, bas, gauche, droite) et surtout le annuler (u come undo) illimité (il est parfois limité à 1). Quel rapport avec le vimrc ? L’existence du fichier fera que votre vi démarre en mode non compatible. donc vous aurez les touches et le undo.
l’auto-complétion sous vim
Il existe une dizaine d’auto complétion sous vim. Certainement pas aussi puissantes que IDE mais cela rend service.
En mode insertion ctrl+p auto complétion en cherchant dans les fichiers ouverts. Cela résout 50 % des auto complétions.
l’auto-complétion est activé via la touche ctrl+x +crtl+lettre avec :
ctrl+xctrl+ff pour file auto complétion suivant le fichier (marche nickel pour les hosts apache !)
ctrl+xctrl+ll pour line auto complétion sur la ligne entière
ctrl+xctrl+p c’est pareil que ctrl+p
ctrl+xctrl+oo pour omni-completion auto complete php/python/bash suivant le fichier que vous éditez. (cela comprend les mots clés et peux vous donner la syntaxe)
Je montrerai dans un prochain post comment faire pareil qu’un IDE avec les fonctions. Ou se balader dans les classes et/ou auto compléter le code. Ce n’est pas encore le niveau des IDEs mais c’est souvent une fonctionnalité méconnue (pourtant c’est dans vi depuis des années)