Conseils pour jouer au golf dans Mathematica


41

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

Réponses:


30

Les astuces ci-dessous varient du plus économique au plus utilisé:

  1. Utilisez les commandes de haut niveau de Mathematica dans la mesure du possible, même les plus volumineuses:

  2. À utiliser Graphics and Textpour l’art Ascii: par exemple, la programmation Star! et construire une horloge analogique

  3. Symboles dédiés:

    • la logique et définir des symboles d'opérations au lieu de leurs noms de forme longs:, ⋃, ∧, ∨

    • Mapet Apply: /@, //@. @@,@@@

  4. Notation de préfixe et infixe:

    • Print@"hello" au lieu de Print["hello"]

    • a~f~b au lieu de f[a,b]

  5. Lorsqu'une fonction est utilisée une seule fois, une fonction pure peut économiser un ou deux caractères.

  6. Joindre des chaînes dans une liste. ""<>{"a","b","c"}au lieu deStringJoin@{"a","b","c"}

  7. Exploiter les fonctions listables. Plus les listes sont longues, mieux c'est.

    {a, b, c} + {x, y, z}= {a+x, b+y, c+z}

    {2, 3, 4} {5, 6, 7}= {10, 18, 28}

    {{a, b}, {c, d}}^{2, 3} = {{a^2, b^2}, {c^3, d^3}}


