Quoi, ce post n'existe pas encore?
Bien entendu, GolfScript est conçu pour le golf. Vous pouvez donc penser qu’aucun conseil particulier n’est vraiment nécessaire. Mais pour utiliser pleinement les fonctionnalités de GolfScript, vous devez apprendre quelques astuces non évidentes. Cet article a pour but de recueillir de tels conseils et astuces utiles.
Pour commencer, voici les pages de référence officielles de GolfScript. Vous devriez vraiment vous familiariser avec ces premiers:
En particulier, je suggérerais fortement de lire les pages dans cet ordre - la référence rapide est inutile si vous n'êtes pas suffisamment familiarisé avec les fonctions intégrées, et le didacticiel inclut des détails importants qui ne sont pas expliqués dans les autres pages. .
Ps. Pour des raisons d’inspiration et d’intérêt personnel, voici quelques questions pour lesquelles j'aimerais avoir de bonnes réponses:
Comment faire une translittération limitée dans GolfScript?
{FROM?TO=}%
fonctionne si vous pouvez être sûr que toutes les entrées sont présentes dansFROM
(ou ne dérange pas qu'elles soient toutes mappées au dernier élément deTO
), mais toutes les manières que j'ai vues de laisser les valeurs non mappées inchangées ont été plus ou moins klugey.Comment convertir au mieux une chaîne en un tableau de codes ASCII et inversement? Quelles opérations font cela comme un effet secondaire? Quelle est la meilleure façon de vider les caractères d'une chaîne sur la pile (comme le
~
font les tableaux)?
la source
... x
en... [x]
? Le mieux que je puisse voir est[.;]
.x
est un nombre, alors[]+
fonctionne et est un caractère plus court. Et bien sûr, six
est la seule chose sur la pile, alors tout]
ira bien.Réponses:
Rationnel / Flotter / Complexe
J'ai lu tellement de fois que GolfScript n'a que des entiers que j'ai commencé à y croire. Eh bien, ce n'est pas vrai.
La sortie est
avec l’interprète GolfScript standard et
sur Web GolfScript .
Des hacks similaires permettent de lancer vers Rational, Float ou même Complex:
la source
Gint.new(@val**b.val)
. Il semble que leGint
constructeur manque un casting int ...Nier un numéro
Il manque à GolfScript un opérateur de négation intégré. Les manières évidentes de convertir un nombre sur la pile en son négatif, comme
-1*
ou0\-
, nécessitent trois caractères. Cependant, il existe un moyen de le faire en deux:Cela fonctionne parce que GolfScript utilise l' arithmétique du complément à deux , de sorte que ~ x est égal à - x −1.
Bien sûr, la variante
(~
fonctionne également; choisir entre eux est généralement une question de goût.la source
Mélanger un tableau
Le moyen le plus simple de mélanger un tableau dans GolfScript consiste à le trier selon une clé de tri aléatoire. Si vous avez seulement besoin de mélanger grossièrement quelques valeurs, le code suivant fera l'affaire:
Notez que, même pour les listes courtes, cela ne donnera pas un très bon brassage. En raison du paradoxe de l' anniversaire , pour obtenir un brassage raisonnablement uniforme, l'argument de
rand
doit être nettement supérieur au carré de la longueur de la liste en cours de brassage.Remplacer ce qui
9
précède99
donne donc des résultats raisonnablement bons pour des listes contenant jusqu'à dix éléments, mais présente un biais notable pour les listes plus longues.Le code suivant, qui utilise 9 9 = 387 420 489 valeurs possibles, convient à environ 1000 éléments environ (et à environ 20 000):
Pour les listes vraiment longues, ajoutez encore un 9 pour 99 99 ≈ 3,7 × 10 197 valeurs:
Essai:
Voici la répartition du premier élément d'une liste de 10 éléments mélangés à l'aide des différentes variantes présentées ci-dessus, échantillonnées sur 10 000 essais:
Le résultat de
10,{;9rand}$0=
montre un biais très clair, avec0
plus de trois fois plus de chances de finir en première position que1
:Avec
10,{;99rand}$0=
, la plupart des biais est parti, mais un montant notable reste toujours:Avec
10,{;9.?rand}$0=
, la sortie est fondamentalement indiscernable d’un échantillon vraiment aléatoire:Ps. Pour un très mauvais brassage de tableaux ou de chaînes numériques, le code suivant peut parfois être acceptable:
Il sera généralement ridiculement biaisé, mais tant que tous les éléments du tableau en entrée (ou tous les codes de caractères dans la chaîne) sont supérieurs à un, il a une probabilité non nulle de produire une permutation du tableau qui peut parfois satisfaire exigences de défi mal écrites.
la source
Pour répondre à une sous-question spécifique:
Pour ceux qui ne comprennent pas le problème, le système de types de GolfScript donne la priorité aux types dans l'ordre entier, tableau, chaîne, bloc. Cela signifie que les opérations de tableau ordinaires appliquées à une chaîne vous donnent presque toujours une chaîne. Par exemple
va laisser
'BCD234'
sur la pile.Par conséquent, le meilleur moyen de convertir une chaîne en un tableau de codes ASCII consiste presque certainement à vider les caractères de la pile, puis à les rassembler dans un tableau.
Quelle est la meilleure façon de vider les caractères d'une chaîne sur la pile?
{}/
Quel est le meilleur moyen de convertir une chaîne en un tableau de codes ASCII?
[{}/]
(avec l'avertissement habituel que s'il n'y a rien d'autre sur la pile, vous pouvez sauter le[
)Quel est le meilleur moyen de convertir un tableau de codes ASCII en chaîne?
''+
(Notez que cela aplatit aussi le tableau, donc par exemple[65 [66 67] [[[49] 50] 51]]''+
donne'ABC123'
)la source
[]+''+
? (semble assez long)Si votre programme se brise mystérieusement, vérifiez vos variables
Je viens de passer un certain temps à déboguer un programme apparemment correct qui servait
!
de variable (au motif que je ne l'utilisais plus). Malheureusement, j’en ai utiliséif
et il s’avère que la mise en œuvre d’if
appels permet!
de décider quelle branche suivre.la source
Envelopper le premier élément de la pile dans un tableau
Pour une généralité complète, la meilleure option semble être 4 caractères. Cependant, dans certains cas particuliers, il est possible de réduire cela.
1 caractère
]
fonctionne dans le cas spécial quix
est la seule chose sur la pile.3 caractères
[]+
fonctionne dans le cas particulier quix
est un entier..,/
fonctionne dans le cas particulier quix
est un tableau ou une chaîne de vérité. Par exemple"AB".,/
donne["AB"]
;3,.,/
donne[[0 1 2]]
. Cependant,"".,/
et les[].,/
deux donnent[]
.4 caractères
[.;]
fonctionne sans condition.la source
C'est une bonne question. Il n’existe aucun moyen direct d’attribuer une valeur à un élément de tableau dans GolfScript. Par conséquent, d’une manière ou d’une autre, vous devrez reconstruire l’ensemble du tableau.
La méthode générale la plus courte que je connaisse pour insérer une nouvelle valeur
x
à indexi
dans un tableau consiste à scinder le tableau à l'index donné et à l'ajouterx
à la première moitié avant de les réunir à nouveau:.i<[x]+\i>+
(11 caractères) - insère la valeurx
dans le tableau à l'index (basé sur 0)i
Pour remplacer la valeur à index
i
parx
, il suffit de raccourcir la seconde moitié du tableau par un élément:.i<[x]+\i)>+
(12 caractères) - remplace l'élément à l'index (base 0)i
par la valeurx
Alternativement, raccourcir la première moitié fera exactement la même chose, mais avec une indexation basée sur 1, ce qui peut parfois être préférable:
.i(<[x]+\i>+
(12 caractères) - remplace l'élément à l'index (basé sur 1)i
par la valeurx
Dans tous les exemples ci-dessus, si
x
est un nombre, les crochets autour de celui-ci peuvent être omis pour sauvegarder deux caractères, car ils seront automatiquement forcés dans un tableau par+
:.i<x+\i>+
(9 caractères) - insère le nombrex
dans le tableau à l'index (basé sur 0)i
.i<x+\i)>+
(10 caractères) - remplace l'élément à l'index (basé sur 0)i
par le nombrex
.i(<x+\i>+
(10 caractères) - remplace l'élément à l'index (basé sur 1)i
par le nombrex
Les crochets peuvent également être omis si l'un
x
ou l'autre "tableau" en entrée (ou les deux) sont en réalité des chaînes, auquel cas le résultat sera également forcé dans une chaîne (en utilisant les règles de conversion tableau → chaîne habituelles).Ps. Dans un cas particulier, si nous savons que le tableau a entre
i
et 2 ×i
éléments, nous pouvons insérer un nouvel élémentx
à l'index ( basé sur 0)i
aveci/[x]*
(6 caractères). Ce que cela fait réellement, c'est diviser le tableau en morceaux de jusqu'ài
éléments et insérerx
entre chaque morceau. Notez que, dans ce cas, les crochets sont nécessaires même s’ilx
s’agit d’un nombre.Pps. Une autre approche consiste à utiliser des variables nommées dynamiquement. Par exemple,
assignera la valeur
'foo'
à la variablex42
, tandis queva le récupérer.
Vous pouvez optimiser cela davantage en omettant le
x
préfixe et en assignant directement les littéraux numériques - ceci est parfaitement légal dans GolfScript, et vous permet d'enregistrer un caractère du code d'attribution et de raccourcir le code de récupération en`~
(ou rien du tout, si l'indice est constant!). L'inconvénient, bien sûr, est que l'affectation à un littéral numérique remplacera la valeur de ce littéral n'importe où ailleurs dans votre code. Souvent, cependant, l’utilisation de littéraux numériques peut être évitée (ou au moins limitée au début du programme, avant qu’un d’eux ne soit réaffecté), auquel cas cette astuce est parfaite.la source
i
pour 9 octets:.[i=]/[x]*
Manipulation finale
Par défaut, à la fin de votre programme, l’interprète GolfScript affiche tout ce qui se trouve sur la pile, ainsi qu’une nouvelle ligne finale, exactement comme si votre programme se terminait par:
Ce que la documentation ne mentionne pas directement, c’est que l’interprète appelle littéralement l’intégré
puts
pour produire cette sortie, et que cela est intégré. littéralement définie comme suit:Ainsi, vous pouvez supprimer ou manipuler la sortie finale en redéfinissant
si vous vous sentez vraiment tordu). Voici quelques exemples:
puts
,print
et / oun
(ouSupprimer la nouvelle ligne finale:
(Vous pouvez bien sûr laisser de côté
;
si vous ne voyez pas d'inconvénient à ajouter une chaîne vide sur la pile.)Supprimer complètement la sortie finale:
Cela écrase
puts
avec tout ce qui se trouve au sommet de la pile. S'il s'agit de quelque chose que vous ne voulez pas exécuter, vous pouvez utiliser, par exemple, à la0:puts;
place. Notez que ceci supprime égalementp
(ce qui est défini comme{`puts}:p;
), mais vous pouvez toujours utiliserprint
pour la sortie si vous le souhaitez.la source
nothing
vous voulez dire\n
?];
pour supprimer la sortie finale.min max
Pour trouver la valeur la plus petite / la plus grande dans un tableau, il suffit de la trier et de prendre le premier / dernier élément:
$0=
(3 caractères) - élément minimum dans un champ$-1=
(4 caractères) - élément maximum dans un tableauSi vous connaissez la longueur du tableau et que son nombre est inférieur ou égal à 10, vous pouvez trouver le maximum dans trois caractères en le remplaçant
-1
par l'index du dernier élément.Si vous avez les valeurs sur la pile, vous pouvez d'abord les rassembler dans un tableau. Pour cela, une astuce parfois utile consiste à
[\]
rassembler les deux premiers éléments de la pile dans un tableau, tout en[@]
collectant les trois premiers. Ainsi, nous obtenons:[\]$0=
(6 caractères) - minimum de deux valeurs sur la pile[@]$0=
(6 caractères) - minimum de trois valeurs sur la pile[\]$1=
(6 caractères) - maximum de deux valeurs sur la pile[@]$2=
(6 caractères) - maximum de trois valeurs sur la pileLa même astuce peut également être utilisée pour trouver la médiane de trois valeurs, ce qui peut être parfois utile:
[@]$1=
(6 caractères) - médiane de trois valeurs sur la pileVoici une autre astuce potentiellement utile pour trouver le minimum / maximum de deux valeurs tout en laissant les valeurs d'origine sur la pile :
.2$>$
(5 caractères) - recherche un minimum de deux valeurs sur la pile, sans altérer les valeurs d'origine.2$<$
(5 caractères) - recherche un maximum de deux valeurs sur la pile, sans altérer les valeurs d'origineLa façon dont cela fonctionne est que
.2$
clone les deux premiers éléments de la pile dans l'ordre inverse (c.-à-d.a b
→a b b a
),<
/>
compare les copies et renvoie 0 ou 1, et scalar$
copie ensuite l’une ou l’autre des valeurs en fonction du résultat de la comparaison.Si vous avez deux entiers non négatifs sur la pile, vous pouvez utiliser
,\,&,
(5 caractères) pour trouver leur minimum et,\,|,
(5 caractères) pour trouver leur maximum. Cette astuce utilise set intersection et union, respectivement, sur les plages. Vous pouvez enregistrer un autre caractère s’il est possible d’appliquer,
séparément chaque argument sans avoir à les échanger. Comme cette méthode calcule une plage pour chaque argument, elle n’est pas très efficace pour les grands nombres, mais peut être très utile pour les plus petites entrées.Une méthode encore plus courte pour trouver le minimum de deux entiers non négatifs sur la pile est
,<,
(3 caractères). Hélas, cette astuce ne fonctionne pas pour trouver le maximum.valeur absolue
L’ opérateur de valeur absolue intégré à GolfScript est
abs
(3 caractères). Bien que ce soit deux personnages plus que je ne le préférerais, il est difficile de battre en général.Dans certains cas (par exemple, pour le tri par valeur absolue), vous pouvez trouver le carré d’un nombre comme substitut adéquat à sa valeur absolue; cela peut être calculé en deux caractères,
2?
ou.*
. Ainsi, nous obtenons:{.*}$0=
(7 caractères) - élément minimum par valeur absolue dans un tableau{.*}$-1=
(8 caractères) - élément maximum par valeur absolue dans un tableauDe même, au lieu de tester par exemple si la valeur absolue d'un nombre est inférieur à 3 avec
abs 3<
(6 caractères, espace compris), vous pouvez tester si son carré est inférieur à 9 avec.*9<
(4 caractères, aucun espace requis).la source
,\,&,
(5 caractères) pour trouver leur minimum et,\,|,
(5 caractères) pour trouver leur maximum. Cette astuce utilise set intersection et union, respectivement, sur les plages. Vous pouvez enregistrer un autre caractère s’il est possible d’appliquer,
séparément chaque argument sans avoir à les échanger. Comme cette méthode calcule une plage pour chaque argument, elle n’est pas très efficace pour les grands nombres, mais peut être très utile pour les plus petites entrées.Supprimer les doublons d'un tableau
Les opérateurs d'ensemble
|
(union),&
(intersection) et^
(différence symétrique) réduiront plusieurs éléments du tableau en un. Ainsi, le moyen le plus simple de supprimer les éléments en double d'un tableau consiste à prendre son union ou intersection avec lui-même:ou:
Ces opérateurs traiteront les chaînes comme des tableaux de caractères. Ils pourront donc également être utilisés pour supprimer les caractères en double des chaînes.
la source
Translittération limitée
Pour répondre à une sous-question spécifique: à partir d'une chaîne, quel est le meilleur moyen d'effectuer une opération
tr
? Par exempletr/ABC/abc/
Si tous les caractères de la chaîne sont affectés, la procédure est simple:
{'ABC'?'abc'=}%
(surcharge: 9 caractères).Cependant, cela casse si certains des personnages ne sont pas translittérés et
'ABC'?
donne-1
.Si la translittération n'est pas cyclique, vous pouvez effectuer un remplacement à la fois avec des divisions et des jointures de chaînes:
'AaBbCc'1/2/{~@@/\*}/
(surcharge: 15 caractères). Cela peut être amélioré, mais il existe une approche alternative qui est actuellement meilleure et fonctionne pour les translittérations cycliques.Actuellement, les solutions générales les plus courtes ont un overhead de 14 caractères:
Une approche implique un caractère d'échappement:, où le désigne un octet nul littéral. (Bien entendu, cette méthode n'est pas complètement générale: elle ne peut mapper aucun autre caractère dans un octet nul.)
{.'ABC'?'abc0'=\or}%
0
Alternativement,
{.'ABC'?'abc'@),+=}%
a la même surcharge, mais utilise uniquement des caractères ASCII imprimables. Le@),+
est un moyen compliqué (mais, apparemment, le plus court) de s'assurer que la chaîne de remplacement se termine toujours par le caractère saisi.la source
'ABCDEF'
I get the result'abc000'
, but the right result would be'abcDEF'
. Am I missing something?Turn a string to an array of char
You can do this by typing:
1/
after it.Example:
"String"1/
pushes to stack the array['S''t''r''i''n''g']
.This is handy when you want to move chars around the string.
la source
"abc"1/(+
->"bca"
, but"abc"(+
->bc97
.Assigning to number literals
Often, instead of writing
1:x
and then using/updating the variablex
, you can just use and update1
directly:Of course, this also works for other starting values, but will break if that value occurs anywhere else in your code.
Punctuation as variable names
If you have to use variables, it's also often wise to use punctuation that isn't already in your code -- lots of programs can do without
&
,|
,^
, or?
. This way, for example, you can write&n
instead ofx n
to push your variable and then push a newline.la source
!
is often a bad idea, as it will breakif
anddo
(as well aswhile
,until
,and
,or
andxor
). Similarly,or
is defined by the interpreter as an alias for1$\if
, so redefining1
,$
or\
will also break it. Redefining`
breaksp
.Filtering an array
The most general way to filter an array is to use
{ },
, which evaluates the code block for each element of the array, and selects those elements for which the resulting value is true (i.e. it acts likegrep
in Perl).However, using the array subtraction operator
-
is often shorter. This operator takes two arrays, and removes every element that occurs in the second array from the first. It does not alter the order of the elements in the first array or collapse duplicates. A useful trick is to apply the subtraction operation twice to yield a non-collapsing array intersection operator:a b -
: remove any elements found in arrayb
from arraya
a. b --
: remove any elements not found in arrayb
from arraya
In particular, this can be used to count the number of times an element occurs in an array:
a.[c]--,
: count the number of times the elementc
occurs in the arraya
In general, this method is not optimal, since either of:
a[c]/,(
: count the number of times the elementc
occurs in the arraya
a{c=},,
: count the number of times the elementc
occurs in the arraya
is one character shorter (and, if it's OK for the count to be off by one,
a[c]/,
saves one character more). However, in the special case wherec
is a number anda
is a normal array (not a string), the square brackets aroundc
may be omitted because the-
operator coerces its arguments to the same type:a.c--,
: count the number of times the numberc
occurs in the array (not string!)a
(If
a
is a string andc
is a number between 0 and 9,a.c--
will count the number of times the digitc
occurs ina
.)A similar trick can by used to find the most common element in an array:
Again, if the input is an array of numbers, the whole
[.]
sequence may be omitted. Alas, this does not work for strings without the[.]
.la source
a[c]/,(
anda{c=},,
are one byte shorter.Read from STDIN
GolfScript can read from stdin:
This will continue reading from STDIN until the EOF is reached. Alternatively:
or
Other things available:
For each of these, they can only be used once (and also once for each change of the parameter, also once more with empty parentheses); after that, the original value is what you'll get instead of a new value.
la source
{"#{STDIN.readline}"p}2*
does not read 2 lines but instead the string is evaluated only once.i
to any integer,'"#{'i):i';STDIN.gets}"'++~
will give a different result every time it is evaluated. Backticks may also be worth mentioning. If we assume Linux, we can use, e.g.,`head -1`
instead ofSTDIN.gets
."#{var'g','gpush Gstring.new(STDIN.gets)'.cc}";
would also let you define a new GolfScript operatorg
that reads a line from stdin and pushes it on the stack.Decoding hexadecimal input
GolfScript has no hex integer literals, so, alas, you can't just parse hexadecimal input with
~
. Instead, if your code must take hex input, you'll need to parse it manually.This 8-char loop, applied to a string, will convert lowercase hex digits to their numeric equivalents:
If you have to (also) accept uppercase hex digits, the easiest (and likely shortest) solution is to first lowercase them with
32|
, for a total of 11 chars:Note that the output will technically still be a string (consisting of the ASCII characters 0 – 15), but most GolfScript array functions will accept strings, too. If you absolutely need an array, you can always use
[{39%9-}/]
(where the first[
is optional if the stack is otherwise empty).To convert the output of the code above into an integer, you can simply use
16base
(6 chars). If you want an array of bytes instead, the shortest solution I've found in simply to decode each pair of hex digits with2/{16base}%
(11 chars). All put together, the shortest code I've found to turn a hex string into a byte array is 8 + 11 = 19 chars:Note that the output of this code is indeed an array, not a string. If needed, you can stringify it by concatenating it e.g. with
""+
or, if you don't mind an extra newline at the end,n+
.la source
Defining new built-in operators
The standard GolfScript interpreter has a rarely used feature that allows interpolated Ruby code in double quoted string literals.
One reason why this feature isn't more commonly used is that, awkwardly, the interpolated code is executed at compile time, and the output is cached by the GolfScript interpreter so that the same string literal will thereafter always yield the same value, even inside string eval.
However, one thing this feature turns out to be good for is defining new GolfScript operators implemented in Ruby code. For example, here's how to define a new binary addition operator that works just like the standard built-in
+
operator:It doesn't really matter where you put the definition in your code; the new operator gets defined as soon as the double-quoted string containing the Ruby code is parsed. The
add
operator defined above works exactly like the built-in+
operator, and can be used in exactly the same way:Of course, defining a new addition operator is pretty useless, unless you've done something silly like erase the built-in
+
operator. But you can use the same trick to define new operators that do things Golfscript cannot (easily) do natively such as, say, uniformly shuffling an array:or printing the contents of the whole stack:
or interactive input:
or even web access:
Of course, a somewhat golfier (and riskier!) implementation of the latter would be e.g.:
While not particularly golfy in itself, this lets you extend the capabilities of GolfScript beyond what the built-in commands provide.
How does it work?
The authoritative reference on how to define new GolfScript operators in this way is, of course, the source code for the interpreter. That said, here's a few quick tips:
To define a new operator
name
that runs the Ruby codecode
, use:Inside the code, use
gpop
to read a value off the stack, andgpush
to push one back in. You can also access the stack directly via the array$stack
. For example, to push botha
andb
onto the stack, it's golfier to do$stack<<a<<b
thangpush a;gpush b
.[
array start markers are stored in the$lb
array. Thegpop
function takes care of adjusting these markers down if the stack shrinks below their position, but manipulating the$stack
array directly does not.The
.cc
string method that compiles Ruby code in a string into a GolfScript operator is just a convenience wrapper aroundGblock.new()
. It also has the variants.cc1
,.cc2
and.cc3
that make the operator automatically pop 1, 2 or 3 arguments off the stack and assign them to the variablesa
,b
andc
. There's also an.order
method that works like.cc2
, except that it automatically sorts the arguments by type priority.All values on the GolfScript stack are (and should be!) objects of type
Gint
,Garray
,Gstring
orGblock
. The underlying native integer or array, where needed, can be accessed via the.val
method.Gstring.val
returns an array ofGint
s! To turn aGstring
into a native Ruby string, call.to_s
on it instead (or use it in a context that does that automatically, like string interpolation). Calling.to_gs
on any GS value turns it into aGstring
, so any GS value can be stringified with.to_gs.to_s
.The
gpush
function doesn't auto-wrap native Ruby numbers, strings or arrays into the corresponding GS types, so you'll often have to do it yourself by explicitly calling e.g.Gstring.new()
. If you push anything other than one of the GS value types onto the stack, any code that later tries to manipulate it is likely to crash.The GS value types also have a
.factory
method that calls the type's constructor, which can be useful e.g. for rewrapping arrays/strings after manipulating their contents. All the types also have a.coerce
method that performs type coercion:a.coerce(b)
returns a pair containinga
andb
coerced to the same type.la source