Underload est un tarpit semi-fonctionnel basé sur la pile créé par ais523 . J'ai récemment essayé de jouer au golf, car c'est une langue étonnamment élégante.
Quels conseils avez-vous pour jouer au golf dans Underload? (Un conseil par réponse)
J'aime que la seule forme de flux de contrôle soit une evalcommande, je n'ai jamais vu un langage comme ça auparavant.
ETHproductions
Réponses:
3
Utiliser *pour la sortie
Parce que vous pouvez sortir en laissant une chaîne sur la pile , il peut être utile d'accumuler la chaîne en utilisant *plutôt qu'en sortie avec S. Supposons que votre défi consistait à "prendre une chaîne et ajouter un espace", la façon de le faire avec la sortie serait:
S( )S
La façon de le faire *, en revanche, est un octet plus court:
( )*
Le problème est que si votre sortie a beaucoup d'accumulation, cela peut coûter des octets pour gérer l'élément de sortie sur la pile.
Utilisez un dictionnaire de fonctions réutilisées à plusieurs reprises
Si vous devez beaucoup utiliser un morceau de code, il est logique de stocker ce code sur la pile et de le dupliquer et de l’évaluer de temps en temps. Jusqu'à présent, c'est juste une programmation normale de sous-charge. Malheureusement, conserver une valeur sur la pile pendant longtemps est difficile et a tendance à rendre votre code verbeux, et cela est vrai même si la valeur est une fonction plutôt que des données. Cela devient bien pire si vous avez plusieurs fonctions qui doivent être réutilisées à plusieurs reprises.
Dans le genre de programme plus grand qui pourrait bénéficier de plusieurs fonctions réutilisées, une solution que vous pouvez utiliser consiste à créer une grande fonction qui peut remplir n'importe lequel de leurs objectifs en fonction de la façon dont elle est appelée (soit en fonction de ce qui se trouve en dessous sur la pile, ou via l' utilisation de séquences appelant plus que juste ^, une fonction bien écrite peut distinguer ^^de ^:^partir ^*^de ^~^, vous donnant quatre distincts, des séquences assez courtes). Vous pouvez également stocker d'autres éléments utiles, tels que des chaînes que vous utilisez plusieurs fois, dans ce "dictionnaire". Notez que si vous utilisez beaucoup le dictionnaire, il peut être judicieux d'en faire une sorte de quine, en repoussant une copie de lui-même sur la pile, de sorte que vous n'avez pas besoin de le copier manuellement avec: pour pouvoir l'utiliser sans perdre la possibilité de l'utiliser à l'avenir.
Je deviendrais fou bien avant d'avoir un programme suffisamment gros dans Underload pour que cela devienne un problème: P
Esolanging Fruit
J'ai écrit une fois quelques exemples sur la page esolangs de la façon de faire des dictionnaires avec ma ^!!!!^recherche de style préférée (que j'ai également utilisée dans plusieurs autres exemples sur la page, en particulier dans la section de minimisation.) Bien que cela ne puisse pas donner la recherche la plus courte.
Ørjan Johansen
2
Choisissez des formats de données spécialisés pour les opérations dont le problème a besoin
À titre d'exemple simple, l'implémentation la plus courante des booléens est !()pour false (c'est-à-dire entier 0), et la chaîne nulle pour true (c'est-à-dire entier 1), mais si vous avez un problème qui est fortement basé sur XOR logique, cela pourrait faire plus sens d'utiliser la chaîne null pour false et ~pour true (ce format de données peut être converti en n'importe quel autre format booléen en utilisant (false)~(true)~^!et permet une implémentation très concise *pour XOR.
Il est possible d'aller plus loin dans ce principe général et d'utiliser des fonctions dont votre programme aura besoin plus tard dans le cadre de vos valeurs de données; cela évite d'avoir à stocker les fonctions et les données séparément sur la pile. Cela peut rendre le flux de contrôle un peu plus confus, mais lorsque vous jouez au golf, la maintenabilité doit souvent prendre un siège arrière, et ce n'est pas comme si Underload est tout ce qui est utilisable de toute façon.
J'avais l'habitude d'utiliser (!)et (~!)pour les booléens, mais votre chemin semble meilleur.
Esolanging Fruit
2
Décrément "sale"
La manière fonctionnellement pure de décrémenter un chiffre d'église est d'utiliser la fonction de prédécesseur du calcul lambda:
\n.n(\p.\z.z($(pT))(pT))(\z.z0[whatever you define the predecessor of 0 to be])
Où 0 = \ x. \ Yy, T = \ x. \ Yx et $ est le successeur.
Réécrit dans Underload, cela fait 28 octets:
(!())~(!())~(!:(:)~*(*)*~)~^!
C'est bien, mais nous pouvons exploiter certaines des propriétés utiles de Underload, à savoir que :!et ()*ne sont pas des ops. Cela signifie que, pour un nombre n, :ⁿ!!()()*ⁿ(où cⁿest crépété nfois) donne n-1. Par exemple, faire cela pour le chiffre 3 de l'Église donne ceci:
:::!!()()***
En supprimant les paires sans opération, nous obtenons:
:*
Ce qui est 2.
Voici donc la nouvelle et plus courte opération précédente:
Cela tombe cependant sur n = 0. Si vous en avez besoin pour fonctionner, il (()~(:))~:(^!!())*~(*)~^** reste encore 3 octets plus court.
Ørjan Johansen
@ ØrjanJohansen Généralement, vous auriez un cas spécial pour n = 0, car avec les nombres de Underload, la décrémentation de 0 n'a aucun sens de toute façon.
Esolanging Fruit
1
Mettez des valeurs de pile inutiles dans l'espace du programme
Underload a en fait deux piles: la pile de chaînes et la pile de commandes qui composent le code source. Les ^instructions de Underload nous permettent de déplacer des chaînes de l'ancienne pile vers la seconde. En faisant cela, nous pouvons économiser beaucoup de manipulation inutile de la pile.
Par exemple, disons que nous avons (a)(b)(c)sur la pile principale et que nous aimerions concaténer les deux éléments inférieurs, en les ignorant (c), pour obtenir (ab)(c). La façon naïve de le faire est de faire pivoter la pile pour obtenir (c)(a)(b), puis de concanter et de permuter:
a~a~*~a*^*~
C'est mauvais. L'utilisation a~a~*~a*^de la rotation de la pile comme celle-ci est extrêmement coûteuse et doit être évitée autant que possible. En plaçant (c)à la place dans l'espace du programme, cela peut être raccourci de quatre octets:
a(*)~*^
L'idée est de prendre les instructions que vous souhaitez exécuter, puis d'ajouter une instruction à repousser (c)à la fin, puis d'évaluer le résultat. Cela signifie que nous n'avons pas à nous inquiéter (c)jusqu'à ce qu'il soit repoussé après avoir terminé.
eval
commande, je n'ai jamais vu un langage comme ça auparavant.Réponses:
Utiliser
*
pour la sortieParce que vous pouvez sortir en laissant une chaîne sur la pile , il peut être utile d'accumuler la chaîne en utilisant
*
plutôt qu'en sortie avecS
. Supposons que votre défi consistait à "prendre une chaîne et ajouter un espace", la façon de le faire avec la sortie serait:La façon de le faire
*
, en revanche, est un octet plus court:Le problème est que si votre sortie a beaucoup d'accumulation, cela peut coûter des octets pour gérer l'élément de sortie sur la pile.
la source
Utilisez un dictionnaire de fonctions réutilisées à plusieurs reprises
Si vous devez beaucoup utiliser un morceau de code, il est logique de stocker ce code sur la pile et de le dupliquer et de l’évaluer de temps en temps. Jusqu'à présent, c'est juste une programmation normale de sous-charge. Malheureusement, conserver une valeur sur la pile pendant longtemps est difficile et a tendance à rendre votre code verbeux, et cela est vrai même si la valeur est une fonction plutôt que des données. Cela devient bien pire si vous avez plusieurs fonctions qui doivent être réutilisées à plusieurs reprises.
Dans le genre de programme plus grand qui pourrait bénéficier de plusieurs fonctions réutilisées, une solution que vous pouvez utiliser consiste à créer une grande fonction qui peut remplir n'importe lequel de leurs objectifs en fonction de la façon dont elle est appelée (soit en fonction de ce qui se trouve en dessous sur la pile, ou via l' utilisation de séquences appelant plus que juste
^
, une fonction bien écrite peut distinguer^^
de^:^
partir^*^
de^~^
, vous donnant quatre distincts, des séquences assez courtes). Vous pouvez également stocker d'autres éléments utiles, tels que des chaînes que vous utilisez plusieurs fois, dans ce "dictionnaire". Notez que si vous utilisez beaucoup le dictionnaire, il peut être judicieux d'en faire une sorte de quine, en repoussant une copie de lui-même sur la pile, de sorte que vous n'avez pas besoin de le copier manuellement avec:
pour pouvoir l'utiliser sans perdre la possibilité de l'utiliser à l'avenir.la source
^!!!!^
recherche de style préférée (que j'ai également utilisée dans plusieurs autres exemples sur la page, en particulier dans la section de minimisation.) Bien que cela ne puisse pas donner la recherche la plus courte.Choisissez des formats de données spécialisés pour les opérations dont le problème a besoin
À titre d'exemple simple, l'implémentation la plus courante des booléens est
!()
pour false (c'est-à-dire entier 0), et la chaîne nulle pour true (c'est-à-dire entier 1), mais si vous avez un problème qui est fortement basé sur XOR logique, cela pourrait faire plus sens d'utiliser la chaîne null pour false et~
pour true (ce format de données peut être converti en n'importe quel autre format booléen en utilisant(false)~(true)~^!
et permet une implémentation très concise*
pour XOR.Il est possible d'aller plus loin dans ce principe général et d'utiliser des fonctions dont votre programme aura besoin plus tard dans le cadre de vos valeurs de données; cela évite d'avoir à stocker les fonctions et les données séparément sur la pile. Cela peut rendre le flux de contrôle un peu plus confus, mais lorsque vous jouez au golf, la maintenabilité doit souvent prendre un siège arrière, et ce n'est pas comme si Underload est tout ce qui est utilisable de toute façon.
la source
(!)
et(~!)
pour les booléens, mais votre chemin semble meilleur.Décrément "sale"
La manière fonctionnellement pure de décrémenter un chiffre d'église est d'utiliser la fonction de prédécesseur du calcul lambda:
Où 0 = \ x. \ Yy, T = \ x. \ Yx et $ est le successeur.
Réécrit dans Underload, cela fait 28 octets:
C'est bien, mais nous pouvons exploiter certaines des propriétés utiles de Underload, à savoir que
:!
et()*
ne sont pas des ops. Cela signifie que, pour un nombren
,:ⁿ!!()()*ⁿ
(oùcⁿ
estc
répétén
fois) donne n-1. Par exemple, faire cela pour le chiffre 3 de l'Église donne ceci:En supprimant les paires sans opération, nous obtenons:
Ce qui est 2.
Voici donc la nouvelle et plus courte opération précédente:
C'est 7 octets plus court.
la source
(()~(:))~:(^!!())*~(*)~^**
reste encore 3 octets plus court.Mettez des valeurs de pile inutiles dans l'espace du programme
Underload a en fait deux piles: la pile de chaînes et la pile de commandes qui composent le code source. Les
^
instructions de Underload nous permettent de déplacer des chaînes de l'ancienne pile vers la seconde. En faisant cela, nous pouvons économiser beaucoup de manipulation inutile de la pile.Par exemple, disons que nous avons
(a)(b)(c)
sur la pile principale et que nous aimerions concaténer les deux éléments inférieurs, en les ignorant(c)
, pour obtenir(ab)(c)
. La façon naïve de le faire est de faire pivoter la pile pour obtenir(c)(a)(b)
, puis de concanter et de permuter:C'est mauvais. L'utilisation
a~a~*~a*^
de la rotation de la pile comme celle-ci est extrêmement coûteuse et doit être évitée autant que possible. En plaçant(c)
à la place dans l'espace du programme, cela peut être raccourci de quatre octets:L'idée est de prendre les instructions que vous souhaitez exécuter, puis d'ajouter une instruction à repousser
(c)
à la fin, puis d'évaluer le résultat. Cela signifie que nous n'avons pas à nous inquiéter(c)
jusqu'à ce qu'il soit repoussé après avoir terminé.la source
(*)~a*^
, je pense que c'est un peu plus composable. C'est essentiellement~a*^
ladip
commande de Joy.