J'écris une langue de golf.
Suggérez-vous des variables, des piles, des bandes, des registres, etc. pour le stockage dans un langage de code-golf? Qu'en est-il de la saisie implicite?
Définitions approximatives:
- Une variable est simplement un nom (généralement un caractère long dans les langues de golf) auquel une valeur peut être affectée, puis récupérée par ce nom.
- Un registre est comme une variable, mais il a ses propres commandes (généralement à un octet) pour définir / obtenir la valeur.
- Une pile est un tableau / liste de valeurs de longueur variable, où les dernières valeurs ajoutées (les valeurs "en haut") sont celles qui sont en cours de modification.
- Une file d'attente est comme une pile, sauf que les valeurs "en bas " sont celles qui sont modifiées.
- Une bande est un tableau / liste de valeurs statique où chaque valeur a un index. La principale différence entre une pile et une bande est que les valeurs d'une bande sont modifiées sur place.
J'apprécierais de connaître les avantages et les inconvénients de chaque option pour différents scénarios et dans l'ensemble. Veuillez éviter les opinions et les déclarations de sauvegarde avec raisonnement.
code-golf
tips
language-design
golfing-language
programmer5000
la source
la source
Réponses:
Tous les types de stockage impliquent de stocker quelque chose à un moment donné et de le récupérer plus tard. Pour ce faire en une seule opération, vous devez soit stocker ou récupérer automatiquement, et spécifier la position de la valeur stockée dans l'autre opération.
Autrement dit, pour un stockage explicite, vous pouvez créer un opérateur pour récupérer la nième valeur calculée avant cette opération, ou remettre la valeur actuelle après n opérations. Alternativement, vous pouvez utiliser la position absolue depuis le début du programme, ou faire plus de choses comme supprimer automatiquement certains éléments après certaines opérations (comme dans une pile). Vous pouvez également créer plusieurs opérateurs, en récupérant à partir de différentes copies du stockage avec ou sans ces opérations automatiques. Et vous devriez essayer de faire le nombre maximum nécessaire pour spécifier dans les opérations raisonnablement petit, afin que vous puissiez affecter un opérateur pour chaque numéro.
Mais dans la plupart des cas, vous n'avez même pas besoin d'un opérateur et le langage le fera implicitement. C'est à ce moment que vous devez envisager un modèle plus standardisé tel que des piles ou des files d'attente. La plus réussie semble être la programmation tacite, qui ne mentionne même pas directement le stockage.
Si vous souhaitez concevoir un nouveau modèle de ce type, vous pouvez essayer d'étendre les évaluations en tant que dag et essayer de penser à un dag par défaut si rien d'autre n'est spécifié. Très probablement, la valeur par défaut est juste un arbre, sauf que plusieurs feuilles peuvent être liées à la même entrée. Vous pouvez par exemple utiliser une file d'attente pour un arbre équilibré, ou une pile pour un arbre profond où les feuilles sont généralement constantes, ou quelque chose comme Jelly pour un arbre profond où les feuilles sont principalement des copies de l'entrée.
Mais notez que vous pouvez encoder la forme d'un arbre binaire en seulement 2 bits par opérateur. Donc, si votre langue a moins de 64 opérateurs, vous pouvez en fait ignorer les modèles traditionnels et simplement encoder l'arborescence complète dans les bits de rechange (appelez-les flags combine_parent et below_leaf). Même s'il y a plus d'opérateurs, vous pouvez faire un assez bon défaut (comme le modèle de Jelly) et 3 modificateurs pour le changer.
Vous pouvez utiliser le même modèle pour le stockage implicite et explicite pour plus de commodité, mais ce n'est pas obligatoire. Par exemple, vous pouvez utiliser une pile pour le stockage implicite, mais ne pas insérer d'éléments dans le stockage explicite (ou dans un autre stockage explicite en plus de celui implicite). Il est probable que cela ne s'appellera pas une pile dans la documentation finale, mais vous avez l'idée.
Pour référence, la taille du codage parfait d'un arbre binaire est le logarithme des nombres catalans . Et la taille de l'encodage parfait d'un dag "binaire" est le logarithme de A082161 , mais évidemment peu pratique. Cela suppose qu'un opérateur avec un ordre d'argument différent deux opérateurs différents, ajoutant un autre bit quand il ne l'est pas.
Parfois, vous pouvez toujours vouloir des variables pour les boucles. Il peut être possible de réécrire les boucles de différentes manières. Mais si vous en avez vraiment besoin, n'utilisez pas une construction à 1 octet en plus d'un nom pour définir la variable. à moins que vous n'utilisiez que les valeurs préinitialisées, il est généralement plus efficace d'utiliser un indicateur 1 bit pour spécifier si vous lisez ou écrivez cette variable.
la source
Je les suggère tous!
Plus sérieusement, ils sont tous utiles à certains moments, et le plus sera le mieux! La saisie implicite n'est jamais mauvaise , il suffit d'avoir un indicateur pour la désactiver. Les variables sont utiles, donc elles n'ont pas besoin d'être trouvées sur une pile ou une bande; idem pour les registres. Les piles sont utiles pour stocker des données, tout comme les bandes. Je recommande d'essayer d'implémenter plusieurs, disons une pile et des registres, ou une pile et des variables, comme GolfScript. Si vous pouvez faire de chaque fonction un octet, votre langue sera probablement efficace au golf, car vous pouvez utiliser les avantages de chacune.
Un exemple:
Exemple de code dans GolfScript (avec entrée implicite):
Cependant, avec des variables (je sais que c'est plus long, il n'a tout simplement pas à changer de place sur la pile):
Surcharges
Les surcharges peuvent également être utiles. Par exemple, si vous avez une fonction de stockage variable, elle pourrait peut-être être utilisée comme une monade (fonction à une entrée; je ne suis pas sûr du terme en dehors de J / K / APL) à ajouter à la pile ou à la bande.
Exemple:
la source
Je suggérerais d'avoir un stockage rapidement utilisable (à partir de la donnée - bande, file d'attente, pile) et un stockage permanent (variables, registres) pour que les choses ne gênent pas pendant que le programme fait quelque chose sans rapport. Je dirais que beaucoup plus donnerait rarement quoi que ce soit et laisserait plus de caractères libres pour plus d'instructions sur 1 octet.
D'après les définitions données, celles qui, selon moi, fonctionneraient le mieux seraient une pile et des registres.
Une pile, car une bande doit utiliser des fonctions juste pour y stocker une nouvelle chose, tandis qu'une pile doit avoir de simples fonctions push et pop (généralement intégrées dans les commandes). Les registres, car ils prennent généralement moins d'octets que les variables et si vous devez stocker plus de 2 à 4 choses différentes pour quelque chose, vous faites quelque chose de mal.
Ne limitez pas leurs fonctions uniquement à ce que leurs noms ou définitions suggèrent - certaines fonctions comme
put the 1st thing of the stack on top of the stack
peuvent certainement aider.la source
Il existe essentiellement deux types de stockage qui doivent être gérés différemment; accès aux valeurs récemment générées et / ou aux entrées; et stockage à long terme.
Pour le stockage à long terme, les variables semblent fonctionner le mieux; tout ce qui a un nombre limité d'options n'est pas évolutif (bien que les langues avec cette propriété, comme Jelly, peuvent néanmoins faire assez bien même sur des tâches de taille moyenne). Cependant, il n'est pas nécessaire de fournir une variable noms de lors du stockage de la variable, la plupart du temps; juste avoir une commande pour stocker une valeur dans la variable, et générer automatiquement les noms selon un modèle prévisible afin que le stockage et la récupération de la valeur puissent être une commande chacun dans des cas simples. (Par exemple, vous pouvez avoir des commandes pour restaurer la variable la plus récemment affectée, la deuxième plus récemment, la troisième plus récemment, et ainsi de suite, jusqu'à un petit nombre fixe, plus une commande générale qui a pris un argument.)
Pour le stockage à court terme, vous voulez autant être implicite que possible. Presque toutes les langues de golf que j'ai vues connectent la sortie d'une commande à une entrée de la suivante, par défaut; la manière exacte dont cela se fait diffère d'une langue à l'autre mais revient normalement à la même chose. Cependant, la deuxième entrée pour une commande à 2 entrées est plus intéressante. Le retirer d'une pile fonctionne bien dans les cas où les valeurs ne sont utilisées qu'une seule fois, mais ne sont pas bien mises à l'échelle lors de la réutilisation des valeurs. (Essayez d'éviter les primitives de manipulation de pile; si vous devez les utiliser, vous gaspillez beaucoup d'octets.) Alternativement, en utilisant l'entrée utilisateur ou une variable récemment affectée comme second argument implicite, vous avez tendance à économiser quelques octets sur des programmes simples, bien que vous
Dans une langue de golf sur laquelle je travaille en ce moment, j'utilise une combinaison d'un mécanisme très bon marché (un seul bit ) pour spécifier la forme de l'arbre d'analyse, remplissant automatiquement les arguments manquants des entrées utilisateur par défaut, et un point de contrôle approche qui permet de définir la valeur par défaut pour les arguments manquants à autre chose (ainsi que de nombreux cas spéciaux). À un moment donné, je vais probablement ajouter des variables pour communiquer des informations sur de plus grandes distances tout au long du programme.
la source
Je propose une cassette et un registre.
Je préfère les bandes aux piles parce que les bandes ont généralement moins d'éléments, ce qui facilite leur manipulation. De plus, le fait de pouvoir placer des éléments dans la bande dans le registre et vice versa permet un code court et facile.
la source