Conception de la gamme parfaite littérale

9

J'ai réfléchi à la façon dont j'allais concevoir la gamme "parfaite" littérale si je devais concevoir une langue. Pour vous qui ne connaissez pas, connaissez un littéral de plage dans une instruction qui représente une plage de valeurs, comme 1-4. Ils sont le plus souvent utilisés dans les boucles for / foreach

Il semble qu'il y ait quelques problèmes à prendre en compte

  • Prise en charge de plages inclusives et exclusives, le virement de +1 ou -1 vers les points de terminaison semble un peu fugace et sujet aux erreurs.

  • Prise en charge de la progression, vous pouvez donc créer une plage de nombres pairs ou impairs, par exemple

  • Lisibilité, il doit être facile de voir ce que le littéral de plage décrit

  • Sans ambiguïté, il devrait être parfaitement sans ambiguïté ce que la gamme littérale décrit

  • La valeur par défaut devrait probablement être comprise entre inclusive et exclusive, car c'est ce qui est utilisé dans la plupart des cas pour boucler sur des tableaux, etc.

Quoi qu'il en soit, un exemple de littéral de plage que j'ai vu est Ruby qui est sous la forme de 1..3 pour une plage exclusive (à la fin) et 1 ... 3 pour une plage inclusive (à la fin). Vous pouvez également faire 1..10. étape (5). Après mûre réflexion, j'ai trouvé cependant quelques choses que je n'aimais pas dans cette approche (de ma connaissance limitée du rubis)

  1. Vous pouvez seulement décrire inclusif et exclusif pour la fin. Tout en décrivant la plupart des scénarios, cela semble un peu incohérent.

  2. Variant de seulement un supplémentaire. semble être une recette pour rendre difficile de voir si une gamme est inclusive ou exclusive. Je ne sais pas pour vous mais les points ont tendance à devenir flous :)

  3. L'ajout d'une méthode comme la notation pour les plages semble mélanger la notion de littéral avec celle d'une classe qui semble un peu incohérente (même si les plages sont compilées dans une classe)

Quoi qu'il en soit, après avoir réfléchi à différentes alternatives. Je suis venu avec cette

  • [5..1] 5,4,3,2,1
  • [1..5 [ 1,2,3,4
  • ] 1..5] 2,3,4,5
  • [ 0..5..20] 0,5,10,15,20

et ainsi de suite. Je l'aime parce que [dénonce normalement un ensemble et cela s'intègre un peu à cela, même si cela contraste avec un ensemble serait commandé.

