Conseils pour jouer au golf dans GolfScript


35

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 dans FROM(ou ne dérange pas qu'elles soient toutes mappées au dernier élément de TO), 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)?


Une autre question: est - il un bon moyen de transformer ... xen ... [x]? Le mieux que je puisse voir est [.;].
Peter Taylor

@Peter: Si xest un nombre, alors []+fonctionne et est un caractère plus court. Et bien sûr, si xest la seule chose sur la pile, alors tout ]ira bien.
Ilmari Karonen

Je voudrais demander les meilleures façons de faire: min, max et valeur absolue. Toutes mes solutions semblent prendre beaucoup plus de caractères qu’elles ne le devraient.
Claudiu

Quel est le meilleur moyen de modifier un tableau à un index donné?
user1502040

@ user1502040: Répondu ci-dessous. (Si quelqu'un connaît un meilleur moyen, partagez-le!)
Ilmari Karonen Le

Réponses:


29

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.

2-1? # Raise 2 to the power of -1. Result: 0.5
4-1? # Raise 4 to the power of -1. Result: 0.25
+    # Add. Result: 0.75

La sortie est

3/4

avec l’interprète GolfScript standard et

0.75

sur Web GolfScript .

Des hacks similaires permettent de lancer vers Rational, Float ou même Complex:

{-2.?./*}:rational
{2.-1??./*}:float
{-2.-1??./*}:complex

9
OMGWTFHAX o_O !!!
Ilmari Karonen

3
WAT! Je suis sûr que c’est un bug de l’interprète, mais wow
Bouton de porte

3
Ligne 82 du plus récent interprète: Gint.new(@val**b.val). Il semble que le Gintconstructeur manque un casting int ...
dimanche

10

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*ou 0\-, 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.


9

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:

{;9rand}$

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 randdoit être nettement supérieur au carré de la longueur de la liste en cours de brassage.

Remplacer ce qui 9précède 99donne 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):

{;9.?rand}$

Pour les listes vraiment longues, ajoutez encore un 9 pour 99 99 ≈ 3,7 × 10 197 valeurs:

{;99.?rand}$

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, avec 0plus de trois fois plus de chances de finir en première position que 1:

    0 16537 #######################################################
    1 5444  ##################
    2 7510  #########################
    3 8840  #############################
    4 9124  ##############################
    5 12875 ##########################################
    6 9534  ###############################
    7 8203  ###########################
    8 7300  ########################
    9 14633 ################################################
    
  • Avec 10,{;99rand}$0=, la plupart des biais est parti, mais un montant notable reste toujours:

    0 10441 ##################################
    1 9670  ################################
    2 9773  ################################
    3 9873  ################################
    4 10134 #################################
    5 10352 ##################################
    6 10076 #################################
    7 9757  ################################
    8 9653  ################################
    9 10271 ##################################
    
  • Avec 10,{;9.?rand}$0=, la sortie est fondamentalement indiscernable d’un échantillon vraiment aléatoire:

    0 9907  #################################
    1 9962  #################################
    2 10141 #################################
    3 10192 #################################
    4 9965  #################################
    5 9971  #################################
    6 9957  #################################
    7 9984  #################################
    8 9927  #################################
    9 9994  #################################
    

Ps. Pour un très mauvais brassage de tableaux ou de chaînes numériques, le code suivant peut parfois être acceptable:

{rand}$

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.


3
Je me souviens que j’avais fait les calculs avec scepticisme pour le paradoxe de l’anniversaire après que mon frère me l’ait dit, il avait raison :(
ajax333221

8

Pour répondre à une sous-question spécifique:

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 fait pour les tableaux)?

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

'ABC123'{)}%

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')


Quel est le meilleur moyen de transformer un seul code ASCII en une chaîne? []+''+? (semble assez long)
Justin

@ Quincunx, c'est plutôt long, mais je ne connais pas de meilleur moyen. La chose à faire pourrait être de regarder d'où vient le code ASCII et de voir si vous pouvez le faire arriver déjà dans un tableau.
Peter Taylor

6

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é ifet il s’avère que la mise en œuvre d’ ifappels permet !de décider quelle branche suivre.


6

Envelopper le premier élément de la pile dans un tableau

Y at - il une belle façon de transformer ... xen ... [x]?

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 qui xest la seule chose sur la pile.

3 caractères

[]+fonctionne dans le cas particulier qui xest un entier.

.,/fonctionne dans le cas particulier qui xest 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.


6

Quel est le meilleur moyen de modifier un tableau à un index donné? - utilisateur1502040

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à index idans un tableau consiste à scinder le tableau à l'index donné et à l'ajouter xà la première moitié avant de les réunir à nouveau:

  • .i<[x]+\i>+(11 caractères) - insère la valeur xdans le tableau à l'index (basé sur 0)i

Pour remplacer la valeur à index ipar x, 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) ipar 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) ipar la valeurx

Dans tous les exemples ci-dessus, si xest 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 nombre xdans le tableau à l'index (basé sur 0)i
  • .i<x+\i)>+(10 caractères) - remplace l'élément à l'index (basé sur 0) ipar le nombrex
  • .i(<x+\i>+(10 caractères) - remplace l'élément à l'index (basé sur 1) ipar le nombrex

Les crochets peuvent également être omis si l'un xou 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 iet 2 × iéléments, nous pouvons insérer un nouvel élément xà l'index ( basé sur 0) iavec i/[x]*(6 caractères). Ce que cela fait réellement, c'est diviser le tableau en morceaux de jusqu'à iéléments et insérer xentre chaque morceau. Notez que, dans ce cas, les crochets sont nécessaires même s’il xs’agit d’un nombre.


Pps. Une autre approche consiste à utiliser des variables nommées dynamiquement. Par exemple,

 'foo' 42 ':x'\+~

assignera la valeur 'foo'à la variable x42, tandis que

 42 'x'\+~

va le récupérer.

Vous pouvez optimiser cela davantage en omettant le xpré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.


3
Complètement hors sujet: félicitations pour 10k! :-D
Poignée de porte

1
Si vous savez que le tableau n'a pas de valeurs en double, vous pouvez remplacer une valeur d'index ipour 9 octets:.[i=]/[x]*
Martin Ender

5

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:

]puts

Ce que la documentation ne mentionne pas directement, c’est que l’interprète appelle littéralement l’intégré putspour produire cette sortie, et que cela est intégré. littéralement définie comme suit:

{print n print}:puts;

Ainsi, vous pouvez supprimer ou manipuler la sortie finale en redéfinissant puts , print et / ou n(ou  si vous vous sentez vraiment tordu). Voici quelques exemples:

Supprimer la nouvelle ligne finale:

'':n;

(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:

:puts

Cela écrase putsavec 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, à la 0:puts;place. Notez que ceci supprime également p(ce qui est défini comme {`puts}:p;), mais vous pouvez toujours utiliser printpour la sortie si vous le souhaitez.


Et par nothingvous voulez dire \n?
CalculatriceFeline

Si vous ne vous souciez pas de la fin de ligne, vous pouvez également utiliser ];pour supprimer la sortie finale.
wastl

5

Je voudrais demander les meilleures façons de faire: min, max et valeur absolue. Toutes mes solutions semblent prendre beaucoup plus de caractères qu’elles ne le devraient. - Claudiu

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 tableau

Si 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 -1par 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 pile

La 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 pile

Voici 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'origine

La façon dont cela fonctionne est que .2$clone les deux premiers éléments de la pile dans l'ordre inverse (c.-à-d. a ba 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 estabs (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 tableau

De 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).


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.
KirarinSnow

@KirarinSnow: Merci! Je l'ai ajouté à la réponse.
Ilmari Karonen

4

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.


4

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.


Using the last approach, for the input string 'ABCDEF' I get the result 'abc000', but the right result would be 'abcDEF'. Am I missing something?
Cristian Lupascu

1
@w0lf, that 0 is in bold because it's the escape character previously mentioned - i.e. the byte 0.
Peter Taylor

4

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.


1
Can you give an example of how this might be handy? Strings already act like arrays so this doesn't seem that useful.
Justin

@Quincunx it's handy when you want to pop characters out and not their ascii value
user3700847

And when would you want to do that?
Justin

5
@Quincunx: Rotating a string, for example. "abc"1/(+ -> "bca", but "abc"(+ -> bc97.
Dennis

4

Assigning to number literals

Often, instead of writing 1:x and then using/updating the variable x, you can just use and update 1 directly:

1:^;{^.p.+:^;}5*
{1.p.+:1;}5*       (4 bytes shorter)

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 of x n to push your variable and then push a newline.


3
Some assignments can have unexpected side effects, though. In particular, assigning to ! is often a bad idea, as it will break if and do (as well as while, until, and, or and xor). Similarly, or is defined by the interpreter as an alias for 1$\if, so redefining 1, $ or \ will also break it. Redefining ` breaks p.
Ilmari Karonen

3

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 like grep 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 array b from array a
  • a. b --: remove any elements not found in array b from array a

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 element c occurs in the array a

In general, this method is not optimal, since either of:

  • a[c]/,(: count the number of times the element c occurs in the array a
  • a{c=},,: count the number of times the element c occurs in the array a

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 where c is a number and a is a normal array (not a string), the square brackets around c may be omitted because the - operator coerces its arguments to the same type:

  • a.c--,: count the number of times the number c occurs in the array (not string!) a

(If a is a string and c is a number between 0 and 9, a.c-- will count the number of times the digit c occurs in a.)


A similar trick can by used to find the most common element in an array:

:a{a\[.]-,}$0=

Again, if the input is an array of numbers, the whole [.] sequence may be omitted. Alas, this does not work for strings without the [.].


For counting occurrences (general case), a[c]/,( and a{c=},, are one byte shorter.
Dennis

@Dennis: Thanks! I've edited that in.
Ilmari Karonen

3

Read from STDIN

GolfScript can read from stdin:

"#{STDIN.read}"

This will continue reading from STDIN until the EOF is reached. Alternatively:

"#{STDIN.gets}"

or

"#{STDIN.readline}"

Other things available:

getbyte
getc
gets([sep])
gets(limit)
gets(sep, limit)
inspect # perhaps useful for an underhanded contest
isatty
read([length])
readbyte
readchar
readline([sep])
readline(limit)
readline(sep, limit)
readlines([sep])
readlines(limit)
readlines(sep, limit)
readpartial(maxlen [, outbuf])

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.


2
You might want to add a remark that {"#{STDIN.readline}"p}2* does not read 2 lines but instead the string is evaluated only once.
Howard

2
If you initialize 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 of STDIN.gets.
Dennis

@Dennis: "#{var'g','gpush Gstring.new(STDIN.gets)'.cc}"; would also let you define a new GolfScript operator g that reads a line from stdin and pushes it on the stack.
Ilmari Karonen

2

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:

{39%9-}%

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:

{32|39%9-}%

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 with 2/{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:

{39%9-}%2/{16base}%

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+.


2

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:

"#{var'add','gpush a+b'.cc2}";

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:

1 2 add          # evaluates to 3
"foo" "bar" add  # evaluates to "foobar"

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:

"#{var'shuf','gpush a.factory(a.val.shuffle)'.cc1}";

10,shuf          # evaluates to 0,1,2,...,9 in random order

or printing the contents of the whole stack:

"#{var'debug','puts Garray.new($stack).ginspect'.cc}";

4,) ["foo" debug  # prints ["" [0 1 2] 3 "foo"], leaving the stack untouched

or interactive input:

"#{var'gets','gpush Gstring.new(STDIN.gets)'.cc}";

]; { "> " print gets ~ ]p 1 } do   # simple GolfScript REPL

or even web access:

"#{
  require 'net/http'
  require 'uri'
  var'get','gpush Gstring.new(Net::HTTP.get_response(URI.parse(a.to_s)).body)'.cc1
}";

"http://example.com" get

Of course, a somewhat golfier (and riskier!) implementation of the latter would be e.g.:

"#{var'get','gpush Gstring.new(`curl -s #{a}`)'.cc1}";

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 code code, use:

    var'name','code'.cc
  • Inside the code, use gpop to read a value off the stack, and gpush to push one back in. You can also access the stack directly via the array $stack. For example, to push both a and b onto the stack, it's golfier to do $stack<<a<<b than gpush a;gpush b.

    • The positions of the [ array start markers are stored in the $lb array. The gpop 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 around Gblock.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 variables a, b and c. 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 or Gblock. The underlying native integer or array, where needed, can be accessed via the .val method.

    • However, note that Gstring.val returns an array of Gints! To turn a Gstring 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 a Gstring, 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 containing a and b coerced to the same type.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.