Golfscript - 12 caractères
{,1\{)*}/}:f
Démarrer avec Golfscript - Factorial étape par étape
Voici quelque chose pour les personnes qui essaient d'apprendre golfscript. La condition préalable est une compréhension de base de golfscript et la capacité de lire la documentation de ce dernier.
Nous voulons donc essayer notre nouvel outil golfscript . Il est toujours bon de commencer par quelque chose de simple, alors nous commençons par factorielle. Voici une première tentative, basée sur un simple pseudocode impératif:
# pseudocode: f(n){c=1;while(n>1){c*=n;n--};return c}
{:n;1:c;{n 1>}{n c*:c;n 1-:n;}while c}:f
Les blancs sont très rarement utilisés dans golfscript. L'astuce la plus simple pour se débarrasser des espaces est d'utiliser différents noms de variables. Chaque jeton peut être utilisé comme une variable (voir la page de syntaxe ). Jetons utiles à utiliser en tant que variables sont des caractères spéciaux comme |
, &
, ?
- en général rien non utilisé ailleurs dans le code. Ceux-ci sont toujours analysés comme des jetons à caractère unique. En revanche, des variables comme n
nécessiteront un espace pour insérer un nombre dans la pile après. Les nombres sont essentiellement des variables pré-initialisées.
Comme toujours, il y aura des déclarations que nous pourrons changer, sans affecter le résultat final. En golfscript, tout évalue à vrai sauf 0
, []
, ""
et {}
(voir ce ). Ici, nous pouvons changer simplement la condition de sortie de la boucle {n}
(on boucle une fois supplémentaire et on termine quand n = 0).
Comme pour le golf, peu importe la langue, il est utile de connaître les fonctions disponibles. Heureusement, la liste est très courte pour golfscript. Nous pouvons changer 1-
pour (
sauver un autre personnage. Actuellement, le code ressemble à ceci: (nous pourrions utiliser la 1
place d’ |
ici si nous le voulions, ce qui laisserait tomber l’initialisation).
{:n;1:|;{n}{n|*:|;n(:n;}while|}:f
Il est important de bien utiliser la pile pour obtenir les solutions les plus courtes (pratique, pratique, pratique). En règle générale, si les valeurs ne sont utilisées que dans un petit segment de code, il peut ne pas être nécessaire de les stocker dans des variables. En supprimant la variable de produit en cours et en utilisant simplement la pile, nous pouvons économiser beaucoup de caractères.
{:n;1{n}{n*n(:n;}while}:f
Voici quelque chose d'autre à penser. Nous retirons la variable n
de la pile à la fin du corps de la boucle, mais nous la repoussons immédiatement après. En fait, avant que la boucle ne commence, nous la retirons également de la pile. Nous devrions plutôt le laisser sur la pile et garder la condition de boucle vide.
{1\:n{}{n*n(:n}while}:f
Peut-être pouvons-nous même éliminer complètement la variable. Pour ce faire, nous devrons garder la variable sur la pile à tout moment. Cela signifie que nous avons besoin de deux copies de la variable sur la pile à la fin du contrôle de condition pour ne pas le perdre après le contrôle. Ce qui signifie que nous aurons un redondant 0
sur la pile à la fin de la boucle, mais cela est facile à corriger.
Cela nous conduit à notre while
solution de boucle optimale !
{1\{.}{.@*\(}while;}:f
Maintenant, nous voulons toujours rendre cela plus court. La cible évidente devrait être le mot while
. En regardant la documentation, il y a deux alternatives viables: déplier et faire . Lorsque vous avez le choix entre différents itinéraires, essayez de peser les avantages des deux. Unfold est "une boucle de while", donc, en tant qu'évaluation, nous allons réduire le caractère à 5 while
par 4 /
. En ce qui concerne do
, nous coupons while
par 3 caractères et arrivons à fusionner les deux blocs, ce qui pourrait sauver un ou deux autres personnages.
Il y a en fait un gros inconvénient à utiliser une do
boucle. Puisque la vérification de la condition est effectuée après que le corps ait été exécuté une fois, la valeur de 0
sera fausse, nous aurons peut-être besoin d'une instruction if. Je vais vous dire maintenant que le déroulement est plus court (quelques solutions do
sont fournies à la fin). Allez-y et essayez, le code que nous avons déjà nécessite des modifications minimes.
{1\{}{.@*\(}/;}:f
Génial! Notre solution est maintenant très courte et nous avons terminé ici, non? Nan. Ceci est 17 caractères, et J a 12 caractères. Ne jamais admettre la défaite!
Maintenant, vous pensez avec ... la récursivité
Utiliser la récursivité signifie que nous devons utiliser une structure de branchement. Malheureusement, mais comme factoriel peut être exprimé de manière si succincte et récurrente, cela semble être une alternative viable à l'itération.
# pseudocode: f(n){return n==0?n*f(n-1):1}
{:n{n.(f*}1if}:f # taking advantage of the tokeniser
Eh bien, c'était facile - si nous avions essayé la récursivité plus tôt, nous n'aurions peut-être même pas envisagé d'utiliser une while
boucle! Pourtant, nous sommes seulement à 16 caractères.
Tableaux
Les tableaux sont généralement créées de deux manières - en utilisant les [
et ]
caractères, ou avec la ,
fonction. S'il est exécuté avec un entier en haut de la pile, ,
retourne un tableau de cette longueur avec arr [i] = i.
Pour itérer sur des tableaux, nous avons trois options:
{block}/
: pousser, bloquer, pousser, bloquer, ...
{block}%
: [pousser, bloquer, pousser, bloquer, ...] (cela a quelques nuances, par exemple les valeurs intermédiaires sont retirées de la pile avant chaque poussée)
{block}*
: pousser, pousser, bloquer, pousser, bloquer, ...
La documentation de golfscript contient un exemple d'utilisation {+}*
de la somme du contenu d'un tableau. Cela suggère que nous pouvons utiliser {*}*
pour obtenir le produit d'un tableau.
{,{*}*}:f
Malheureusement, ce n'est pas si simple. Tous les éléments sont désactivés par un ( [0 1 2]
au lieu de [1 2 3]
). Nous pouvons utiliser {)}%
pour rectifier ce problème.
{,{)}%{*}*}:f
Eh bien pas tout à fait. Cela ne gère pas le zéro correctement. Nous pouvons calculer (n + 1)! / (N + 1) pour remédier à cela, bien que cela coûte beaucoup trop cher.
{).,{)}%{*}*\/}:f
Nous pouvons également essayer de traiter n = 0 dans le même compartiment que n = 1. C’est vraiment très court, essayez de travailler le plus rapidement possible.
Pas si bien est le tri, à 7 caractères: [1\]$1=
. Notez que cette technique de tri a des objectifs utiles, tels qu'imposer des limites à un nombre (par exemple `[0 \ 100] $ 1 =).
Voici le gagnant, avec seulement 3 caractères:.! +
Si nous voulons avoir l'incrément et la multiplication dans le même bloc, nous devrions itérer sur chaque élément du tableau. Puisque nous ne construisons pas de tableau, cela signifie que nous devrions utiliser {)*}/
, ce qui nous amène à la plus courte implémentation de factorial de golfscript! À 12 caractères de long, c'est à égalité avec J!
{,1\{)*}/}:f
Solutions bonus
Commençant par une if
solution simple pour une do
boucle:
{.{1\{.@*\(.}do;}{)}if}:f
Nous pouvons en extraire un couple supplémentaire. Un peu compliqué, alors vous devrez vous convaincre que ceux-ci fonctionnent. Assurez-vous de comprendre tout cela.
{1\.!!{{.@*\(.}do}*+}:f
{.!{1\{.@*\(.}do}or+}:f
{.{1\{.@*\(.}do}1if+}:f
Une meilleure alternative consiste à calculer (n + 1)! / (N + 1), ce qui élimine le besoin d'une if
structure.
{).1\{.@*\(.}do;\/}:f
Mais la do
solution la plus courte ici prend quelques caractères pour mapper 0 à 1, et tout le reste à elle-même - nous n’avons donc besoin d’aucune branche. Ce type d’optimisation est extrêmement facile à manquer.
{.!+1\{.@*\(.}do;}:f
Pour ceux qui sont intéressés, quelques solutions alternatives récursives avec la même longueur que ci-dessus sont fournies ici:
{.!{.)f*0}or+}:f
{.{.)f*0}1if+}:f
{.{.(f*}{)}if}:f
* remarque: je n'ai pas encore testé la plupart des éléments de code de ce message, alors n'hésitez pas à nous informer en cas d'erreur.