Consejos para jugar golf en Mathematica

41

¿Qué consejos generales tienes para jugar al golf en Mathematica? Estoy buscando ideas que puedan aplicarse a los problemas de código de golf en general que sean al menos algo específicos de Mathematica (por ejemplo, "eliminar comentarios" no es una respuesta).

alephalpha
fuente

Respuestas:

30

Los siguientes consejos varían de los más económicos a los más utilizados:

  1. Use los comandos de alto nivel de Mathematica cuando sea posible, incluso los voluminosos:

  2. Uso Graphics and Textpara el arte Ascii: por ejemplo, programación de estrellas y construir un reloj analógico

  3. Símbolos dedicados:

    • Lógica y establecer símbolos de operaciones en lugar de sus nombres largos: ⋂, ⋃, ∧, ∨

    • Mapy Apply: /@, //@. @@,@@@

  4. Prefijo y notación de infijo:

    • Print@"hello" en lugar de Print["hello"]

    • a~f~b en lugar de f[a,b]

  5. Cuando una función se usa solo una vez, una función pura puede economizar uno o dos caracteres.

  6. Unir cadenas en una lista. ""<>{"a","b","c"}en lugar deStringJoin@{"a","b","c"}

  7. Explotar funciones listables. Cuanto más largas sean las listas, mejor.

    {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}}