Une chose qui me déchire un peu, c'est de rendre les indicateurs exclusifs / inclusifs obligatoires ou non, c'est-à-dire que si vous écrivez juste 1..5, la valeur par défaut serait 1,2,3,4 car c'est le cas le plus courant avec les tableaux, etc. C'est plus facile et plus lisible, mais moins spécifique et si vous deviez écrire [1..5 [vous apprenez tôt comment ils fonctionnent.

Alors que pensez-vous, ai-je couvert la plupart des bases, oublié quelque chose? rendriez-vous le [] obligatoire? Souhaitez-vous concevoir des littéraux de plage différemment dans votre langage de programmation?

Candidats

  • style de support: [0..10 [ , avec étape: [0..5..20 [
  • notation d'intervalle: [0..10) avec étape: [0..5..20)
  • exclamation pour exlusif. 0 ..! 10, avec pas: 0..5 ..! 20
    • avec une étape différente. 0 ..! 20, 5
    • cependant, cela rendrait la valeur par défaut * 0..10 ' inclusive-inclusive
  • wordy: [0 à! 20 par 5]

Je dois dire que mon préféré est jusqu'ici 0 ..! 10 et 0..5 ..! 20 , je souhaite juste que le 0..10 par défaut inclusif-exclusif soit plus logique

Homde
la source
2
1,5,10,15,20Écart 4, 5, 5, 5?!
Peter Taylor
oops :) corrigé
Homde

Réponses:

14

Pourquoi inventer ce qui existe déjà en mathématiques?

La notation des intervalles

Cela devrait couvrir votre point d'ambiguïté tel qu'il existe déjà.

Votre seul problème serait de définir la progression. Peut-être que quelque chose d'autre en mathématiques peut aider à cela?

Dan McGrath
la source
3
Salut, j'ai en fait sur la notation d'intervalle. Cependant, ce qui m'a fait ne pas vouloir l'utiliser, c'est le fait qu'il n'était pas symétrique à la fois [et] et utilisait des paranthèses ainsi que l'absence d'étapes (comme vous le mentionnez). L'utilisation de parenthèses semble avoir le potentiel de confondre l'appariement d'accolades et je préfère les enregistrer dans des expressions. Utiliser] et [est une norme ISO et semble aller mieux avec l'intégration du pas à pas, mais c'est juste mon avis bien sûr.
Homde
7
@MKO: Votre idée d'utiliser des crochets inégalés (comme [1..5[) confondra les éditeurs au moins autant que la notation d'intervalle standard.
David Thornley
2
Hmm, une autre idée, qu'en est-il de l'utilisation! pour, c'est-à-dire: 1 ..! 5 ou 0..5 ..! 20 (avec pas à pas)
Homde
1
@MKO: Utiliser !pour exclure le premier ou le dernier élément pourrait être une bonne chose .
FrustratedWithFormsDesigner
8

Avez-vous vu les gammes Haskell? Leur idée des étapes est similaire à la vôtre (au milieu) mais indiquée par une différence syntaxique (de la réponse de @ luqui ):

[1,2..10] = [1,2,3,4,5,6,7,8,9,10]
[1,3..10] = [1,3,5,7,9]
[4,3..0]  = [4,3,2,1,0]
[0,5..]   = [0,5,10,15,20,25,30,35...  -- infinite
[1,1..]   = [1,1,1,1,1,1,1,1,1,1,1...  -- infinite

Je trouve cette notation très intuitive: vous commencez à énumérer et à sauter le corps pour marquer où il doit se terminer.

Il ne couvre pas le cas de "exclusif", mais franchement, je préfère être clair une fois sur le comportement (spécifier quelle limite est inclusive et laquelle est exclusive), et attend de l'utilisateur qu'il fasse des ajustements à moins que la syntaxe ne soit extrêmement claire (et ne confond pas les éditeurs indépendants de la langue).

Matthieu M.
la source
5

Vous devriez lire les listes de compréhension dans les langues qui les proposent.

Python, par exemple, couvre assez bien vos cas avec la range()fonction et la compréhension de la liste des cas trop complexes pour s'exprimer range().

S.Lott
la source
2

Je pense que votre notation est loin d'être sans ambiguïté. Intuitivement, [0..5..20]cela ne me ressemble pas à une plage avec une largeur de pas = 5. Cela échoue même dans certains cas d'angle: qu'en est-il de l'expression de l'ensemble (0,2) - [0..2..2]ou que dois-je mettre au milieu?

Je pense que la notation de tranche de Python est une approche solide: [start:end:step](l'étape étant facultative).

Si vous avez vraiment besoin de la prise en charge de plages exclusives et inclusives, j'utiliserais la notation de plage standard (c'est-à-dire en utilisant (et [) à moins que cela n'introduise des ambiguïtés syntaxiques dans votre langue.

Alexander Gessler
la source
1
en ce qui concerne la notation « standard », les écoles européennes enseignent [], [[, ]]et ][, sans parenthèses ... et notre norme est mieux, bien sûr;)
Matthieu M.
@Matthieu: En fait, ici aux Pays-Bas (qui est en Europe), nous avons également appris cette notation standard.
Frits
1

Je pense que lorsque vous définissez une troisième valeur d'incrément, vous feriez mieux de l'ajouter à la fin plutôt qu'au milieu, car il s'agit d'une valeur facultative, et semble plus intuitif pour les programmeurs chevronnés que d'ajouter un 3e argument facultatif au milieu .

au lieu de [1..5..20], vous pourriez faire quelque chose comme [1..20] [5] ou [1..20,5]

Canuteson
la source
C'est un point valable et peut-être une question de goût, il me semblait simplement qu'il serait plus facile de penser "1 étape 5 à 20" plutôt que "de 1 à 2, avec l'étape 5" en utilisant [1..20] [5] semble un peu encombré mais peut-être que l'approche par virgule serait viable. J'apprécierais plus de commentaires à ce sujet
Homde
1
  • [1..5 [1,2,3,4
  • ] 1..5] 2,3,4,5
  • ] 1..5 [2,3,4

Pourquoi ne pas simplement utiliser [1..4], [2..5], [2..4]? L'exclusion des plages n'offre pas beaucoup d'avantages. Vous n'avez pas besoin d'écrire MIN + 1 ou MAX-1, oui, mais ils ne l'interdisent pas de toute façon. Trop de variations, à mon humble avis. Ma langue de prédilection, la scala, a (1 à 3) et (1 à 3) [= (1 à 2)] mais cela m'a juste dérouté au début. Maintenant, j'utilise toujours 'x to y' et je sais donc que l'option que je n'utilise pas est celle qui exclut, mais bien sûr, je ne bénéficie pas d'une option que je n'utilise pas.

  • [5..1] 5,4,3,2,1

est bien sûr (1 à MAX) (x => y = (MAX + 1) - x) mais c'est beaucoup plus standard et pas convivial.

  • [0..5..20] 0,5,10,15,20

est bien sûr (0 à 4) (x => y = x * 5) n'est pas tellement passe-partout. Contestable.

Utilisateur inconnu
la source
Le principal avantage de l'exclusion des plages, afaik, est que le nombre d'éléments est la différence des points de terminaison.
Justin L.
@JustinL .: 5-1 est 4 dans mon algèbre mais contient 3 éléments, à l'exclusion des limites: 2, 3, 4.
utilisateur inconnu
1

Et quelque chose comme ça:

  • [5..1] 5,4,3,2,1
  • [1..5] [i, e] 1,2,3,4
  • [5..1] [i, e] 5,4,3,2
  • [1..5] [e, i] 2,3,4,5
  • [1..5] [e, e] 2,3,4
  • [0..20] par 5 0,5,10,15,20

Le iet edans la deuxième paire de crochets indique si le début ou la fin de la plage est inclusif ou exclusif (ou vous pouvez utiliser incl, et exclsi vous voulez être plus clair). le by 5indique l'intervalle de pas. Le premier et le dernier exemple incluent la première et la dernière valeur, j'ai donc omis le [i,i], qui, je pense, serait un comportement par défaut supposé OK.

Les valeurs par défaut peuvent être [i,i]pour le spécificateur inclusif / exclusif et by 1pour le spécificateur d'étape.

Normalement, j'aurais suggéré la notation standard impliquant (et [comme mentionné par @Dan McGrath, mais je conviens que dans le code, cela pourrait sembler déroutant.

FrustratedWithFormsDesigner
la source
Quelqu'un peut-il expliquer le downvote?
FrustratedWithFormsDesigner
Je n'ai rien vu de mal . J'ai voté pour contrer le vote négatif.
Berin Loritsch
1

Et le gagnant est

Désolé d'avoir choisi ma propre solution, j'apprécie vraiment tous les commentaires et commentaires et veuillez critiquer cette solution si vous trouvez quelque chose de mal avec elle

J'ai donc décidé d'aller avec:

0..5           = 0,1,2,3,5
0..5..10       = 0,5,10
..3            = 0,1,3
!0..!5         = 1,2,3,4
3..            = 3 to infinity

segmented ranges
-
0..5,..5..20   = 0,1,2,3,4,5,10,15,20
0..3,10..5..20 = 0,1,2,3,10,15,20

Comme vous pouvez le voir, l'inclusion est la valeur par défaut dans les deux sens, c'est ce qui est intuitivement le plus logique, en particulier lorsque vous utilisez le pas. Vous pouvez utiliser ! pour faire une fin exclusive.

L'inconvénient est que, normalement, vous souhaitez utiliser l'exclusivité lorsque vous travaillez contre un tableau, mais là encore, la plupart du temps, vous devriez probablement utiliser quelque chose comme for-each dans ces cas. Si vous avez vraiment vraiment besoin de travailler contre l'index, l'analyseur pourrait reconnaître quand vous avez peut-être oublié le marqueur d'exclusion à la fin et émettre un avertissement

J'ai choisi d'utiliser .. des deux côtés de la variable step au lieu d'utiliser une virgule ou toute autre chose car c'est ce qui semblait le plus propre et j'espère que c'est le plus lisible.

J'ai également ajouté une syntaxe avec des virgules pour pouvoir créer des plages où différentes parties ont des étapes différentes

Homde
la source
Cela semble correct sauf lors de la spécification de l'étape. Je pense que ce serait plus propre que [0..10 par 5]. La plage segmentée pourrait être de [0..5, ..20 par 5], etc.
supercat
0

Je n'utiliserais pas le formulaire '[1..5 [' pour l'une des mêmes raisons pour lesquelles vous évitez de mélanger les parenthèses et les accolades - cela confondra la correspondance des accolades de la plupart des éditeurs existants. Utilisez peut-être un marqueur à l'intérieur des accolades, comme «[1 .. | 5]».

L'autre chose à considérer est le comportement des méthodes pour obtenir les limites. Par exemple, «début» / «fin» vs «premier» / «dernier» vs «min» / «max». Ils doivent être raisonnables pour les limites inclusives et exclusives, ainsi que pour celles qui ont une taille de pas.

AShelly
la source
0

Ruby a une notation et un objet Range qui fonctionne. Essentiellement, cela ressemble à ceci:

1..5  # range 1,2,3,4,5

Avec Ruby, la taille des pas n'est pas une fonction de la plage, mais plutôt une fonction d'itération à travers la plage. Nous pourrions utiliser la même plage ci-dessus et l'itérer différemment si nous voulions:

(1..5).step(3) { |x| puts x }
(1..5).step(2) { |x| puts x }

Voici donc des éléments à considérer:

  • L'ajout de la prise en charge linguistique pour les points de terminaison inclusifs / exclusifs peut être problématique. Si toutes les plages sont inclusives (choix de Ruby), les développeurs ajusteront les points de terminaison de la plage en conséquence. Comment représentez-vous l'inclusivité ou l'exclusivité des points d'extrémité d'une manière qui est à la fois visuellement sans ambiguïté et sans ambiguïté avec une grammaire d'analyse simple?
  • Utilisez-vous la gamme pour plus qu'une simple itération? Les exemples courants incluent les comparaisons de plages, l'épissage, les comparaisons de valeurs dans les plages. Si oui, comment représentez-vous ces possibilités? (Voir le lien vers la classe Range pour des idées sur chacun de ces exemples.)

BTW, les plages sont plutôt bien si vous autorisez leur inclusion dans une instruction switch comme Ruby le fait:

switch(myval)
    when 0..33 then puts "Low"
    when 34..66 then puts "Medium"
    when 67..100 then puts "High"
end

Je ne dis pas que l'objet de gamme de Ruby est la fin de tout et soyez tout, mais c'est une implémentation fonctionnelle du concept dont vous parlez. Jouez avec pour voir ce que vous aimez, n'aimez pas et feriez différemment.

Berin Loritsch
la source
Lisez attentivement la question, OP est déjà au courant des gammes Ruby:Anyways, one example of range literal I've seen is Ruby which is in the form of 1..3 for an exclusive (on the end) range and 1...3 for inclusive (on the end). You can also do 1..10.step(5).
FrustratedWithFormsDesigner
0

Une solution que j'envisagerais certainement est d'utiliser la surcharge de l'opérateur pour permettre par exemple

1+[0..3]*5

Il ne serait pas nécessairement immédiatement transparent pour tout le monde, mais une fois qu'ils s'arrêteront et penseront que j'espère que la plupart des programmeurs le comprendront et que les personnes qui utilisent régulièrement le langage l'apprendront simplement comme un idiome.

Pour clarifier, le résultat serait de [1, 6, 11, 16]lever la multiplication puis l'addition sur la plage. Je n'avais pas réalisé que cela pouvait être ambigu pour les utilisateurs de Python; Je considère les plages comme des sous-ensembles de commandes totales plutôt que comme des listes. et répéter un ensemble n'a pas de sens.

Peter Taylor
la source
2
Très ambigu. list expression * 5pourrait également signifier "répéter la valeur de list expression5 fois", comme c'est le cas en Python.
nikie
C'est très étrange et je ne sais pas ce que ce serait ... 1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3? 1,3,6,9,12? 5,10,15? autre chose, peut-être? Quoi qu'il en soit, je trouve cette notation complètement contre-intuitive. On dirait que c'est peut-être une sorte d'opération étrange du pointeur C ...
FrustratedWithFormsDesigner