Les programmeurs semblent tous s'accorder pour dire que la lisibilité du code est bien plus importante que les guillemets simples à syntaxe courte qui fonctionnent, mais qu'un développeur expérimenté doit l'interpréter avec la plus grande précision - mais cela semble être exactement la façon dont les expressions régulières ont été conçues. Y avait-il une raison à cela?
Nous sommes tous d'accord pour dire que selfDocumentingMethodName()
c'est beaucoup mieux que e()
. Pourquoi cela ne s'appliquerait-il pas également aux expressions régulières?
Il me semble que plutôt que de concevoir une syntaxe de logique à une ligne sans organisation structurelle:
var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})(0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
Et ce n'est même pas une analyse syntaxique stricte!
Au lieu de cela, nous pourrions rendre une structure de pipeline organisée et lisible, pour un exemple de base:
string.regex
.isRange('A-Z' || 'a-z')
.followedBy('/r');
Quels avantages la syntaxe extrêmement concise d'une expression régulière offre-t-elle en dehors de la syntaxe logique et de l'opération la plus courte possible? En fin de compte, y a-t-il une raison technique spécifique à la mauvaise lisibilité de la conception de syntaxe d'expression régulière?
la source
Réponses:
Les expressions régulières ont été conçues pour être utilisées comme des commandes pour un éditeur de code, et non comme un langage pour coder. Il s’agit plus précisément d’
ed
un des premiers programmes à utiliser des expressions régulières. et à partir de là, les expressions régulières ont commencé leur conquête de la domination mondiale. Par exemple, laed
commande ag/<regular expression>/p
rapidement inspiré un programme séparé appelégrep
, qui est toujours utilisé aujourd'hui. En raison de leur puissance, ils ont ensuite été normalisés et utilisés dans divers outils telssed
quevim
Mais assez pour le trivia. Alors, pourquoi cette origine favoriserait-elle une grammaire laconique? Parce que vous ne tapez pas une commande d'éditeur pour la lire, même une fois de plus. Il suffit que vous vous souveniez de la façon dont vous le réunissiez et que vous puissiez faire ce que vous voulez avec. Cependant, chaque caractère que vous devez taper ralentit votre progression en modifiant votre fichier. La syntaxe des expressions rationnelles a été conçue pour écrire des recherches relativement complexes à la volée. C’est précisément ce qui donne le courage à ceux qui les utilisent en tant que code pour analyser certaines entrées d’un programme.
la source
grep
c'est un "grab" mal prononcé, cela vient en fait deg
/re
(pour une expression régulière) /p
?<aaa bbb="ccc" ddd='eee'>
, il n'y a pas de balises imbriquées à l'intérieur. Vous ne pouvez pas imbriquer des balises, ce que vous imbriquer sont des éléments (balise ouverte, contenu incluant des éléments enfants, balise de fermeture), que la question ne demandait pas à propos de l'analyse. Les balises HTML sont un langage courant - l’équilibrage / imbrication se produit à un niveau supérieur aux balises.L'expression régulière que vous citez est un désordre épouvantable et je ne pense pas que quiconque s'accorde pour dire que c'est lisible. Dans le même temps, une grande partie de cette laideur est inhérente au problème à résoudre: il existe plusieurs niveaux d'imbrication et la grammaire des URL est relativement compliquée (certainement trop compliquée pour pouvoir communiquer de manière succincte dans n'importe quelle langue). Cependant, il est certainement vrai qu'il existe de meilleures façons de décrire ce que décrit cette regex. Alors pourquoi ne sont-ils pas utilisés?
Une grande raison est l'inertie et l'ubiquité. Cela n’explique pas à quel point elles sont devenues si populaires, mais maintenant qu’elles le sont, toute personne connaissant les expressions régulières peut utiliser ces compétences (avec très peu de différences entre les dialectes) dans une centaine de langues différentes et avec un millier d’outils logiciels supplémentaires ( par exemple, des éditeurs de texte et des outils de ligne de commande). À propos, ces derniers n’auraient pas et ne pourraient utiliser aucune solution qui reviendrait à écrire des programmes , car ils sont très utilisés par des non-programmeurs.
Malgré cela, les expressions régulières sont souvent surexploitées, c'est-à-dire appliquées même lorsqu'un autre outil serait bien meilleur. Je ne pense pas que la syntaxe des expressions rationnelles soit terrible . Mais il est clairement beaucoup mieux pour les modèles courts et simples: l'exemple archétypal d'identifiants dans les langages de type C
[a-zA-Z_][a-zA-Z0-9_]*
peut être lu avec un minimum absolu de connaissances sur les expressions rationnelles et, une fois cette barre atteinte, elle est à la fois évidente et succincte. Exiger moins de caractères n'est pas mauvais en soi, bien au contraire. Être concis est une vertu à condition de rester compréhensible.Il existe au moins deux raisons pour lesquelles cette syntaxe excelle dans les modèles simples suivants: elle ne nécessite pas l'échappement de la plupart des caractères; elle lit donc de manière relativement naturelle, et elle utilise toute la ponctuation disponible pour exprimer une variété de combinateurs d'analyse simple. Le plus important est peut-être qu’il n’exige rien du tout pour le séquençage. Vous écrivez la première chose, puis la chose qui vient après. Comparez cela avec votre
followedBy
, en particulier lorsque le modèle suivant n'est pas un expression littérale, mais une expression plus complexe.Alors, pourquoi sont-ils défaillants dans les cas les plus compliqués? Je peux voir trois problèmes principaux:
Il n'y a pas de capacités d'abstraction. Les grammaires formelles, qui proviennent du même domaine de l'informatique théorique que les regex, ont un ensemble de productions, de sorte qu'elles puissent donner des noms aux parties intermédiaires du motif:
Comme nous avons pu le voir ci-dessus, les espaces n’ayant pas de signification particulière sont utiles pour permettre un formatage plus agréable pour les yeux. Même chose avec des commentaires. Les expressions régulières ne peuvent pas faire cela, car un espace n'est que cela, un littéral
' '
. Note cependant: certaines implémentations autorisent un mode "verbeux" dans lequel les espaces sont ignorés et les commentaires possibles.Il n'y a pas de méta-langage pour décrire les modèles et les combinateurs communs. Par exemple, on peut écrire une
digit
règle une fois et continuer à l’utiliser dans une grammaire sans contexte, mais on ne peut pas définir une "fonction" pour ainsi dire qui reçoit une productionp
et crée une nouvelle production qui en fait quelque chose de plus, par exemple créer une production pour une liste d'occurrences dep
.L’approche que vous proposez résout certainement ces problèmes. Cela ne les résout tout simplement pas très bien, car il offre une concision beaucoup plus concise que nécessaire. Les deux premiers problèmes peuvent être résolus tout en restant dans un langage relativement simple et concis spécifique à un domaine. Le troisième, eh bien ... une solution programmatique nécessite bien sûr un langage de programmation général, mais, d’après mon expérience, le troisième est de loin le moindre de ces problèmes. Peu de modèles ont suffisamment d'occurrences de la même tâche complexe que le programmeur souhaite pour pouvoir définir de nouveaux combinateurs. Et lorsque cela est nécessaire, le langage est souvent assez compliqué pour qu'il ne puisse et ne doive pas être analysé avec les expressions régulières de toute façon.
Des solutions à ces cas existent. Il existe environ dix mille bibliothèques de combinateurs d’analyseurs qui font à peu près ce que vous proposez, mais avec un ensemble différent d’opérations, souvent une syntaxe différente, et presque toujours avec plus de puissance d’analyse que les expressions régulières (c’est-à-dire qu’elles traitent de sous-ensemble de ceux-ci). Il existe ensuite des générateurs d’analyseur, qui vont de l’approche "utiliser un meilleur DSL" décrite ci-dessus. Et il y a toujours la possibilité d'écrire une partie de l'analyse syntaxique à la main, dans le code approprié. Vous pouvez même combiner, en utilisant des expressions régulières pour des sous-tâches simples et en effectuant des opérations compliquées dans le code en appelant les regex.
Je ne connais pas suffisamment les débuts de l'informatique pour expliquer comment les expressions régulières sont devenues si populaires. Mais ils sont là pour rester. Vous devez simplement les utiliser à bon escient, et non les utiliser quand c'est plus sage.
la source
I don't know enough about the early years of computing to explain how regular expressions came to be so popular.
Cependant, nous pouvons nous risquer à deviner: un moteur d'expression régulière de base est très facile à implémenter, beaucoup plus facile qu'un analyseur syntaxique efficace sans contexte.grep
était (Version 3 vs Version 4). Il semble que la première utilisation majeure de regexyacc
idée , c’est en 1975 que fut créée l’idée même des analyseurs syntaxiques LALR (qui faisaient partie de la première classe d’analyseurs exploitables de leurs kind) a été créé en 1973. Alors que la première implémentation du moteur d’expression rationnelle compilée par JIT (!) a été publiée en 1968. Mais vous avez raison, il est difficile de dire ce qui l’a balancé. de". Mais j’imaginais que, une fois qu’ils étaient intégrés aux éditeurs de texte, ils souhaitaient également les utiliser dans leur propre logiciel.with very few differences between dialects
Je ne dirais pas que c'est "très peu". Toute classe de caractères prédéfinie comporte plusieurs définitions entre différents dialectes. Et il existe aussi des bizarreries d’analyse spécifiques à chaque dialecte.Perspective historique
L'article de Wikipedia est assez détaillé sur les origines des expressions régulières (Kleene, 1956). La syntaxe originale était relativement simple avec seulement
*
,+
,?
,|
et le regroupement(...)
. C'était concis ( et lisibles, les deux ne sont pas nécessairement opposés), car les langages formels ont tendance à être exprimés avec des notations mathématiques concises.Plus tard, la syntaxe et les capacités ont évolué avec les éditeurs et grandi avec Perl , qui essayait d’être concis par nature ( "les constructions courantes devraient être courtes" ). Cela a beaucoup complexifié la syntaxe, mais notez que les gens sont maintenant habitués aux expressions régulières et sont doués pour les écrire (sinon les lire). Le fait qu’ils soient parfois uniquement en écriture suggère que, quand ils sont trop longs, ils ne sont généralement pas le bon outil. Les expressions régulières ont tendance à être illisibles lorsqu'elles sont maltraitées.
Au-delà des expressions régulières basées sur des chaînes
En ce qui concerne les syntaxes alternatives, examinons celle qui existe déjà ( cl-ppcre , dans Common Lisp ). Votre longue expression régulière peut être analysée
ppcre:parse-string
comme suit:... et résultats sous la forme suivante:
Cette syntaxe est plus détaillée et, si vous regardez les commentaires ci-dessous, pas nécessairement plus lisible. Donc, ne supposez pas que, puisque vous avez une syntaxe moins compacte, les choses seront automatiquement plus claires .
Toutefois, si vous commencez à avoir des problèmes avec vos expressions régulières, leur conversion dans ce format peut vous aider à déchiffrer et à déboguer votre code. C'est un avantage par rapport aux formats basés sur des chaînes de caractères, où une erreur d'un seul caractère peut être difficile à repérer. Le principal avantage de cette syntaxe est de manipuler des expressions régulières en utilisant un format structuré au lieu d'un codage basé sur une chaîne. Cela vous permet de composer et de construire de telles expressions comme n'importe quelle autre structure de données de votre programme. Lorsque j'utilise la syntaxe ci-dessus, c'est généralement parce que je veux construire des expressions à partir de parties plus petites (voir aussi ma réponse à CodeGolf ). Pour votre exemple, nous pouvons écrire 1 :
Des expressions régulières basées sur des chaînes peuvent également être composées, à l'aide d'une concaténation de chaînes et / ou d'une interpolation incorporée dans des fonctions d'assistance. Cependant, il y a des limites aux manipulations de chaîne qui ont tendance à encombrer le code (pensez aux problèmes d'imbrication, un peu comme les backticks et les
$(...)
bash; les caractères d'échappement peuvent également vous donner des maux de tête).Notez également que le formulaire ci-dessus autorise les
(:regex "string")
formulaires afin que vous puissiez mélanger des notations concises avec des arbres. Tout cela conduit à mon humble avis à une bonne lisibilité et composition. il aborde les trois problèmes exprimés par delnan , indirectement (c'est-à-dire pas dans le langage des expressions régulières).De conclure
Dans la plupart des cas, la notation abrégée est en fait lisible. Il existe des difficultés lorsqu’il s’agit de notations étendues impliquant des retours en arrière, etc., mais leur utilisation est rarement justifiée. L'utilisation injustifiée d'expressions régulières peut conduire à des expressions illisibles.
Les expressions régulières ne doivent pas nécessairement être codées sous forme de chaînes. Si vous avez une bibliothèque ou un outil qui peut vous aider à construire et composer des expressions régulières, vous éviter beaucoup de bugs potentiels liés aux manipulations de cordes.
Alternativement, les grammaires formelles sont plus lisibles et sont meilleures pour nommer et résumer des sous-expressions. Les terminaux sont généralement exprimés sous forme d'expressions régulières simples.
1. Vous préférerez peut-être créer vos expressions à la lecture, car les expressions régulières ont tendance à être des constantes dans une application. Voir
create-scanner
etload-time-value
:la source
digits
,ident
et les composent. Je vois que cela se fait généralement avec des manipulations de chaînes (concaténation ou interpolation), ce qui pose d’autres problèmes, comme une échappement correct. Recherchez des occurrences\\\\`
dans les paquets emacs, par exemple. Btw, cela est rendu pire parce que le même caractère d'échappement est utilisé à la fois pour les caractères spéciaux comme\n
et\"
et pour la syntaxe regex\(
. Un exemple non-lisp de bonne syntaxe estprintf
, où%d
n'entre pas en conflit avec\d
.greedy-repetition
ne sont pas intuitifs et doivent encore être appris). Cependant, cela sacrifie la facilité d'utilisation pour les experts, car il est beaucoup plus difficile de voir et de saisir l'ensemble du schéma.do {optional (many1 (letter) >> char ':'); choice (map string ["///","//","/",""]); many1 (oneOf "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-."); optional (char ':' >> many1 digit); optional (char '/' >> many (noneOf "?#")); optional (char '?' >> many (noneOf "#")); optional (char '#' >> many (noneOf "\n")); eof}
. Avec quelques lignes comme la désignation de la longue chaîne commedomainChars = ...
etsection start p = optional (char start >> many p)
cela semble assez simple.Le plus gros problème de regex n’est pas la syntaxe trop concise, c’est que nous essayons d’exprimer une définition complexe en une seule expression, au lieu de la composer à partir de blocs de construction plus petits. Ceci est similaire à la programmation dans laquelle vous n'utilisez jamais de variables et de fonctions mais intégrez votre code dans une seule ligne.
Comparez regex avec BNF . Sa syntaxe n'est pas tellement plus nette que regex, mais elle est utilisée différemment. Vous commencez par définir des symboles nommés simples et vous les composez jusqu'à ce que vous obteniez un symbole décrivant le motif complet auquel vous souhaitez apparier.
Par exemple, regardez la syntaxe de l'URI dans rfc3986 :
Vous pouvez écrire presque la même chose en utilisant une variante de la syntaxe regex qui prend en charge l’incorporation de sous-expressions nommées.
Personnellement, je pense qu’une syntaxe rationnelle comme la syntaxe convient aux fonctionnalités couramment utilisées telles que les classes de caractères, la concaténation, le choix ou la répétition, mais pour les fonctionnalités plus complexes et plus rares telles que les noms verbeux à l’avenir sont préférables. Tout à fait similaire à la façon dont nous utilisons des opérateurs similaires
+
ou*
dans la programmation normale et basculons vers des fonctions nommées pour des opérations plus rares.la source
est-ce Il y a une raison pour laquelle la plupart des langues ont {et} comme délimiteurs de bloc plutôt que BEGIN et END.
Les gens aiment la concision, et une fois que vous connaissez la syntaxe, une terminologie courte est préférable. Imaginez votre exemple de regex si d (pour digit) était 'digit', la regex serait encore plus horrible à lire. Si vous le rendiez plus facilement analysable avec les caractères de contrôle, cela ressemblerait davantage à XML. Ni sont aussi bons une fois que vous connaissez la syntaxe.
Pour répondre correctement à votre question, vous devez toutefois vous rendre compte que la regex vient de l'époque où la légèreté était obligatoire. Il est facile de penser qu'un document XML de 1 Mo n'est pas un problème aujourd'hui, mais nous parlons des jours où 1 Mo était assez toute votre capacité de stockage. Il y avait aussi moins de langues utilisées à l'époque, et regex n'étant pas à des kilomètres de perl ou de C, la syntaxe serait familière aux programmeurs de l'époque qui seraient heureux d'apprendre cette syntaxe. Il n'y avait donc aucune raison de le rendre plus verbeux.
la source
selfDocumentingMethodName
est généralement reconnu comme étant meilleur quee
parce que l’ intuition du programmeur n’est pas conforme à la réalité en termes de lisibilité ou de code de bonne qualité . Les gens qui s'entendent ont tort, mais c'est comme ça.e()
c'est mieux queselfDocumentingMethodName()
?e()
un nom de méthode auto-documentant . Pouvez-vous expliquer dans quel contexte il est préférable d'utiliser des noms de méthode d'une seule lettre plutôt que des noms de méthode descriptifs?Regex est comme un lego. À première vue, vous voyez des pièces en plastique de formes différentes qui peuvent être assemblées. Vous pensez peut-être qu'il n'y a pas beaucoup de choses différentes que vous pouvez façonner, mais ensuite vous voyez les choses incroyables que font les autres et vous vous demandez simplement à quel point c'est un jouet incroyable.
Regex est comme un lego. Peu d’arguments peuvent être utilisés, mais leur enchaînement sous différentes formes formera des millions de motifs de regex pouvant être utilisés pour de nombreuses tâches compliquées.
Les gens utilisaient rarement les paramètres de regex seuls. De nombreuses langues vous proposent des fonctions permettant de vérifier la longueur d'une chaîne ou de séparer les parties numériques de celle-ci. Vous pouvez utiliser des fonctions de chaîne pour découper des textes et les reformer. La puissance de regex se remarque lorsque vous utilisez des formulaires complexes pour effectuer des tâches complexes très spécifiques.
Vous pouvez trouver des dizaines de milliers de questions de regex sur SO et elles sont rarement marquées comme des doublons. Cela seul montre les cas d'utilisation uniques possibles qui sont très différents les uns des autres.
Et il n’est pas facile de proposer des méthodes prédéfinies pour gérer des tâches aussi différentes. Vous avez des fonctions de chaîne pour ce type de tâches, mais si ces fonctions ne suffisent pas pour votre tâche spécifique, il est temps d'utiliser regex
la source
Je reconnais que c'est un problème de pratique plutôt que de puissance. Le problème se pose généralement lorsque des expressions régulières sont directement implémentées, au lieu de prendre une nature composite. De même, un bon programmeur décomposera les fonctions de son programme en méthodes concises.
Par exemple, une chaîne de regex pour une URL peut être réduite d'environ:
à:
Les expressions régulières sont des choses astucieuses, mais elles sont sujettes aux abus de ceux qui se sentent absorbés par leur complexité apparente . Les expressions résultantes sont une rhétorique, sans valeur à long terme.
la source
Comme @cmaster le dit, les expressions rationnelles ont été conçues à l'origine pour être utilisées uniquement à la volée, et il est simplement étrange (et légèrement déprimant) que la syntaxe de bruit de ligne reste la plus répandue. Les seules explications auxquelles je puisse penser sont soit l'inertie, le masochisme ou le machisme (ce n'est pas souvent que l'inertie est la raison la plus attrayante de faire quelque chose ...)
Perl fait un effort plutôt faible pour les rendre plus lisibles en permettant des espaces et des commentaires, mais ne fait rien de façon imaginative à distance.
Il existe d'autres syntaxes. Un bon exemple est la syntaxe scsh pour regexps , qui, selon mon expérience, produit des regexps qui sont assez faciles à taper, mais qui sont toujours lisibles après coup.
[ scsh est magnifique pour d'autres raisons, dont l'une est son célèbre texte de remerciements ]
la source
Je crois que les expressions régulières ont été conçues pour être aussi générales et simples que possible, de sorte qu'elles puissent être utilisées (à peu près) de la même manière n'importe où.
Votre exemple
regex.isRange(..).followedBy(..)
est lié à la fois à la syntaxe d'un langage de programmation spécifique et éventuellement à un style orienté objet (chaînage de méthodes).Comment cette "regex" exacte ressemblerait-elle en C par exemple? Le code devrait être changé.
L’approche la plus «générale» consisterait à définir un langage simple, concis, qui puisse ensuite être facilement intégré dans n’importe quel autre langage sans changement. Et c'est (presque) ce que sont les regex.
la source
Les moteurs d’ expression régulière compatibles Perl sont largement utilisés, fournissant une syntaxe d’expression régulière concise que de nombreux éditeurs et langues comprennent. Comme @ JDługosz l'a souligné dans des commentaires, Perl 6 (pas seulement une nouvelle version de Perl 5, mais un langage totalement différent) a tenté de rendre les expressions régulières plus lisibles en les construisant à partir d'éléments définis individuellement. Par exemple, voici un exemple de grammaire permettant d’analyser les URL de Wikibooks :
La division de l'expression régulière de cette manière permet à chaque bit d'être défini individuellement (par exemple, contraint
domain
d'être alphanumérique) ou étendu par le biais d'un sous-classement (par exemple,FileURL is URL
que les contraintesprotocol
soient uniquement"file"
).Donc: non, il n'y a pas de raison technique à la minceur des expressions régulières, mais des moyens plus récents, plus propres et plus lisibles de les représenter sont déjà là! Nous espérons donc voir de nouvelles idées dans ce domaine.
la source