DavidC
fuente
2
Es siempre más corto para escribir (Norm[#-#2]&)en lugar de EuclideanDistance.
user202729
32

Algunas funciones integradas con nombres largos se pueden reemplazar con expresiones más cortas.

Por ejemplo:

  • Total => Tr
  • Transpose=> Threado\[Transpose]
  • True => 1<2
  • False => 1>2
  • Times => 1##&
  • Alternatives => $|##&
  • IntegerQ => ⌊#⌋==#&
  • a[[1]] => #&@@a
  • a[[All,1]] => #&@@@a
  • ConstantArray[a,n]=> Array[a&,n]oTable[a,{n}]
  • Union@a=> {}⋃aoa⋃a
  • ToExpression@n=> FromDigits@nsi nes un número
  • Divisible[n,m] => m∣n
  • FromDigits[n,2]=> Fold[#+##&,n]Si nes una lista de 0s y 1s
  • Complex@z=> {1,I}.zdonde zhay una lista del formulario{x,y}
alephalpha
fuente
55
@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
Creo que tu Foldtruco FromDigitstambién funciona para cualquier otra base, excepto 10. Por ejemplo FromDigits[n,5]-> Fold[4#+##&,n](con la ventaja de guardar un byte extra para bases 100y 1000).
Martin Ender
1
@ mbomb007 3 bytes en UTF-8. De hecho este personaje es U+F3C7.
alephalpha
1
Finalmente instalé 10.3. Si estamos considerando programas completos, no creo que Echosea ​​una opción, porque imprime >>(y un espacio) en STDOUT antes de imprimir la cadena real.
Martin Ender
2
Porque Complex[x,y] => {1,I}.{x,y}, creo que x+y*Ies mucho más corto con el mismo efecto?
Shieru Asakoto
22

Listas con valores repetidos

Este es un vector bastante común para trabajar con:

{0,0}

Resulta que esto puede ser acortado por un byte:

0{,}

Incluso se guardan más bytes si el vector es más largo que dos ceros. Esto también se puede utilizar para inicializar matrices cero, por ejemplo, lo siguiente proporciona una matriz de ceros 2x2:

0{{,},{,}}

Esto también se puede utilizar para valores distintos de cero si son lo suficientemente grandes, suficientes o negativos. Compare los siguientes pares:

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

Pero recuerde que a partir de 6 valores, es mejor 1~Table~6en este caso (potencialmente más temprano, dependiendo de los requisitos de precedencia).

La razón por la que esto funciona es que ,introduce dos argumentos en la lista, pero los argumentos omitidos (en cualquier lugar de Mathematica) son Nulls implícitos . Además, la multiplicación es Listabley 0*xes 0para casi cualquier x(excepto para cosas como Infinityy Indeterminate), así que aquí está lo que está sucediendo:

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

Para las listas de 1s, puede usar un truco similar haciendo uso de las reglas de exponenciación. Hay dos formas diferentes de guardar bytes si tiene al menos tres 1s en la lista:

{1,1,1}
1^{,,}
{,,}^0
Martin Ender
fuente
77
+1; Esto demuestra que, si bien Mathematica puede tener una función integrada para todo, jugar al golf puede ser un verdadero desafío.
LegionMammal978
Si desea una matriz que finalmente se llena con 1s, entonces 1^{,,,}es un byte más pequeño que 0{,,,}+1.
Misha Lavrov
@MishaLavrov Oh, buena captura. Eso lo hace más corto en tres valores y también puede usar {,,}^0. Editaré la publicación.
Martin Ender
19

Conoce tus argumentos de función pura

Al jugar al golf, a menudo empleará un enfoque funcional, en el que utiliza funciones anónimas (puras) con la &sintaxis abreviada. Hay muchas maneras diferentes de acceder a los argumentos de una función de este tipo, y a menudo puede reducir un par de bytes al tener un buen conocimiento de las posibilidades.

Acceso a argumentos únicos

Probablemente lo sepas si has usado funciones puras antes. El n º argumento se denomina #n, y #actúa como un alias para #1. Entonces, si, por ejemplo, desea escribir una función que tome como parámetros otra función y su argumento (para pasar el argumento a esa función), use

#@#2&

Esto no funciona con números negativos (como los que podría usar al acceder a las listas).

Acceso a argumentos con nombre (nuevo en V10)

Una de las principales características nuevas del lenguaje en Mathematica 10 es Associations, que son básicamente mapas de valores clave con tipos de clave arbitrarios, escritos como

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

Si dicha asociación se pasa como primer argumento a una función pura, puede acceder a algunos de sus argumentos como parámetros con nombre:

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

Tenga en cuenta que #todavía se refiere a toda la asociación como se esperaba. Para que los parámetros nombrados funcionen, las claves deben ser cadenas (no funcionará si utiliza variables indefinidas, por ejemplo), y esas cadenas deben comenzar con una letra y solo contener letras y dígitos.

El argumento del "yo" #0

Una característica menos conocida es que #0también existe y le proporciona el objeto de función en sí. Esto puede ser realmente útil en quines y quines generalizados. De hecho, la quine más corta de Mathematica (que conozco) es

ToString[#0][] & []

Lo que es un poco molesto es que no te dará los caracteres exactos que ingresaste. Por ejemplo, si se usa @para la aplicación de funciones, todavía se representará como [...]y se insertarán espacios en algunos lugares. Esto generalmente hará que la quine sea un poco más larga de lo que le gustaría, pero siempre funcionará, primero jugando al quine y luego simplemente copiando su salida, que ahora debería ser una quine real.

Además de quines, esto también significa que puede escribir código recursivo sin tener que nombrar su función. Compare estas tres implementaciones de Fibonacci (ingenuas pero golfistas):

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]]&

Secuencias de argumentos

Ahora aquí es donde comienza la verdadera magia. Las secuencias no se usan a menudo en el golf, porque Sequencees un nombre demasiado largo para que valga la pena la mayor parte del tiempo. Pero en las funciones puras es donde brillan. Si no está familiarizado con las secuencias, básicamente son como símbolos en algunos otros idiomas, si usa una secuencia en una Listlista de argumentos de una función, sus elementos se expandirán automáticamente en ranuras separadas. Asi que

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

Ahora, en funciones puras ##o ##1es una secuencia de todos los argumentos. Del mismo modo, ##2es una secuencia de todos los argumentos que comienzan desde el segundo, ##3todos los argumentos que comienzan desde el tercero, etc. Entonces, para empezar, podemos volver a implementar Sequencecomo ##&, ahorrando 5 bytes. Como ejemplo de uso, esto nos proporciona una alternativa a Join@@list(vea este consejo ), que no guarda ningún byte, pero es bueno saber de todos modos:

 ##&@@@list

Esto efectivamente aplana el primer nivel de una lista anidada. ¿Qué más podemos hacer con esto? Aquí hay una alternativa más corta de 2 bytes para RotateLeft:

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

Solo por estas cosas vale la pena tener en cuenta esta característica. Sin embargo, podemos hacerlo mejor! Las secuencias se vuelven realmente interesantes cuando se considera que los operadores se implementan realmente como funciones ocultas. Por ejemplo, en a+brealidad evalúa a Plus[a,b]. Entonces, si le damos una secuencia ...

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

Este truco se ha utilizado en este consejo para guardar un byte Times, porque técnicamente la yuxtaposición también es solo un operador:

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

También puede usarlo para guardar un byte Unequalsi tiene un valor o variable de un solo carácter que sabe que no está en sus argumentos ( Nprobablemente funcionará en el 99% de los casos):

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

Esto se vuelve aún más interesante con los operadores unarios y -y /- los dos últimos se llevan a la práctica los términos de la multiplicación y exponenciación. Aquí hay una lista de cosas que puede hacer, donde la última columna asume que la función pasó los argumentos 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

Otros operadores son comunes !=, ==, &&, ||. Menos comunes a tener en cuenta son |, @*, /*. Para concluir, aquí hay un pequeño truco extra:

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

¡Siga experimentando con estos y avíseme si encuentra otras aplicaciones útiles o particularmente interesantes!

Martin Ender
fuente
15

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

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

#+#2&=>+##&

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

Function[x,x^2]=> xx^2o#^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

chyanog
fuente
1
Tenga en cuenta que cuando mide por bytes, usa y toma 3 bytes cada uno (suponga UTF8)
usuario202729
12

Operadores como funciones

Inspirado por el reciente descubrimiento de Dennis para Julia , pensé en investigar esto para Mathematica. Sabía que Mathematica define una gran cantidad de operadores no utilizados, pero nunca le prestó mucha atención.

Como referencia, la lista de todos los operadores se puede encontrar aquí en forma de una tabla de precedencia. El triángulo en la última columna indica si ese operador tiene un significado incorporado o no. Si bien no todos los que no se pueden definir fácilmente, la mayoría sí.

Convenientemente, hay dos operadores no utilizados con un punto de código inferior a 256, de modo que se pueden usar como bytes individuales en un archivo fuente codificado ISO 8859-1:

  • ± (0xB1) se puede usar como operador de prefijo unario o como operador de infijo binario.
  • · (0xB7) se puede utilizar como operador infijo variadic o n-ary, para n> 2.

Sin embargo, hay una trampa más: por alguna extraña razón al definir estos operadores, necesita un espacio delante de ellos, o Mathematica intenta analizar una multiplicación. Sin embargo, al usarlos no necesita espacios:

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

Compare esto con:

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

Esto ahorra un byte cuando se define la función y dos bytes cuando se usa. Tenga en cuenta que la definición de ·no guardará bytes para cuatro operandos y comenzará a costar bytes para más operandos, pero el uso aún puede guardar bytes, dependiendo de la precedencia de los operadores utilizados en los argumentos. También es bueno tener en cuenta que puede definir de manera económica una función variada que luego se puede llamar de manera mucho más eficiente:

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

Pero tenga en cuenta que no es fácilmente posible llamar a estas funciones variadas con un solo argumento. (Podría hacerlo CenterDot[x]o ##&[]·xsi realmente lo necesita, hay una buena posibilidad de que esté mejor con una solución diferente).

Por supuesto, esto no está guardando nada para soluciones en las que una función sin nombre es suficiente, pero a veces es necesario definir funciones auxiliares para su uso posterior, y a veces es más corto definir funciones con nombre, por ejemplo, para establecer diferentes definiciones para diferentes parámetros. En esos casos, usar un operador en su lugar puede ahorrar una cantidad decente de bytes.

Tenga en cuenta que el uso de estos archivos codificados ISO 8859-1 $CharacterEncodingdebe establecerse en un valor compatible, como el predeterminado de Windows WindowsANSI. En algunos sistemas, este valor predeterminado UTF-8no podrá leer estos puntos de código desde bytes individuales.

Martin Ender
fuente
Esto es realmente genial, no sabía que Mathematica tenía una lista de operadores, e incluso incluía su precedencia. Esos dos operadores que encontraste, estoy seguro, serán útiles.
millas
8

Elegir valores basados ​​en enteros

El enfoque ingenuo para elegir entre yy z, dependiendo de si xes 0o 1es

If[x<1,y,z]

Sin embargo, hay una forma más corta:

y[z][[x]]

Esto funciona porque [[0]]da la Headde una expresión, en este caso y, mientras que [[1]]solo da el primer elemento - en este caso el primer argumento, z.

Incluso puede usar esto para elegir entre más de dos valores:

u[v,w][[x]]

Tenga en cuenta que esto no funcionará si ues una función que realmente se evalúa como algo. Es importante que Mathematica se mantenga u[v,w]como está. Sin embargo, esto funciona en la mayoría de los casos, incluso si ues un número, una cadena o una lista.

Los créditos para este truco van a alephalpha: descubrí esto en una de sus respuestas.

Si xse basa en 1 en lugar de en cero, solo use

{y,z}[[x]]

o

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

En algunos casos raros, incluso puede hacer uso del hecho de que la multiplicación no se evalúa para algunos valores:

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

Sin embargo, tenga en cuenta que Mathematica reordenará los argumentos, de una multiplicación si permanece sin evaluar, por lo que lo anterior es idéntico a

("def""abc")[[x]]
Martin Ender
fuente
8

Alternativas a Length

Esto ha sido completamente reescrito con algunas sugerencias de LegionMammal978 y Misha Lavrov. Muchas gracias a los dos.

En muchos casos, Lengthse puede acortar un poco haciendo uso de Tr. La idea básica es convertir la entrada en una lista de 1s, de modo que las Trsume , lo que equivale a la longitud de la lista.

La forma más común de hacer esto es usar 1^x(para una lista x). Esto funciona porque Poweres Listabley 1^npara la mayoría de los valores atómicos nes justo 1(incluidos todos los números, cadenas y símbolos). Entonces ya podemos guardar un byte con esto:

Length@x
Tr[1^x]

Por supuesto, esto supone que xes una expresión con mayor precedencia que ^.

Si xcontiene solo 0sy 1s, podemos guardar otro byte usando Factorial(suponiendo que xtenga mayor prioridad que !):

Length@x
Tr[x!]

En algunos casos raros, xpodría tener una precedencia más baja que ^una precedencia aún mayor que la multiplicación. En ese caso, también tendrá una precedencia menor que @, por lo que realmente necesitamos comparar Length[x]. Un ejemplo de tal operador es .. En esos casos, aún puede guardar un byte con este formulario:

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

Finalmente, algunos comentarios sobre qué tipo de listas funcionan:

Como se mencionó en la parte superior, esto funciona en listas planas que contienen solo números, cadenas y símbolos. Sin embargo, también funcionará en algunas listas más profundas, aunque en realidad calcula algo ligeramente diferente. Para una matriz rectangular n -D, el uso Trle brinda la dimensión más corta (en oposición a la primera). Si sabe que la dimensión más externa es la más corta, o sabe que son todas iguales, entonces las Trexpresiones -son equivalentes a Length.

Martin Ender
fuente
3
Acaba de encontrar una solución aún más corto: Length@x == Tr[1^x]. Debería funcionar con la mayoría de las listas.
LegionMammal978
@ LegionMammal978 eso es increíble, gracias :). Lo editaré pronto.
Martin Ender
1
Dos veces ahora, me he encontrado usando en Tr[x!]lugar de Tr[1^x]guardar un byte en el caso especial donde xsolo contiene ceros y unos.
Misha Lavrov
@MishaLavrov ¡Eso es genial! :)
Martin Ender
7
  1. Explore soluciones recursivas : Mathematica es multi-paradigma, pero el enfoque funcional es a menudo el más económico. NestWhilepuede ser una solución muy compacta a problemas de búsqueda, y NestWhileListy FoldListson poderosos cuando se necesita devolver o procesar los resultados de iteraciones intermedios. Map (/@), Apply (@@, @@@), MapThread, Y realmente todo el contenido de Wolfram programación funcional página de documentación es potente cosas.

  2. Forma acortada para incremento / decremento : por ejemplo, en lugar de While[i<1,*code*;i++]que pueda hacer
    While[i++<1,*code*]

  3. No olvide que puede aumentar / disminuir previamente : por ejemplo, en --ilugar de i--. Esto a veces puede ahorrarle unos pocos bytes en el código circundante al eliminar una operación preparatoria.

  4. Corolario del n. ° 5 de David Carraher: cuando se usa la misma función muchas veces, asignarle un símbolo puede ahorrar bytes. Por ejemplo, si está usando ToExpression4 veces en una solución, le t=ToExpressionpermite usar t@*expression*después. Sin embargo, antes de hacer esto, considere si la aplicación repetida de la misma función indica una oportunidad para un enfoque recursivo más económico.

Jonathan Van Matre
fuente
MapThreada menudo puede ser reemplazado por \[Transpose]. TIO .
user202729
7

No lo use {}si lo está usando @@@.

En algunos casos, puede encontrar una expresión como:

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

Es posible reducir bytes escribiendo:

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

Alternativestiene una precedencia muy baja, por lo que generalmente está bien escribir expresiones (una excepción notable son las funciones puras; solo puede usarlo en el elemento más a la izquierda de Alternatives).

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

Tenga en cuenta que f@@a|b|c(en lugar de f@@{a,b,c}) no funciona porque Applytiene una precedencia mayor que Alternative.

En este caso, simplemente debes usar f@@{a,b,c}.

JungHwan Min
fuente
6

Solo Mathematica 10

Formularios de operador

Mathematica 10 admite las llamadas "formas de operador", lo que básicamente significa que algunas funciones se pueden cursar. Hacer curry de una función es crear una nueva función arreglando uno de sus operadores. Digamos que estás usando SortBy[list, somereallylongfunction&]muchos lists diferentes . Antes, es probable que haya asignado SortBya sy la función pura a fpor lo

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

Ahora puedes curry SortBy, lo que significa que ahora puedes hacer

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

Lo mismo funciona para muchas otras funciones, que tienen una lista o función argumento, incluyendo (pero no limitado a) Select, Map, Nearest, etc.

ybeltukov en Mathematica.SE pudo producir una lista completa de estos :

{"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"}

Composición y composición derecha

Hay nuevas shorthands para Composition( @*) y RightComposition( /*). Un ejemplo obviamente ideado donde estos pueden guardar caracteres se ve en las siguientes tres líneas equivalentes

Last@Range@# & /@ Range[5]
Last@*Range /@ Range[5]
Range /* Last /@ Range[5]
Martin Ender
fuente
5

No escriba funciones de argumento 0

No hay necesidad de código como este:

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

Simplemente puede usar una variable con :=para forzar la reevaluación del lado derecho:

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

Esto también significa que puede alias cualquier acción que realice a menudo (incluso si es algo así n++) a un solo carácter a un costo de 5 bytes. Entonces, en el caso de n++que pague después del cuarto uso:

n++;n++;n++;n++
f:=n++;f;f;f;f
Martin Ender
fuente
5

Use %para obtener una variable libre

Este consejo solo es aplicable si se puede suponer el entorno REPL de Mathematica. %no se define cuando el código se ejecuta como un script.

Cuando pueda hacer uso de las funciones REPL, no haga esto:

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

En cambio, recuerde que Mathematica almacena la última expresión evaluada (terminada en nueva línea) en %:

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

La nueva línea agregada cuesta un byte, pero está ahorrando dos al eliminar a=, por lo que, en general, esto ahorra un byte.

En algunos casos (por ejemplo, cuando desea imprimir el valor de atodos modos), incluso puede dejar de lado ;, guardando dos bytes:

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

Uno o dos bytes pueden parecer bastante menores, pero este es un caso importante, ya que hace que la extracción de expresiones repetidas (que es una técnica muy común) sea mucho más útil cuando se juega al golf:

La técnica normal de extraer expresiones repetidas cuesta cuatro bytes de sobrecarga, que deben guardarse mediante usos adicionales de la expresión. Aquí hay una breve tabla del número mínimo de usos de una expresión (por longitud de la expresión) para extracción en una variable con nombre para guardar cualquier cosa:

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

Al usar la variable sin nombre, será posible guardar un par de bytes con mucha más frecuencia:

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

No creo %%o %npuedo usarlo para jugar al golf, porque si no los usas al menos dos veces, puedes poner la expresión justo donde se necesita. Y si lo usa dos veces, el carácter adicional en el nombre de la variable cancela los ahorros por omitir algunos x=.

Martin Ender
fuente
Tenga en cuenta que no funciona en modo script.
alephalpha
@alephalpha ¿Qué es el modo script?
Martin Ender
Un guión matemático .
alephalpha
@alephalpha Oh, claro, apagué mi cerebro allí por un segundo ... así que eso significaría que realmente no se puede usar en absoluto, a menos que se pueda suponer el entorno REPL.
Martin Ender
5

Comprobando si una lista está ordenada

Esto es esencialmente un corolario de este consejo, pero esta es una tarea suficientemente común que creo que justifica su propia respuesta.

La forma ingenua de verificar si una lista está en orden es usar

OrderedQ@a

Podemos hacer un byte mejor con

Sort@a==a

Sin embargo, esto no funciona si ya no tenemos lo que queremos registrar en una variable. (Necesitaríamos algo como lo Sort[a=...]==aque es innecesariamente largo). Sin embargo, hay otra opción:

#<=##&@@a

Lo mejor es que esto se puede usar para verificar si la entrada está ordenada inversamente para el mismo recuento de bytes:

#>=##&@@a

Se puede guardar un byte más si a) sabemos que los elementos de la lista son distintos yb) conocemos un límite inferior entre 0 y 9 (inclusive; o límite superior para el orden inverso):

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

Para ver por qué esto funciona, consulte "Secuencias de argumentos" en la sugerencia vinculada en la parte superior.

Martin Ender
fuente
Alternativamente, (estricto) límite inferior para el revés-ordenados también trabajo: ##>0&@@a. Similar para límite superior para ordenado.
user202729
@ user202729 Oh, buen punto, siéntete libre de editar (de lo contrario, intentaré hacerlo el fin de semana si no recuerdo mal).
Martin Ender
5

Repetir una cuerda

En lugar de StringRepeat[str,n]usar (0Range[n]+str)<>"". O si strno depende de ningún argumento de ranura, aún mejor es Array[str&,n]<>""según este consejo.

Feersum
fuente
1
Corolario: en lugar de StringRepeat[s,n+1]usar Array[s&,n]<>s(incluso cuando ya tiene n+1una variable, también).
Martin Ender
Mejor,Table[str,n]<>""
attinat
5

Si necesita una lista de números ordenados al revés, no use

Reverse@Sort@x

pero

-Sort@-x

para guardar seis bytes. Ordenar por un valor negativo también es útil para SortByescenarios:

Reverse@SortBy[x,Last]
SortBy[x,-Last@#&]
Martin Ender
fuente
2
¿Qué hay de -Sort@-x?
JungHwan Min
1
@JungHwanMin Oh, uhhh, sí, eso está mucho mejor. :)
Martin Ender
4

Puede pegar una expresión en la Breakque puede guardar uno o dos caracteres. Ejemplo ( otros detalles no revelados para mayor claridad ):

result = False;
Break[]

se puede convertir en

Break[result = False]

para salvar a un personaje Si la expresión en cuestión no tiene menor prioridad que la aplicación de función, incluso puede guardar otro carácter:

Print@x;
Break[]

se puede convertir en

Break@Print@x

Aunque no está documentado, el argumento Breakparece ser devuelto por el ciclo circundante, lo que puede conducir a ahorros aún mayores.

Martin Ender
fuente
4

Para eliminar todos los espacios en blanco de una cadena s, use

StringSplit@s<>""

Es decir, use StringSplitel valor predeterminado (dividido en componentes que no sean espacios en blanco) y simplemente vuelva a unirlos. Probablemente, el mismo sea el más corto si quieres deshacerte de cualquier otro personaje o subcadena:

s~StringSplit~"x"<>""
Martin Ender
fuente
4

Alternativas a Range

Una tarea muy común es aplicar algún tipo de función a todos los números del 1 al a n(generalmente se proporciona como entrada). Básicamente, hay 3 formas de hacer esto (usando una función de identidad sin nombre como ejemplo):

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

Tiendo a ir por el primero (por cualquier razón), pero rara vez es la mejor opción.

Usando en su Arraylugar

El ejemplo anterior muestra que usar Arraytiene el mismo número de bytes. Sin embargo, tiene la ventaja de que es una sola expresión. En particular, si desea seguir procesando el resultado con una función f, puede usar la notación de prefijo, que guarda un byte sobre Range:

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

Además, puede omitir paréntesis alrededor de su función sin nombre que podría haber necesitado Range, por ejemplo,

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

Si no desea usarlo más (o con un operador que tiene menor prioridad), puede escribirlo Arrayen notación infija y también guardar un byte:

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

Por lo tanto, Arrayes casi seguro mejor que Range.

Usando en su Tablelugar

Ahora la tabla tiene que compensar 3 bytes, o al menos 2 cuando la notación infijada es una opción:

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

Cuando no use la notación infija, Tablepodría permitirle omitir paréntesis si su función consta de varias declaraciones:

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

Esto aún es más largo, pero ofrece ahorros adicionales en el caso mencionado a continuación.

Los ahorros reales se Tabledeben al hecho de que no se debe descartar el nombre de la variable en ejecución. A menudo, habrá anidado funciones sin nombre donde desea utilizar la variable externa dentro de una de las funciones internas. Cuando eso sucede, Tablees más corto que Range:

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

No solo guarda los caracteres para asignarlos i, sino que también puede reducir la función a una sola declaración en el proceso, lo que le permite usar la notación infija encima. Como referencia, Arraytambién es más largo en este caso, pero aún más corto que Range:

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

¿Cuándo lo usarías realmente Range?

Siempre que no necesite una llamada de función para procesar los valores, por ejemplo, cuando la asignación se puede realizar mediante una operación vectorizada. Por ejemplo:

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

Por supuesto, también es más corto si no desea asignar ninguna función, por ejemplo,

Mean@Array[#&,n]
Mean@Range@n
Martin Ender
fuente
Finalmente alguien más que usa f/@Range[x]regularmente ...
LegionMammal978
4

Encontrar el número más pequeño que satisface una condición

Algunos constructos like i=1;While[cond[i],i++]están bien como están, pero hay una alternativa que es dos bytes más corta:

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

El código anterior reemplaza repetidamente un número ipor i+1mientras cumple la condición cond[i]. En este caso, icomienza en 1.

Tenga en cuenta que el número máximo predeterminado de iteraciones es 2 ^ 16 (= 65536). Si necesita más iteraciones que eso, Whilesería mejor. ( MaxIterations->∞es demasiado largo)

JungHwan Min
fuente
4

Abuso de evaluación de cortocircuito

A veces puede reemplazar Ifcon un operador lógico.

Por ejemplo, supongamos que desea hacer una función que verifique si un número es primo, e imprimir 2*(number) - 1es si es verdadero:

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

Es más corto si usas en su &&lugar:

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

Incluso cuando tiene varias expresiones, aún guarda byte (s):

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

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

Puede usar ||para casos en los que desea que la condición sea False:

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

Estos trucos funcionan porque los operadores lógicos pueden estar en cortocircuito ; el segundo argumento y, posteriormente, ni siquiera necesitan ser expresiones booleanas válidas.

Por supuesto, esto no funciona si necesita el valor de retorno de Ifo cuando necesita argumentos tanto verdaderos como falsos If.

JungHwan Min
fuente
3

Utilizando Optional (:)

Optional (:) se puede usar para expandir listas en reemplazos, sin tener que definir una regla separada para la expansión.

Esta respuesta de mí y esta respuesta de @ngenisis son ejemplos.

Uso

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

El reemplazo anterior utiliza primero el patrón {p___, a_, b_, q___}y encuentra una coincidencia que bcumple una determinada condición.

Cuando no se encuentra dicha coincidencia, se omite a_y en su lugar se busca {p___, b_, q___}. ano está incluido en la búsqueda y se supone que tiene el valor 0.

Tenga en cuenta que la segunda búsqueda de patrones solo funcionaría para beso ocurre al comienzo de la lista; si un bvalor que satisface una condición está en el medio, entonces {p___, a_, b_, q___}(que tiene una precedencia más alta) lo igualará en su lugar.

El reemplazo es equivalente a anteponer a 0cuando se bproduce una condición satisfactoria al comienzo de la lista. (es decir, no es necesario definir una regla separada {b_, q___} /; cond[b] :> ...)

JungHwan Min
fuente
3

Sepa cuándo (y cuándo no) usar argumentos de función pura con nombre

Para el golf de código, los Functionargumentos puros se referencian más comúnmente usando Slots; por ejemplo, #para el primer argumento, #2para el segundo, etc. (vea esta respuesta para más detalles).

En muchos casos, querrás anidar Functions. Por ejemplo, 1##&@@#&es un Functionque toma una lista como su primer argumento y genera el producto de sus elementos. Aquí está esa función en TreeForm:

ingrese la descripción de la imagen aquí

Los argumentos pasados ​​al nivel superior Functionsolo pueden llenar Slotsys SlotSequencepresentes en el nivel superior, lo que en este caso significa que SlotSequenceen el interior Functionno habrá ninguna forma de acceder a los argumentos al nivel superior Function.

Sin embargo, en algunos casos, es posible que desee un Functionanidado dentro de otro Functionpara poder hacer referencia a argumentos externos Function. Por ejemplo, es posible que desee algo como Array[fun,...]&, donde la función fundepende de un argumento al nivel superior Function. Para concretar, digamos que fundebería dar al resto del cuadrado de su módulo de entrada la entrada al nivel superior Function. Una forma de lograr esto es asignar el argumento de nivel superior a una variable:

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

Dondequiera que xaparezca en lo interno Function Mod[#^2,x]&, se referirá al primer argumento a lo externo Function, mientras que #se referirá al primer argumento a lo interno Function. Un mejor enfoque es utilizar el hecho de que Functiontiene una forma de dos argumentos donde el primer argumento es un símbolo o una lista de símbolos que representarán argumentos con nombre para el Function(en lugar de Slots sin nombre ). Esto termina ahorrándonos tres bytes en este caso:

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

es el carácter de uso privado de tres bytes que U+F4A1representa el operador infijo binario \[Function]. También puede usar la forma binaria de Functionotro Function:

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

Esto es equivalente a lo anterior. La razón es que, si está utilizando argumentos con nombre, se supone que Slotsy SlotSequencespertenecen al siguiente Functionanterior que no utiliza argumentos con nombre.

Ahora, solo porque podamos anidar Functions de esta manera, no significa que siempre debamos hacerlo. Por ejemplo, si quisiéramos seleccionar aquellos elementos de una lista que son menores que la entrada, podríamos sentir la tentación de hacer algo como lo siguiente:

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

En realidad, sería más corto de usar Casesy evitaría la necesidad de un anidado por Functioncompleto:

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

Puede guardar un byte trabajando Prependo PrependTo:

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

o

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

Desafortunadamente, esto no ayuda para los más comunes Append, que parece ser el equivalente más corto de un Array.push()en otros idiomas.

Martin Ender
fuente
2

Mathematica 10.2: BlockMapes Partition+Map

Este consejo también podría titularse, "Lea las notas de la versión, todas". (Como referencia, aquí están las notas de la versión 10.2 y aquí la versión 10.3 de hoy ).

De todos modos, incluso las versiones menores contienen una gran cantidad de nuevas características, y una de las más útiles (para jugar al golf) de 10.2 es la nueva BlockMapfunción. Básicamente se combina Partitiony Mapes ideal para los golfistas, porque Partitionse usa con bastante frecuencia y es un nombre de función realmente molesto y largo. La nueva función no se acortará Partitionpor sí sola, pero cada vez que desee asignar una función a las particiones (lo que probablemente ocurre con mayor frecuencia), ahora puede guardar un byte o dos:

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

Los ahorros se hacen aún mayores cuando la nueva posición de la función sin nombre le permite guardar algunos paréntesis:

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

Desafortunadamente, no tengo idea de por qué no agregué BlockApplymientras estaban en eso ...

También tenga en cuenta que BlockMapno admite el cuarto parámetro con el que puede usar Partitionpara obtener una lista cíclica:

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... *)
Martin Ender
fuente
2

Almacenar funciones y expresiones en una variable

Si su respuesta termina usando las mismas funciones o expresiones varias veces, puede considerar almacenarlas en variables.

Si su expresión es longitud ly la usa nveces, normalmente usaría l * nbytes.

Sin embargo, si lo almacena en una variable de longitud 1, solo tomaría 3 + l + nbytes (o 2 + l + nbytes, si asigna la variable donde no la necesitará CompoundExpression (;)o paréntesis).


Por ejemplo, consideremos un problema simple, encontrar primos gemelos menores que N.

Se podría escribir esta solución de 54 bytes:

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

En este ejemplo, la función PrimeQse usa tres veces.

Al asignar PrimeQun nombre de variable, el recuento de bytes se puede reducir. Los dos siguientes son 48 bytes (54 - 6 bytes):

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

Para lograr una lista ascendente de clave-valor, use en Sortlugar deSortBy

Para listas como list = {{1, "world"}, {0, "universe"}, {2, "country"}}, las siguientes tres declaraciones son casi equivalentes.

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

Combinar SelectySortBy

A veces necesitamos seleccionar entradas de un conjunto más grande y ordenarlas para encontrar un mínimo / máximo. En algunas circunstancias , dos operaciones podrían combinarse en una.

Por ejemplo, como mínimo, las siguientes dos declaraciones son casi equivalentes.

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

y

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

1/0es decir ComplexInfinity, que es "más grande" que todos los números reales.

Para una lista de valores clave, por ejemplo:

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

Aplanar un Arraycon##&

Cuando use una matriz multidimensional para calcular una lista de resultados que necesita ser aplanada, use ##&como cuarto argumento. Esto reemplaza las cabezas de la matriz con ##&(equivalente a Sequence) en lugar de List, por lo que el resultado final será un (plano) Sequencede resultados.

En dos dimensiones, compare

{Array[f,dims,origin,##&]}
Join@@Array[f,dims,origin]

Por supuesto, Join@@Array[f,dims] sigue siendo 2 (o 3, si se puede usar la notación infija) bytes más cortos que {Array[f,dims,1,##&]}.

En tres o más dimensiones, {Array[f,dims,origin,##&]}siempre es más corto que la alternativa, incluso si el origen es 1.

{Array[f,dims,1,##&]}
f~Array~dims~Flatten~2
attinat
fuente
1

Valores predeterminados

Los valores predeterminados tratan los argumentos de patrones faltantes de manera eficiente. Por ejemplo, si queremos un patrón de coincidencia Exp[c_*x]en una regla para cualquier valor de c, el ingenuo

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

usa muchos más bytes que si usamos el valor predeterminado para ccuando falta:

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

El uso de un valor predeterminado, se indica con un punto después el patrón: c_..

Los valores predeterminados están asociados con las operaciones: en el ejemplo anterior, la operación está Timesactivada c_.*xy, por c_lo tanto, se toma un valor faltante del valor predeterminado asociado con Times, que es 1. Para Plus, el valor predeterminado es 0:

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

Para Powerexponentes, el valor predeterminado es 1:

x + x^2 /. x^n_. -> p[n]
(*    p[1] + p[2]    *)
romano
fuente