2
C'est toujours plus court d'écrire (Norm[#-#2]&)au lieu de EuclideanDistance.
user202729

32

Certaines fonctions intégrées avec des noms longs peuvent être remplacées par des expressions plus courtes.

Par exemple:

  • Total => Tr
  • Transpose=> Threadou\[Transpose]
  • True => 1<2
  • False => 1>2
  • Times => 1##&
  • Alternatives => $|##&
  • IntegerQ => ⌊#⌋==#&
  • a[[1]] => #&@@a
  • a[[All,1]] => #&@@@a
  • ConstantArray[a,n]=> Array[a&,n]ouTable[a,{n}]
  • Union@a=> {}⋃aoua⋃a
  • ToExpression@n=> FromDigits@nsi nest un nombre
  • Divisible[n,m] => m∣n
  • FromDigits[n,2]=> Fold[#+##&,n]if nest une liste de 0s et 1s
  • Complex@z=> {1,I}.zzest une liste du formulaire{x,y}

5
@belisarius Thread[{{a,b},{c,d}}]== == Thread[List[{a,b},{c,d}]]== {List[a,c],List[b,d]}== {{a,c},{b,d}}==Transpose[{{a,b},{c,d}}]
alephalpha

2
Je pense que votre Foldastuce FromDigitsfonctionne aussi pour n'importe quelle autre base sauf 10. Par exemple FromDigits[n,5]-> Fold[4#+##&,n](avec le bonus de sauvegarder un octet supplémentaire pour les bases 100et 1000).
Martin Ender

1
@ mbomb007 3 octets en UTF-8. En fait ce personnage est U+F3C7.
Alephalpha

1
J'ai finalement installé 10.3. Si nous envisageons des programmes complets, je ne pense pas que ce Echosoit une option, car cela imprime >>(et un espace) sur STDOUT avant d’imprimer la chaîne réelle.
Martin Ender

2
Car Complex[x,y] => {1,I}.{x,y}, je pense que x+y*Ic'est beaucoup plus court avec le même effet?
Shieru Asakoto

22

Listes avec des valeurs répétées

C'est un vecteur commun avec lequel travailler:

{0,0}

Il s'avère que cela peut être raccourci par un octet:

0{,}

Encore plus d'octets sont enregistrés si le vecteur est plus long que deux zéros. Ceci peut également être utilisé pour initialiser des matrices nulles, par exemple, ce qui suit donne une matrice 2x2 de zéros:

0{{,},{,}}

Ceci peut également être utilisé pour des valeurs non nulles si elles sont suffisamment grandes ou suffisamment nombreuses ou négatives. Comparez les paires suivantes:

{100,100}
0{,}+100
{-1,-1}
0{,}-1
{3,3,3,3}
0{,,,}+3

Mais rappelez-vous qu'à partir de 6 valeurs, il vaut mieux 1~Table~6dans ce cas (potentiellement plus tôt, en fonction des exigences de priorité).

La raison pour laquelle cela fonctionne est que cela ,introduit deux arguments dans la liste, mais les arguments omis (n'importe où dans Mathematica) sont des Nulls implicites . En outre, la multiplication est Listable, et 0*xest 0pour presque tout x(sauf pour des choses comme Infinityet Indeterminate), alors voici ce qui se passe:

  0{,}
= 0*{,}
= 0*{Null,Null}
= {0*Null,0*Null}
= {0,0}

Pour les listes de 1s, vous pouvez utiliser une astuce similaire en utilisant des règles d’exponentiation. Il existe deux manières différentes de sauvegarder des octets si vous avez au moins trois 1secondes dans la liste:

{1,1,1}
1^{,,}
{,,}^0

7
+1; Cela montre bien que si Mathematica peut être intégré à tout, jouer au golf peut être un véritable défi.
LegionMammal978

Si vous voulez un tableau qui est finalement rempli de 1, 1^{,,,}un octet est inférieur à 0{,,,}+1.
Misha Lavrov

@MishaLavrov Oh, bonne prise. Cela le rend plus court à trois valeurs et vous pouvez également utiliser {,,}^0. Je vais éditer le post.
Martin Ender

19

Connaissez vos arguments de fonction purs

Lorsque vous jouez au code de golf, vous utiliserez souvent une approche fonctionnelle, consistant à utiliser des fonctions anonymes (pures) avec une &syntaxe abrégée. Il y a beaucoup de façons d'accéder aux arguments d'une telle fonction, et vous pouvez souvent supprimer quelques octets en ayant une bonne compréhension des possibilités.

Accéder à des arguments simples

Vous le savez probablement si vous avez déjà utilisé des fonctions pures. Le n ième argument est appelé #n, et #agit comme un alias pour #1. Donc, si, par exemple, vous voulez écrire une fonction qui prenne comme paramètres une autre fonction et son argument (pour passer l'argument à cette fonction), utilisez

#@#2&

Cela ne fonctionne pas avec les nombres négatifs (tels que ceux que vous pourriez utiliser pour accéder aux listes).

Accès aux arguments nommés (nouveauté V10)

L’une des nouvelles fonctionnalités majeures de Mathematica 10 est Association s, qui sont essentiellement des mappes clé-valeur avec des types de clé arbitraires, écrits comme suit:

<| x -> 1, "abc" -> 2, 5 -> 3 |>

Si une telle association est passée en tant que premier argument à une fonction pure, vous pouvez accéder à certains si ses arguments en tant que paramètres nommés:

{#, #2, #3, #abc, #xyz} & [<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th"]
(* {<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th", "1st", "2nd"} *)

Notez que # fait toujours référence à toute l'association comme prévu. Pour que les paramètres nommés fonctionnent, les clés doivent être des chaînes (cela ne fonctionnera pas si vous utilisez des variables non définies, par exemple), et ces chaînes doivent commencer par une lettre et ne contenir que des lettres et des chiffres.

Le "soi" argument #0

Une fonctionnalité moins connue est celle qui #0existe également et vous donne l'objet fonction lui-même. Cela peut être vraiment utile dans les quines et les quines généralisées. En fait, la quine la plus courte de Mathematica (à ma connaissance) est

ToString[#0][] & []

Ce qui est légèrement ennuyeux, c'est que cela ne vous donnera pas les caractères exacts que vous avez entrés. Par exemple si utilisation@ pour une application de fonction, il restera rendu en tant que [...]et des espaces seront insérés à certains endroits. La quine sera généralement un peu plus longue que vous ne le voudriez, mais cela fonctionnera toujours, en jouant d'abord sur la quine, puis en copiant simplement sa sortie - qui devrait maintenant être une vraie quine.

Outre quines, cela signifie également que vous pouvez écrire du code récursif sans avoir à nommer votre fonction. Comparez ces trois implémentations de Fibonacci (naïves mais golfées):

f@0=0;f@1=1;f@n_:=f[n-1]+f[n-2]
f@n_:=If[n<2,n,f[n-1]+f[n-2]]
If[#<2,#,#0[#-1]+#0[#-2]]&

Séquences d'arguments

Maintenant, c’est là que commence la vraie magie. Les séquences ne sont pas souvent utilisées dans le golf, parce que Sequencec'est un nom trop long pour qu'il en vaille la peine la plupart du temps. Mais dans les fonctions pures, c'est là où elles brillent. Si vous n'êtes pas familier avec les séquences, elles ressemblent fondamentalement aux splats dans d'autres langues. Si vous utilisez une séquence dans une Listliste d'arguments ou une liste d'arguments d'une fonction, ses éléments seront automatiquement développés dans des emplacements distincts. Alors

{1, Sequence[2, 3, 4], 5} == {1, 2, 3, 4, 5}
f["a", Sequence[0, {}], "b"] == f["a", 0, {}, "b"]

Maintenant, dans les fonctions pures ##ou ##1est une séquence de tous les arguments. De même, ##2est une séquence de tous les arguments commençant à partir du deuxième, ##3tous les arguments commençant à partir du troisième, etc. Ainsi, pour commencer, nous pouvons simplement réimplémenter Sequenceen ##&économisant 5 octets. À titre d'exemple d'utilisation, cela nous fournit une alternative à Join@@list(voir cette astuce ), qui ne sauvegarde aucun octet, mais qui est bon à savoir de toute façon:

 ##&@@@list

Cela aplatit efficacement le premier niveau d'une liste imbriquée. Que pouvons-nous faire avec ça? Voici une alternative plus courte de 2 octets à RotateLeft:

 RotateLeft@list
 {##2,#}&@list

Rien que pour ces choses, il convient de garder cette caractéristique à l’esprit. Cependant, nous pouvons faire mieux! Les séquences deviennent vraiment intéressantes quand on considère que les opérateurs sont réellement implémentés en tant que fonctions sous le capot. Par exemplea+b évalue à Plus[a,b]. Donc, si nous donnons à cela une séquence ...

1+##&[1,2,3]
=> Plus[1,##] 
=> Plus[1,1,2,3]
=> 7

Cette astuce a été utilisée dans cette astuce pour enregistrer un octet surTimes , car la juxtaposition n’est techniquement qu’un opérateur:

1##&[1,2,3]
=> Times[1,##]
=> Times[1,1,2,3]
=> 6

Vous pouvez également l'utiliser pour enregistrer un octet Unequalsi vous avez une valeur à caractère unique ou une variable dont vous savez qu'elle ne figure pas dans vos arguments ( Nfonctionnera probablement dans 99% des cas):

Unequal[a,b,c]
N!=##&[a,b,c]

Cela devient encore plus intéressant avec les opérateurs unaires -et /- les deux derniers sont en réalité mis en œuvre en termes de multiplication et d’exponentiation. Voici une liste de choses que vous pouvez faire, où la dernière colonne suppose que la fonction a reçu les arguments a, b, c:

Operator    Function                Expanded                    Equivalent to

+##         Plus[##]                Plus[a,b,c]                 a+b+c
1##         Times[1,##]             Times[1,a,b,c]              a*b*c
-##         Times[-1,##]            Times[-1,a,b,c]             -a*b*c
x+##        Plus[x,##]              Plus[x,a,b,c]               x+a+b+c
x-##        Plus[x,Times[-1,##]]    Plus[x,Times[-1,a,b,c]]     x-a*b*c
x##         Times[x,##]             Times[x,a,b,c]              x*a*b*c
x/##        Times[x,Power[##,-1]]   Times[x,Power[a,b,c,-1]]    x*a^b^c^-1
##/x        Times[##,Power[x,-1]]   Times[a,b,c,Power[x,-1]]    a*b*c/x
x^##        Power[x,##]             Power[x,a,b,c]              x^a^b^c
##^x        Power[##,x]             Power[a,b,c,#]              a^b^c^x
x.##        Dot[x,##]               Dot[x,a,b,c]                x.a.b.c

D' autres opérateurs sont communs !=, ==, &&, ||. Les moins communes à garder à l' esprit sont |, @*, /*. Pour conclure, voici un petit bonus:

####        Times[##,##]            Times[a,b,c,a,b,c]          (a*b*c)^2

Continuez à expérimenter avec ceux-ci et faites-moi savoir si vous trouvez d'autres applications utiles ou particulièrement intéressantes!


15

Sqrt@2ou 2^.5=>√2

a[[1]]=>a〚1〛

#+#2&=>+##&

Flatten@a=> Join@@a(parfois)

Function[x,x^2]=> xx^2ou#^2&

a〚1;;-1;;2〛=>a〚;;;;2〛

a〚2;;-1 ;;2〛=>a〚2;;;;2〛

a〚All,1〛=>a〚;;,1〛

{{1}}〚1,1〛=>Tr@{{1}}

0&~Array~10=>0Range@10

Range[10^3]=>Range@1*^3


1
Notez que lorsque vous mesurez en octets, vous utilisez et
prenez

12

Opérateurs en tant que fonctions

Inspiré par la récente découverte de Dennis par Julia, je pensais que je devrais examiner cela pour Mathematica. Je savais que Mathematica définissait un grand nombre d'opérateurs inutilisés, mais je n'y avais jamais prêté attention.

Pour référence, la liste de tous les opérateurs peut être trouvée ici sous la forme d'un tableau de priorité. Le triangle dans la dernière colonne indique si cet opérateur a une signification intégrée ou non. Bien que tous ceux qui ne le sont pas ne puissent pas être définis facilement, la plupart d’entre eux le peuvent.

De manière pratique, il existe deux opérateurs inutilisés avec un point de code inférieur à 256, de sorte qu'ils peuvent être utilisés comme octets simples dans un fichier source codé ISO 8859-1:

  • ± (0xB1) peut être utilisé comme opérateur préfixe unaire ou comme opérateur infixe binaire.
  • · (0xB7) peut être utilisé comme opérateur d'infixe variadique ou n-aire, pour n> 2.

Il y a cependant un autre inconvénient: pour une raison étrange lorsque vous définissez ces opérateurs, vous avez besoin d'un espace devant eux, sinon Mathematica essaie d'analyser une multiplication. Lorsque vous les utilisez, vous n'avez pas besoin d'espaces:

±x_:=2x
x_ ±y_:=x+y
x_ ·y_ ·z_:=x*y+z
Print[±5]  (* 10 *)
Print[3±4] (*  7 *)
Print[3·4·5] (* 17 *)

Comparez ceci avec:

f@x_:=2x
x_~g~y_:=x+y
h[x_,y_,z_]:=x*y+z
Print[f@5]   (* 10 *)
Print[3~g~4] (*  7 *)
Print[h[x,y,z]] (* 17 *)

Donc, cela enregistre un octet lors de la définition de la fonction et deux octets lors de son utilisation. Notez que la définition de ·ne permettra pas d'économiser des octets pour quatre opérandes et commencera à coûter des octets pour plus d'opérandes, mais que l'utilisation pourra toujours économiser des octets, en fonction de la priorité des opérateurs utilisés dans les arguments. Il est également bon de noter que vous pouvez définir à moindre coût une fonction variadique qui peut ensuite être appelée beaucoup plus efficacement:

x_ ·y__:={y}
Print[1·2·3·4·5] (* {2, 3, 4, 5} *)

Mais notez qu'il est difficile d'appeler ces fonctions variadiques avec un seul argument. (Vous pouvez faire CenterDot[x]ou##&[]·x si vous en avez réellement besoin, il y a de fortes chances que vous ayez une solution différente.)

Bien sûr, cela ne sauvegarde rien pour les solutions où une fonction non nommée suffit, mais vous devez parfois définir des fonctions d'assistance pour une utilisation ultérieure, et parfois il est plus court de définir des fonctions nommées, par exemple pour configurer différentes définitions pour différents paramètres. Dans ces cas, utiliser plutôt un opérateur peut économiser une quantité décente d'octets.

Notez que l'utilisation de ces fichiers codés ISO 8859-1 nécessite $CharacterEncodingune valeur compatible, telle que la valeur par défaut de Windows WindowsANSI. Sur certains systèmes, cette valeur par défaut UTF-8ne permet pas de lire ces points de code à partir d’un seul octet.


C’est vraiment bien, je ne savais pas que Mathematica avait une liste d’opérateurs, et a même inclus leur priorité. Je suis sûr que ces deux opérateurs vous seront utiles.
miles

8

Choisir des valeurs basées sur un entier

L'approche naïve de choisir entre yet z, selon que xest 0ou 1est

If[x<1,y,z]

Cependant, il existe un moyen plus court:

y[z][[x]]

Cela fonctionne parce que [[0]]donne la Headd'une expression, dans ce cas y, alors [[1]]donne juste le premier élément - dans ce cas , le premier argument, z.

Vous pouvez même l'utiliser pour choisir entre plus de deux valeurs:

u[v,w][[x]]

Notez que cela ne fonctionnera pas si uc'est une fonction qui évalue quelque chose. Il est important que Mathematica conserve la situation actuelle u[v,w]. Cependant, cela fonctionne dans la plupart des cas, y compris s'il us'agit d'un nombre, d'une chaîne ou d'une liste.

Les crédits pour cette astuce vont à Alephalpha - j'ai découvert cela dans l'une de ses réponses.

Si xest basé sur 1 au lieu de zéro, utilisez simplement

{y,z}[[x]]

ou

{u,v,w}[[x]]

Dans de rares cas, vous pouvez même utiliser le fait que la multiplication n'est pas évaluée pour certaines valeurs:

{"abc","def"}[[x]]
("abc""def")[[x]]

Notez cependant que Mathematica réorganisera en réalité les arguments d’une multiplication si elle n’est pas évaluée, ce qui est identique à ce qui précède.

("def""abc")[[x]]

8

Alternatives à Length

Cela a été entièrement réécrit avec quelques suggestions de LegionMammal978 et Misha Lavrov. Un grand merci à tous les deux.

Dans de nombreux cas, Lengthpeut être un peu raccourci en utilisant Tr. L’idée de base est de transformer l’entrée en une liste de 1s, de sorte queTr résumer, qui seront alors égaux à la longueur de la liste.

Le moyen le plus courant consiste à utiliser 1^x(pour une liste x). Cela fonctionne car Powerc’est juste Listableet 1^npour la plupart des valeurs atomiques (y compris tous les nombres, chaînes et symboles). Nous pouvons donc déjà économiser un octet avec ceci:n1

Length@x
Tr[1^x]

Bien entendu, cela suppose qu’il xs’agit d’une expression ayant une priorité plus élevée que ^.

Si xne contient que 0s et 1s, nous pouvons enregistrer un autre octet en utilisant Factorial(en supposant xque la priorité est supérieure à !):

Length@x
Tr[x!]

Dans de rares cas, xla préséance peut être inférieure ^à la multiplication, mais néanmoins supérieure. Dans ce cas, la préséance sera également inférieure à celle de @, nous avons donc vraiment besoin de comparer Length[x]. Un exemple d'un tel opérateur est .. Dans ces cas, vous pouvez toujours enregistrer un octet avec ce formulaire:

Length[x.y]
Tr[0x.y+1]

Enfin, quelques remarques sur le type de listes sur lesquelles cela fonctionne:

Comme indiqué en haut, cela fonctionne sur des listes non hiérarchiques contenant uniquement des nombres, des chaînes et des symboles. Cependant, cela fonctionnera également sur des listes plus profondes, bien qu'il calcule en réalité quelque chose de légèrement différent. Pour un tableau rectangulaire n- D, utiliser Trvous donne la dimension la plus courte (par opposition à la première). Si vous savez que la dimension la plus extérieure est la plus courte, ou si vous savez qu'elles sont toutes identiques, les Trexpressions -express sont toujours équivalentes Length.


3
Je viens de trouver une solution encore plus courte: Length@x == Tr[1^x]. Devrait fonctionner avec la plupart des listes.
LegionMammal978

@ LegionMammal978 c'est incroyable, merci :). Je vais l'éditer bientôt.
Martin Ender

1
Deux fois maintenant, je me suis retrouvé à utiliser Tr[x!]au lieu de Tr[1^x]pour sauvegarder un octet dans le cas spécial où xne contient que des zéros et des uns.
Misha Lavrov

@MishaLavrov C'est vraiment chouette! :)
Martin Ender

7
  1. Explorer des solutions récursives - Mathematica est multi-paradigme, mais l'approche fonctionnelle est souvent la plus économique. NestWhilepeut être une solution très compacte aux problèmes de recherche NestWhileListet FoldListest puissant lorsque vous devez renvoyer ou traiter les résultats d'itérations intermédiaires. Map (/@), Apply (@@, @@@), MapThread, Et vraiment tout sur Wolfram fonctionnelle Programmation page de documentation est une chose puissante.

  2. Forme abrégée pour incrémenter / décrémenter - Par exemple, à la place de ce que While[i<1,*code*;i++]vous pouvez faire
    While[i++<1,*code*]

  3. N'oubliez pas que vous pouvez effectuer une pré-incrémentation / décrémentation - par exemple, --iau lieu de i--. Cela peut parfois vous faire économiser quelques octets dans le code environnant en éliminant une opération préparatoire.

  4. Corollaire au n ° 5 de David Carraher: lorsque la même fonction est utilisée à plusieurs reprises, lui attribuer un symbole peut économiser des octets. Par exemple, si vous utilisez ToExpression4 fois dans une solution, t=ToExpressionvous permet de l'utiliser t@*expression*par la suite. Toutefois, avant de procéder ainsi, déterminez si l'application répétée de la même fonction indique une possibilité d'adopter une approche récursive plus économique.


MapThreadpeut souvent être remplacé par \[Transpose]. TIO .
user202729

7

Ne pas utiliser {}si vous utilisez @@@.

Dans certains cas, vous pouvez rencontrer une expression telle que:

f@@@{{a,b},{c,d}}

Il est possible de réduire les octets en écrivant:

f@@@{a|b,c|d}

Alternativesa une priorité très faible, il est donc généralement acceptable d'écrire des expressions (une exception notable étant les fonctions pures; vous ne pouvez l'utiliser que dans l'élément le plus à gauche de Alternatives).

f@@@{f@a|b~g~1,#^2&@c|d@2}

Notez que f@@a|b|c(au lieu de f@@{a,b,c}) ne fonctionne pas car Applya une priorité plus élevée que Alternative.

Dans ce cas, vous devriez simplement utiliser f@@{a,b,c}.


6

Mathematica 10 uniquement

Formulaires d'opérateur

Mathematica 10 prend en charge les "formes d'opérateur", ce qui signifie que certaines fonctions peuvent être curry. Currying une fonction est de créer une nouvelle fonction en fixant l'un de ses opérateurs. Dis, tu utilisesSortBy[list, somereallylongfunction&] beaucoup de choses différentes list. Avant, vous auriez probablement assigné SortByà set la fonction pure à ftant

s=SortBy;
f=somereallylongfunction&;
list1~s~f;
list2~s~f;
list3~s~f;

Maintenant, vous pouvez curry SortBy, ce qui signifie que vous pouvez maintenant faire

s=SortBy[somereallylongfunction&];
s@list1;
s@list2;
s@list3;

Les mêmes œuvres pour beaucoup d'autres fonctions qui prennent un argument de liste ou de la fonction, y compris (mais sans s'y limiter) Select, Map, Nearest, etc.

ybeltukov sur Mathematica.SE a été en mesure de produire une liste complète de ces - :

{"AllTrue", "AnyTrue", "Append", "Apply", "AssociationMap", "Cases", 
 "Count", "CountDistinctBy", "CountsBy", "Delete", "DeleteCases", 
 "DeleteDuplicatesBy", "Extract", "FirstCase", "FirstPosition", 
 "FreeQ", "GroupBy", "Insert", "KeyDrop", "KeyExistsQ", "KeyMap", 
 "KeySelect", "KeySortBy", "KeyTake", "Map", "MapAt", "MapIndexed", 
 "MatchQ", "MaximalBy", "MemberQ", "Merge", "MinimalBy", "NoneTrue", 
 "Position", "Prepend", "Replace", "ReplacePart", "Scan", "Select", 
 "SelectFirst", "SortBy", "StringCases"}

Composition et RightComposition

Il y a de nouveaux raccourcis pour Composition( @*) et RightComposition( /*). Un exemple évidemment artificiel où ceux-ci peuvent sauver des caractères est vu dans les trois lignes équivalentes suivantes

Last@Range@# & /@ Range[5]
Last@*Range /@ Range[5]
Range /* Last /@ Range[5]

5

Ne pas écrire des fonctions à 0 argument

Il n'y a pas besoin de code comme celui-ci:

f[]:=DoSomething[1,2]
(*...*)
f[]
(*...*)
f[]

Vous pouvez simplement utiliser une variable avec :=pour forcer la réévaluation du côté droit:

f:=DoSomething[1,2]
(*...*)
f
(*...*)
f

Cela signifie également que vous pouvez aliaser n'importe quelle action que vous effectuez souvent (même si c'est quelque chose du genre n++) avec un seul caractère au prix de 5 octets. Donc, dans le cas où n++cela rapporte après la quatrième utilisation:

n++;n++;n++;n++
f:=n++;f;f;f;f

5

Utiliser %pour obtenir une variable libre

Ce conseil ne s'applique que si l'environnement REPL de Mathematica peut être supposé. %n'est pas défini lorsque le code est exécuté en tant que script.

Lorsque vous pouvez utiliser les fonctionnalités REPL, ne faites pas ceci:

a=someLongExpression;some[other*a,expression@a,using^a]

N'oubliez pas que Mathematica stocke la dernière expression évaluée (terminée par une nouvelle ligne) dans %:

someLongExpression;
some[other*%,expression@%,using^%]

La nouvelle ligne ajoutée coûte un octet, mais vous en économisez deux en supprimant a= , donc globalement, cela enregistre un octet.

Dans certains cas (par exemple, lorsque vous souhaitez imprimer la valeur de atoute façon), vous pouvez même laisser la ;, en sauvegardant deux octets:

someLongExpression
some[other*%,expression@%,using^%]

Un ou deux octets peuvent sembler assez mineurs, mais il s'agit d'un cas important, car il rend l'extraction d'expressions répétées (technique très courante) beaucoup plus utile lors de la pratique du golf:

La technique normale d'extraction d'expressions répétées coûte quatre octets de surcharge, qui doivent être sauvegardés par d'autres utilisations de l'expression. Voici un court tableau du nombre minimum d'utilisations d'une expression (par la longueur de l'expression) à extraire dans une variable nommée pour enregistrer quoi que ce soit:

Length   Min. Uses
2        6
3        4
4        3
5        3
6        2
...      2

En utilisant la variable non nommée, il sera possible de sauvegarder quelques octets beaucoup plus souvent:

When ; is required                        When ; can be omitted

Length   Min. Uses                        Length   Min. Uses
2        5                                2        4
3        3                                3        3
4        3                                4        2
5        2                                ...      2
...      2

Je ne pense pas %%ou %nne peux pas être utilisé pour jouer au golf, car si vous ne les utilisez pas au moins deux fois, vous pouvez simplement mettre l'expression là où elle est nécessaire. Et si vous l'utilisez deux fois, le caractère supplémentaire dans le nom de la variable annule les économies réalisées x=.


Notez que cela ne fonctionne pas en mode script.
Alephalpha

@alephalpha Qu'est-ce que le mode script?
Martin Ender


@alephalpha Oh oui, j'ai coupé mon cerveau là-bas pendant une seconde ... cela voudrait donc dire qu'il ne peut pas être utilisé du tout, à moins que l'environnement REPL ne puisse être supposé.
Martin Ender

5

Vérifier si une liste est triée

C’est essentiellement un corollaire de cette astuce, mais c’est une tâche suffisamment commune pour que je pense qu’elle mérite sa propre réponse.

Le moyen naïf de vérifier si une liste est en ordre est d'utiliser

OrderedQ@a

On peut mieux faire un octet avec

Sort@a==a

Cependant, cela ne fonctionne pas si nous ne disposons pas de la chose que nous voulons déjà archiver dans une variable. (Nous aurions besoin de quelque chose comme Sort[a=...]==ace qui est inutilement long.) Cependant, il existe une autre option:

#<=##&@@a

La meilleure chose à faire est que cela peut être utilisé pour vérifier si l'entrée est triée de manière inverse pour le même nombre d'octets:

#>=##&@@a

Un octet supplémentaire peut être sauvegardé si a) nous savons que les éléments de la liste sont distincts et b) nous connaissons une limite inférieure comprise entre 0 et 9 (inclus; ou une limite supérieure pour un ordre de tri inversé):

0<##&@@a
5>##&@@a

Pour voir pourquoi cela fonctionne, consultez "Séquences d'arguments" dans l'info-bulle liée en haut.


Sinon, (stricte)-minoration inverse trié également des travaux: ##>0&@@a. Semblable pour la limite supérieure pour trié.
user202729

@ user202729 Oh bon point, n'hésitez pas à éditer (sinon je vais essayer de le faire le week-end si je me souviens bien).
Martin Ender

5

Répéter une chaîne

Au lieu d' StringRepeat[str,n]utiliser (0Range[n]+str)<>"". Ou sistr ne dépend d'aucun argument de slot, le mieux est Array[str&,n]<>""de suivre cette astuce.


1
Corollaire: au lieu d'être StringRepeat[s,n+1]utilisé Array[s&,n]<>s(même si vous avez déjà n+1une variable).
Martin Ender

Mieux,Table[str,n]<>""
attinat

5

Si vous avez besoin d’une liste de numéros triée en ordre inverse, n’utilisez pas

Reverse@Sort@x

mais

-Sort@-x

pour économiser six octets. Le tri par valeur négative est également utile pour les SortByscénarios:

Reverse@SortBy[x,Last]
SortBy[x,-Last@#&]

2
Qu'en est- il -Sort@-x?
JungHwan Min

1
@JungHwanMin Oh, euh, oui, c'est beaucoup mieux. :)
Martin Ender

4

Vous pouvez coller une expression dans Breaklaquelle vous pouvez enregistrer un ou deux caractères. Exemple ( autres détails non détaillés pour plus de clarté ):

result = False;
Break[]

peut être transformé en

Break[result = False]

pour sauvegarder un personnage. Si l'expression en question n'a pas une priorité inférieure à celle de l'application de la fonction, vous pouvez même enregistrer un autre caractère:

Print@x;
Break[]

peut être transformé en

Break@Print@x

Bien que non documenté, l'argument à Breaksemble être renvoyé par la boucle environnante, ce qui peut potentiellement permettre de réaliser encore plus d'économies.


4

Pour supprimer tous les espaces d'une chaîne s, utilisez

StringSplit@s<>""

Autrement dit, utilisez StringSplitla valeur par défaut (divisée en composants non blancs) et reliez-les simplement. La même chose est probablement toujours la plus courte si vous voulez vous débarrasser de tout autre caractère ou sous-chaîne:

s~StringSplit~"x"<>""

4

Alternatives à Range

Une tâche très courante consiste à appliquer une sorte de fonction à tous les nombres de 1 à a n(généralement donnés en entrée). Il y a essentiellement 3 façons de faire cela (en utilisant une fonction d'identité non nommée comme exemple):

#&/@Range@n
Array[#&,n]
Table[i,{i,n}]

J'ai tendance à aller pour le premier (pour une raison quelconque), mais c'est rarement le meilleur choix.

Utiliser à la Arrayplace

L'exemple ci-dessus montre que l'utilisation Arraya le même nombre d'octets. Cependant, il a l'avantage d'être une expression unique. En particulier, si vous souhaitez continuer à traiter le résultat avec une fonction, fvous pouvez utiliser la notation préfixe, qui enregistre un octet sur Range:

f[#&/@Range@n]
f@Array[#&,n]

De plus, vous pourrez peut-être omettre les fonctions non nommées dont vous auriez peut-être besoin entre parenthèses Range, par exemple:

15/(#&)/@Range@n
15/Array[#&,n]

Si vous ne voulez plus l'utiliser (ou avec un opérateur moins prioritaire), vous pouvez plutôt écrire Arraylui-même en notation infixe et sauvegarder également un octet:

#&/@Range@n
#&~Array~n

Par conséquent, Arrayest presque certainement meilleur que Range.

Utiliser à la Tableplace

Maintenant, la table doit compenser 3 octets, ou au moins 2 lorsque la notation infixe est une option:

#&/@Range@n
i~Table~{i,n}

Lorsque vous n'utilisez pas la notation infixe, Tablevous pouvez éventuellement omettre les parenthèses si votre fonction est composée de plusieurs instructions:

(#;#)&/@Range@n
Table[i;i,{i,n}]

C'est encore plus long, mais donne des économies supplémentaires avec dans le cas mentionné ci-dessous.

Les économies réelles proviennent du fait que Tablela variable en cours d’exécution ne doit pas être ignorée. Vous aurez souvent des fonctions imbriquées non nommées dans lesquelles vous souhaitez utiliser la variable externe à l'intérieur de l'une des fonctions internes. Lorsque cela se produit, Tableest plus court que Range:

(i=#;i&[])&/@Range@n
Table[i&[],{i,n}]
i&[]~Table~{i,n}

Non seulement vous enregistrez les caractères à affecter i, mais vous pouvez également réduire la fonction à une seule instruction du processus, ce qui vous permet d'utiliser la notation infixe par-dessus. Pour référence, Arrayest également plus long dans ce cas, mais toujours plus court que Range:

(i=#;i&[])&~Array~n

Quand utiliseriez-vous réellement Range?

Chaque fois que vous n'avez pas besoin d'un appel de fonction pour traiter les valeurs, par exemple lorsque le mappage peut être effectué via une opération vectorisée. Par exemple:

5#&~Array~n
5Range@n
#^2&~Array~n
Range@n^2

Bien sûr, il est également plus court si vous ne souhaitez pas mapper une fonction du tout, par exemple

Mean@Array[#&,n]
Mean@Range@n

Enfin quelqu'un d'autre qui utilise f/@Range[x]régulièrement ...
LegionMammal978

4

Trouver le plus petit nombre qui satisfait une condition

Certains types de construction i=1;While[cond[i],i++]sont corrects tels quels, mais il existe une alternative plus courte de deux octets:

1//.i_/;cond[i]:>i+1

Le code ci-dessus remplace à plusieurs reprises un numéro ipar i+1tant qu'il remplit la condition cond[i]. Dans ce cas, icommence à 1.

Notez que le nombre maximum d'itérations par défaut est 2 ^ 16 (= 65536). Si vous avez besoin de plus d'itérations, Whilecela serait mieux. ( MaxIterations->∞est trop long)


4

Abuser de court-circuit d'évaluation

Vous pouvez parfois remplacer If par un opérateur logique.

Par exemple, supposons que vous souhaitiez créer une fonction qui vérifie si un nombre est premier et que l’impression l’ 2*(number) - 1est si c’est vrai:

If[PrimeQ@#,Print[2#-1]]&

C'est plus court si vous utilisez &&plutôt:

PrimeQ@#&&Print[2#-1]&

Même si vous avez plusieurs expressions, vous enregistrez toujours le (s) octet (s):

If[PrimeQ@#,a++;Print[2#-1]]&

PrimeQ@#&&a++&&Print[2#-1]&
(* or *)
PrimeQ@#&&(a++;Print[2#-1])&

Vous pouvez utiliser ||pour les cas où vous souhaitez que la condition soit False:

If[!PrimeQ@#,Print[2#-1]]&
(* or *)
If[PrimeQ@#,,Print[2#-1]]&
(* can become *)
PrimeQ@#||Print[2#-1]&

Ces astuces fonctionnent parce que les opérateurs logiques peuvent être court-circuités ; le deuxième argument et par la suite n'ont même pas besoin d'être des expressions booléennes valides.

Bien sûr, cela ne fonctionne pas si vous avez besoin de la valeur de retour Ifou lorsque vous avez besoin des arguments Trueity et Falsy de If.



3

En utilisant Optional (:)

Optional (:) peut être utilisé pour développer des listes dans des remplacements, sans avoir à définir une règle distincte pour le développement.

Cette réponse de moi et cette réponse de @ngenisis sont des exemples.

Usage

... /. {p___, a_: 0, b_, q___} /; cond[b] :> ...

Le remplacement ci-dessus utilise d’abord le motif {p___, a_, b_, q___}et trouve une correspondance qui brépond à une certaine condition.

Lorsqu'aucune correspondance de ce type n'est trouvée, elle est omise a_et recherchée à la place {p___, b_, q___}. an'est pas inclus dans la recherche et est supposé avoir la valeur 0.

Notez que la deuxième recherche de modèle ne fonctionne que pour b que qui se produit au début de la liste; si une bvaleur remplissant une condition est au milieu, alors{p___, a_, b_, q___} (qui a une priorité plus élevée) lui correspondra à la place.

Le remplacement équivaut à ajouter un préfixe 0lorsqu'une bcondition satisfaisante est remplie au début de la liste. (c.-à-d. qu'il n'est pas nécessaire de définir une règle séparée, {b_, q___} /; cond[b] :> ...)


3

Savoir quand (et quand non) utiliser des arguments de fonction purs nommés

Pour le golf de code, les Functionarguments purs sont le plus souvent référencés en utilisant Slots; par exemple #pour le premier argument, #2pour le second, etc. (voir cette réponse pour plus de détails).

Dans de nombreux cas, vous voudrez imbriquer des Functions. Par exemple, 1##&@@#&est un Functionqui prend une liste comme premier argument et génère le produit de ses éléments. Voici cette fonction dans TreeForm:

enter image description here

Les arguments passés au niveau supérieur Functionne peuvent remplir que les éléments Slots et SlotSequences présents au niveau supérieur, ce qui signifie que, dans ce cas, SlotSequenceles éléments internes Functionne disposeront d'aucun moyen d'accéder aux arguments du niveau supérieur Function.

Dans certains cas, cependant, vous pouvez souhaiter qu'un Functionimbriqué dans un autre Functionpuisse référencer des arguments à l'extérieur Function. Par exemple, vous pouvez vouloir quelque chose comme Array[fun,...]&, où la fonction fundépend d'un argument au plus haut niveau Function. Pour être concret, supposons que cela fundonne le reste du carré de son entrée modulo l’entrée au niveau supérieur Function. Une façon d'accomplir ceci est d'affecter l'argument de niveau supérieur à une variable:

(x=#;Array[Mod[#^2,x]&,...])&

Où que ce soit xà l'intérieur Function Mod[#^2,x]&, cela fera référence au premier argument à l'extérieur Function, alors que #fera référence au premier argument à l'intérieur Function. Une meilleure approche consiste à utiliser le fait qui Functiona une forme à deux arguments, le premier argument étant un symbole ou une liste de symboles qui représentera des arguments nommés à Function(par opposition à des noms non nommés Slot). Cela finit par nous sauver trois octets dans ce cas:

xArray[Mod[#^2,x]&,...]

est le caractère d'utilisation privée sur trois octets U+F4A1représentant l'opérateur infixe binaire \[Function]. Vous pouvez également utiliser la forme binaire de Functiondans un autre Function:

Array[xMod[x^2,#],...]&

Ceci est équivalent à ce qui précède. La raison en est que, si vous utilisez des arguments nommés, alors Slots et SlotSequencessont supposés appartenir à next Functionqui n’utilise pas d’arguments nommés.

Maintenant Function, ce n'est pas parce que nous pouvons faire la nidification de cette manière que nous devrions toujours le faire. Par exemple, si nous souhaitons sélectionner les éléments d’une liste inférieurs à l’entrée, nous pourrions être tentés de procéder de la manière suivante:

Select[...,xx<#]&

Il serait en fait plus court d’utiliser Caseset d’éviter la nécessité d’un niché Functioncomplet:

Cases[...,x_/;x<#]&

2

Vous pouvez enregistrer un octet en travaillant autour Prependou PrependTo:

l~Prepend~x
{x}~Join~l
{x,##}&@@l

ou

l~PrependTo~x
l={x}~Join~l
l={x,##}&@@l

Malheureusement, cela n’aide en rien le plus courant Append, qui semble être l’équivalent le plus court de un Array.push()dans d’autres langues.


2

Mathematica 10.2: BlockMapest Partition+Map

Cette astuce pourrait également s'intituler "Lisez les notes de version, toutes". (Pour référence, voici les notes de publication de 10.2 et de la version 10.3 d’aujourd’hui .)

Quoi qu'il en soit, même les versions mineures contiennent une multitude de nouvelles fonctionnalités, et l'une des plus utiles (pour le golf) à partir de 10.2 est la nouvelle BlockMapfonction. Il combine essentiellement Partitionet Map, ce qui est excellent pour les golfeurs, car il Partitionest utilisé assez souvent, et c’est un nom de fonction vraiment ennuyeux. La nouvelle fonction ne se raccourcira pas Partitiond'elle-même, mais chaque fois que vous souhaitez mapper une fonction sur les partitions (ce qui arrive probablement plus souvent qu'autrement), vous pouvez maintenant enregistrer un octet ou deux:

#&/@l~Partition~2
BlockMap[#&,l,2]
#&/@Partition[l,3,1]
BlockMap[#&,l,3,1]

Les économies deviennent encore plus importantes lorsque la nouvelle position de la fonction non nommée vous permet de vous épargner quelques parenthèses:

#&@@(#&/@Partition[l,3,1])
#&@@BlockMap[#&,l,3,1]

Malheureusement, je ne sais pas pourquoi n'a pas aussi ajouté BlockApplypendant qu'ils y étaient ...

Notez également que BlockMapne prend pas en charge le 4ème paramètre que vous pouvez utiliser Partitionpour obtenir une liste cyclique:

Partition[Range@5, 2, 1, 1]
(* Gives {{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 1}} *)
BlockMap[f, Range@5, 2, 1, 1]
(* Nope... *)

2

Stocker des fonctions et des expressions dans une variable

Si votre réponse finit par utiliser les mêmes fonctions ou expressions plusieurs fois, vous pouvez envisager de les stocker dans des variables.

Si votre expression est longue let que vous l'utilisez nfois, elle utilisera normalement plus d' l * noctets.

Cependant, si vous la stockez dans une variable de longueur 1, cela ne prend que des 3 + l + noctets (ou des 2 + l + noctets, si vous affectez la variable sans utiliser de CompoundExpression (;)parenthèses).


Par exemple, considérons un problème simple, trouver des nombres premiers jumeaux inférieurs à N.

On pourrait écrire cette solution de 54 octets:

Select[Range@#,PrimeQ@#&&(PrimeQ[#+2]||PrimeQ[#-2])&]&

Dans cet exemple, la fonction PrimeQest utilisée trois fois.

En attribuant PrimeQun nom de variable, le nombre d'octets peut être réduit. 48 octets (54 à 6 octets) sont les deux suivants:

Select[p=PrimeQ;Range@#,p@#&&(p[#+2]||p[#-2])&]&
Select[Range@#,(p=PrimeQ)@#&&(p[#+2]||p[#-2])&]&

2

Pour obtenir une liste de valeurs-clés croissante, utilisez Sortplutôt queSortBy

Pour les listes telles que list = {{1, "world"}, {0, "universe"}, {2, "country"}}, les trois déclarations suivantes sont presque équivalentes.

SortBy[list,#[[1]]&]
list~SortBy~First
Sort@list

Combiner SelectetSortBy

Parfois, nous devons sélectionner des entrées dans un ensemble plus grand et les trier pour trouver un minimum / maximum. Dans certaines circonstances , deux opérations pourraient être combinées en une seule.

Par exemple, pour un minimum, les deux déclarations suivantes sont presque équivalentes.

SortBy[Select[l,SomeFormula==SomeConstant&],SortValue&]
SortBy[l,SortValue+99!(SomeFormula-SomeConstant)^2&]

et

SortBy[Select[l,SomeFormula!=SomeConstant&],SortValue&]
SortBy[l,SortValue+1/(SomeFormula-SomeConstant)&]

1/0 est ComplexInfinity , qui est "plus grand" que tous les nombres réels.

Pour une liste de valeurs-clés, par exemple:

{SortValue,#}&/@SortBy[Select[l,SomeFormula==SomeConstant],SortValue&]
Sort[{SortValue+99!(SomeFormula-SomeConstant)^2,#})&/@l]

2

Résoudre et Réduire: liste automatique de variables

Lorsque la liste des variables pour Solveet Reduceest omise, elles résolvent toutes les variables libres dans la ou les expressions. Exemples:

Solve[x^4==1]
(*    {{x -> -1}, {x -> -I}, {x -> I}, {x -> 1}}    *)
(*    two bytes saved on Solve[x^4==1,x]            *)

Solve[x^2+y^2==25,Integers]
(*    {{x -> -5, y -> 0}, {x -> -4, y -> -3}, {x -> -4, y -> 3}, {x -> -3, y -> -4},
       {x -> -3, y -> 4}, {x -> 0, y -> -5}, {x -> 0, y -> 5}, {x -> 3, y -> -4},
       {x -> 3, y -> 4}, {x -> 4, y -> -3}, {x -> 4, y -> 3}, {x -> 5, y -> 0}}    *)
(*    six bytes saved on Solve[x^2+y^2==25,{x,y},Integers]                         *)

Reduce[x^2+y^2<25,Reals]
(*    -5 < y < 5 && -Sqrt[25 - y^2] < x < Sqrt[25 - y^2]    *)
(*    six bytes saved on Reduce[x^2+y^2<25,{x,y},Reals]     *)

Particulièrement Solvesur le Integers, NonNegativeIntegerset PositiveIntegersest extrêmement puissant pour les problèmes de dénombrement.


1

Les valeurs par défaut

Les valeurs par défaut traitent les arguments de modèle manquants de manière efficace. Par exemple, si nous voulons appliquer une correspondance de modèle Exp[c_*x]à une règle pour n’importe quelle valeur de c, le naïf

Exp[x] + Exp[2x] /. {Exp[c_*x] -> f[c], Exp[x] -> f[1]}
(*    f[1] + f[2]    *)

utilise beaucoup plus d'octets que si nous utilisions la valeur par défaut cchaque fois qu'elle est manquante:

Exp[x] + Exp[2 x] /. Exp[c_.*x] -> f[c]
(*    f[1] + f[2]    *)

L'utilisation d'un défaut est indiqué par un point après le modèle: c_..

Les valeurs par défaut sont associées aux opérations: dans l'exemple ci-dessus, l'opération est Timesen cours c_.*xet une valeur manquante pour c_est donc extraite de la valeur par défaut associée à Times, qui est 1. Pour Plus, la valeur par défaut est 0:

Exp[x] + Exp[x + 2] /. Exp[x + c_.] -> f[c]
(*    f[0] + f[2]    *)

Pour les Powerexposants, la valeur par défaut est 1:

x + x^2 /. x^n_. -> p[n]
(*    p[1] + p[2]    *)
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.