Conseils pour jouer au golf à Julia


20

Quels conseils généraux avez-vous pour jouer au golf à Julia? Je suis à la recherche d'idées pouvant être appliquées aux problèmes de golf de code en général qui sont au moins quelque peu spécifiques à Julia (par exemple, "supprimer les commentaires" n'est pas une réponse).

Réponses:


19

REMARQUE: ce qui suit peut contenir des conseils obsolètes, car Julia n'est pas encore assez stabilisée en termes de structure.

Quelques astuces pour sauver quelques personnages

  1. Opérateurs de surcharge avec des fonctions binaires fréquemment utilisées . Par exemple, si vous avez besoin de faire beaucoup de divisions entières, et pas besoin de division inverse, utilisez \ =div, et vous pouvez alors taper à la a\bplace de div(a,b). Notez l'espace - ceci est nécessaire pour éviter qu'il ne soit analysé comme un opérateur "\ =". Notez également que, s'il est surchargé au niveau de l'invite REPL, utilisez (\)=Base.(\)ou \ =Base. \pour le réinitialiser. REMARQUE: certaines fonctions ont des opérateurs UTF-8 existants prédéfinis, comme ÷pour div, comme l'a noté Alex A.
  2. Utilisez ^ avec des chaînes pour la sortie conditionnelle . C'est-à-dire, plutôt que a>0?"Hi":"", utiliser "Hi"^(a>0)pour enregistrer un octet, ou pour booléen a, utiliser "Hi"^apour enregistrer trois octets.
  3. (parfois) Stockez de petits vecteurs de taille fixe en tant que variables distinctes . Par exemple, plutôt que a=split("Hi there"," "), vous pouvez éviter a[1]et a[2]utiliser a,b=split("Hi there"," "), qui peut être référencé comme aet b, en économisant trois octets pour chaque utilisation, au prix de seulement deux caractères supplémentaires lors de l'affectation. Évidemment, ne faites pas cela si vous pouvez travailler avec des opérations vectorielles.
  4. Accédez au premier élément du tableau avec[] - pour les tableaux, l'expression A[]est équivalente à A[1]. Notez que cela ne fonctionne pas pour les chaînes si vous souhaitez obtenir le premier caractère ou pour les tuples.
  5. N'utilisez pas isempty pour les tableaux, les tuples ou les chaînes - utilisez plutôt les ==[]tableaux et les ==()tuples; de même, pour le négatif, utilisez !=[]et !=(). Pour les chaînes, utilisez ==""pour vide, mais utilisez >""pour non-vide, car "" est lexicographiquement avant toute autre chaîne.
  6. Utilisez l'opérateur booléen de court-circuit à la place de "si" . C'est peut-être un peu moins spécifique à Julia, mais cela vaut la peine d'être gardé à l'esprit. x<=1&&"Hi"peut s'écrire en x>1||"Hi"sauvegardant un caractère (tant que le retour du booléen n'est pas important).
  7. N'utilisez pas contient pour vérifier la présence de caractère dans la chaîne - si vous êtes limité à l'ASCII de base, utilisez in('^',s)plutôt que contains(s,"^"). Si vous pouvez utiliser d'autres caractères, vous pouvez économiser un peu plus avec '^'∈s, mais notez que c'est 3 octets en UTF-8.
  8. Vous recherchez des valeurs minimum / maximum dans un tableau? N'utilisez pas de minimum ou de maximum - plutôt que d'utiliser minimum(x)ou maximum(x), utilisez min(x...)ou max(x...), pour raser un caractère de votre code, si vous savez xqu'il aura au moins deux éléments. Alternativement, si vous savez que tous les éléments de xseront non négatifs, utilisez minabs(x)oumaxabs(x)
  9. Lorsque cela est possible et autorisé par le défi, utilisez un saut de ligne réel au lieu de \ n - notez que cela rendra votre code plus difficile à lire, et peut signifier que vous devez fournir une version "non golfée" pour permettre aux gens de réellement comprendre il.
  10. Mettez des options après la chaîne d'expression régulière - si vous voulez avoir une chaîne d'expression régulière en mode multiligne, par exemple, ne tapez pas r"(?m)match^ this", tapez r"match^ this"m, en enregistrant trois caractères.
  11. Inverser les tableaux 1-D en utilisant flipud - reverse(x)est un octet de plus que flipud(x)et effectuera la même opération, donc ce dernier est meilleur.
  12. Dans la mesure du possible, utilisez la concaténation de tableau au lieu de pousser !, unshift !, append !, ou préfixez! - pour les tableaux normaux, cela peut être fait facilement. Pour les tableaux de type Any avec des éléments de tableau, vous aurez besoin de crochets autour des tableaux ajoutés (c'est-à-dire {[1,2]}, non {1,2}) - pour Julia 0.4, vous en auriez besoin Any[[1,2]].
  13. Utilisez l'indexation de tableau pour obtenir la taille d'un tableau ou d'une chaîne - lorsque vous utilisez l' endindexation de tableau, il est automatiquement converti en la longueur du tableau / de la chaîne. Donc, plutôt que k=length(A), utilisez A[k=end]pour enregistrer 3 caractères. Notez que cela peut ne pas être bénéfique si vous souhaitez utiliser k immédiatement. Cela fonctionne également dans un cas multidimensionnel - A[k=end,l=end]obtiendra la taille de chaque dimension de A- cependant, (k,l)=size(A)est plus court d'un octet dans ce cas, donc utilisez-le uniquement si vous souhaitez accéder immédiatement au dernier élément en même temps.
  14. Obtenir un itérateur d'index à l'aide de l'indexation de tableau - Similaire à 13, vous pouvez également obtenir un itérateur correspondant à la longueur d'un tableau à l'aide A[k=1:end], auquel cas kcontiendra une correspondance d'itérateur 1:length(A). Cela peut être utile lorsque vous souhaitez également utiliser un tableau Aen même temps.
  15. N'utilisez pas collect pour convertir une chaîne en un tableau de caractères - au lieu de collect(A), utilisez [A...], ce qui fera la même chose et économisera 4 octets.
  16. Besoin d'un nombre converti en chaîne? Utilisez "$(s[i])"ou dec(s[i])pour des expressions ou des variables à plusieurs caractères et "$i"pour des variables à un seul caractère.
  17. Utiliser à la ?:place de &&ou ||pour une affectation conditionnelle - c'est-à-dire que si vous souhaitez effectuer une affectation uniquement à certaines conditions, vous pouvez enregistrer un octet en écrivant cond?A=B:1plutôt que cond&&(A=B), ou cond?1:A=Bplutôt que cond||(A=B). Notez que le 1, ici, est une valeur fictive.
  18. Utiliser unionou à la place deunique - union(s)fera la même chose que unique(s)et enregistrera un octet dans le processus. Si vous pouvez utiliser des caractères non ASCII, ∪(s)cela fera la même chose et ne coûtera que 3 octets au lieu des 5 octets union.

2
Oh comme j'aimerais ce premier tour en Python.
seequ

Vous pouvez diviser les espaces en utilisant simplement split("Hi there")car l'argument de modèle par défaut est un espace.
Alex A.

@AlexA. - Je sais, mais ce n'est pas le point de la pointe, et la pointe s'applique aussi bien de toute façon.
Glen O

Le point 12 a changé en 0,5.
Lyndon White

@Oxinabox - Je ne suis pas surpris, je suis presque sûr que certains d'entre eux sont désormais dépassés. J'ai écrit à l'origine la plupart des conseils pour 0.3, je pense.
Glen O

15

Redéfinir les opérateurs pour définir les fonctions

La redéfinition des opérateurs peut économiser beaucoup d'octets entre parenthèses et virgules.

Opérateurs unaires récursifs

Pour un exemple unaire, comparez les implémentations récursives suivantes de la séquence de Fibonacci:

F(n)=n>1?F(n-1)+F(n-2):n # 24 bytes
!n=n>1?!~-n+!(n-2):n     # 20 bytes
!n=n>1?!~-n+!~-~-n:n     # 20 bytes

Essayez-le en ligne!

L'opérateur redéfini conserve sa priorité initiale.

Notez que nous ne pouvons pas simplement échanger !en faveur de ~, car ~est déjà défini pour les entiers, tandis que !n'est défini que pour les booléens.

Opérateurs binaires

Même sans récursivité, la redéfinition d'un opérateur est plus courte que la définition d'une fonction binaire. Comparez les définitions suivantes d'un test de divisibilité simple.

f(x,y)=x==0?y==0:y%x==0 # 23 bytes
(x,y)->x==0?y==0:y%x==0 # 23 bytes
x->y->x==0?y==0:y%x==0  # 22 bytes
x\y=x==0?y==0:y%x==0    # 20 bytes

Essayez-le en ligne!

Opérateurs binaires récursifs

Ce qui suit illustre comment redéfinir un opérateur binaire pour calculer la fonction Ackermann:

A(m,n)=m>0?A(m-1,n<1||A(m,n-1)):n+1    # 35 bytes
^ =(m,n)->m>0?(m-1)^(n<1||m^~-n):n+1   # 36 bytes
| =(m,n)->m>0?m-1|(n<1||m|~-n):n+1     # 34 bytes
m\n=m>0?~-m\(n<1||m\~-n):n+1           # 28 bytes

Essayez-le en ligne!

Notez que ^c'est encore plus long que d'utiliser un identifiant régulier, car sa priorité est trop élevée.

Comme mentionné précédemment

m|n=m>0?m-1|(n<1||m|~-n):n+1           # 28 bytes

ne fonctionnerait pas pour les arguments entiers, car il |est déjà défini dans ce cas. La définition des entiers peut être modifiée avec

m::Int|n::Int=m>0?m-1|(n<1||m|~-n):n+1 # 38 bytes

mais c'est trop long. Cependant, il fait le travail si on passe un flotteur comme argument gauche et un entier comme argument droit.


11
  1. Ne soyez pas trop facilement séduit par le facteur (n) La fonction de bibliothèque de base tentante factor(n)a un défaut fatal: elle renvoie la factorisation de votre entier dans un Dicttype non ordonné . Ainsi, il faut de l'argent collect(keys())et collect(values())potentiellement aussi un catet un sortpour obtenir les données que vous vouliez en tirer. Dans de nombreux cas, il peut être moins coûteux de simplement prendre en compte la division de première instance. Triste mais vrai.

  2. Utiliser la carte map est une excellente alternative au bouclage. Soyez conscient de la différence entre mapet map!exploitez la fonctionnalité en place de ce dernier lorsque vous le pouvez.

  3. Utiliser mapreduce mapreduce étend encore plus la fonctionnalité de la carte et peut être un économiseur d'octets important.

  4. Les fonctions anonymes sont super! .. surtout lorsqu'il est passé aux mapfonctions susmentionnées .

  5. Fonctions de réseau cumulatif cumprod , cumsumle savoureux cumminet les autres fonctions de même nom permettent les opérations cumulées le long d' une dimension spécifiée d'un tableau à n dimensions. (Ou * un * spécifié si le tableau est 1-d)

  6. Notation courte pour Any Lorsque vous souhaitez sélectionner toutes les dimensions particulières d'un tableau multidimensionnel (ou Dict), par exemple A[Any,2], vous pouvez enregistrer des octets en utilisantA[:,2]

  7. Utilisez la notation sur une seule ligne pour les fonctions Au lieu de cela, function f(x) begin ... endvous pouvez souvent simplifierf(x)=(...)

  8. Utilisez l'opérateur ternaire Il peut être un économiseur d'espace pour les constructions If-Then-Else à expression unique. Avertissements: Bien que cela soit possible dans certaines autres langues, vous ne pouvez pas omettre la partie après les deux points dans Julia. En outre, l'opérateur est au niveau de l'expression dans Julia, vous ne pouvez donc pas l'utiliser pour l'exécution conditionnelle de blocs entiers de code.
    if x<10 then true else false endcontre
    x<10?true:false


3
Comment diable «utiliser l'opérateur ternaire» est-il quelque peu spécifique à Julia? C'est pertinent pour toutes les langues qui l'ont.
Peter Taylor

5
Il est pertinent qu'il l'ait. De nombreuses langues ont également des fonctions de carte, anonymes ou pures, une sorte de raccourci pour tout / tout, des fonctions cumulatives, etc. Si nous devions réduire chaque fil de conseils uniquement aux fonctionnalités absolument uniques à cette langue, il y aurait très peu de contenu de conseils .
Jonathan Van Matre

3
Mon Dieu, seulement tous les fonctionnels pour les débutants, où tout retourne une valeur donc un op ternaire serait redondant. Si vous devez avoir des exemples spécifiques: Go, Haskell, Scala, Lua, VB, Prolog, PL / SQL ... même Python ne l'a pas fait pour de nombreuses versions. Parmi la douzaine de langues dont les fils de conseils mentionnent leur opérateur ternaire, y a-t-il une raison pour laquelle vous avez seulement choisi de devenir provincial dans la mienne?
Jonathan Van Matre

3
Eh bien, au moins, vous êtes un curmudgeon de l'égalité des chances. ヘ ( ̄ ー  ̄ ヘ)
Jonathan Van Matre

1
Puis-je suggérer d'ajuster la pointe 3? mapreduce est plus long que mapfoldl ou mapfoldr, et peut avoir un comportement variable selon l'implémentation. mapfoldl et mapfoldr sont respectivement associatifs à gauche et à droite, et sont donc un meilleur choix. Cela vaut également plus généralement pour réduire (utiliser foldl ou foldr).
Glen O

9

Itérer sur les fonctions

Ceci est également possible dans d'autres langues, mais généralement plus long que la méthode simple. Cependant, la capacité de Julia à redéfinir ses opérateurs unaires et binaires le rend assez golfique.

Par exemple, pour générer la table d'addition, de soustraction, de multiplication et de division pour les nombres naturels de 1 à 10, on pourrait utiliser

[x|y for x=1:10,y=1:10,| =(+,-,*,÷)]

qui redéfinit le binaire opérateur |comme +, -, *et ÷, calcule ensuite x|ypour chaque opération et xet ydans les plages souhaitées.

Cela fonctionne également pour les opérateurs unaires. Par exemple, pour calculer des nombres complexes 1 + 2i , 3-4i , -5 + 6i et -7-8i , leurs négatifs, leurs conjugués complexes et leurs inverses multiplicatifs, on pourrait utiliser

[~x for~=(+,-,conj,inv),x=(1+2im,3-4im,-5+6im,-7-8im)]

qui redéfinit le unaire opérateur ~comme +, -, conjet inv, calcule ensuite ~xpour tous les nombres complexes souhaités.

Exemples dans des concours réels


6
  1. Les mots-clés peuvent parfois suivre immédiatement des constantes sans avoir besoin d'espace ou de point-virgule. Par exemple:

    n->(for i=1:n n-=1end;n)

    Notez le manque d'espace entre 1et end. Cela est également vrai pour endse produire après une parenthèse étroite, à savoir )end.

  2. Effectuer une division entière en utilisant ÷plutôt qu'en div()surchargeant un opérateur. Notez que cela ÷vaut 2 octets en UTF-8.

  3. Utilisez vec()ou A[:](pour certains tableaux A) plutôt que dans la reshape()mesure du possible.

  4. Créez des fonctions plutôt que des programmes complets lorsque cela est autorisé dans les règles du défi. Il est plus court de définir une fonction qui accepte une entrée plutôt que de définir des variables en lisant depuis stdin. Par exemple:

    n->(n^2-1)
    n=read(STDIN,Int);n^2-1
  5. Les variables peuvent être incrémentées à l'intérieur de l'argument d'une fonction. Par exemple, voici ma réponse au défi Trouver le prochain numéro binaire 1-éparse :

    n->(while contains(bin(n+=1),"11")end;n)

    Ceci est plus court que l'incrémentation à l' nintérieur de la boucle.


6
  1. Ne pas taperreturn f(x)=x+4 est identique à mais plus court que f(x)=return x+4. Julia renvoie toujours le résultat de la dernière instruction.
  2. Utilisez = au lieu de in . [x for x in 1:4]est plus long que 3 caractères, mais équivalent à[x for x=1:4]

5

Utilisez les appels de fonction de diffusion.

Introduit dans Julia 0.5. C'est comme la carte, mais utilise moins de caractères et fait un comportement de diffusion sur ses arguments - ce qui signifie que vous pouvez écrire moins de lambda pour gérer les choses.

Plutôt que:

  • map(f,x) - 8 caractères.
  • f.(x) - 5 caractères

Mieux encore:

  • map(a->g(a,y),x) - 16 caractères
  • g.(x,[y]) - 9 caractères
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.