Consejos para jugar golf en Julia

20

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

Jonathan Van Matre
fuente

Respuestas:

19

NOTA: El siguiente puede contener algunos consejos desactualizados, ya que Julia aún no está completamente estabilizada en términos de estructura.

Algunos trucos para salvar algunos personajes.

  1. Operadores de sobrecarga con funciones binarias de uso frecuente . Por ejemplo, si necesita hacer muchas divisiones enteras, y no necesita división inversa, use \ =div, y luego puede escribir en a\blugar de div(a,b). Tenga en cuenta el espacio: esto es necesario para evitar que se analice como un operador "\ =". También tenga en cuenta que, si está sobrecargado en el nivel de solicitud REPL, use (\)=Base.(\)o \ =Base. \para restablecerlo. NOTA: algunas funciones tienen operadores UTF-8 existentes predefinidos, como ÷for div, como señaló Alex A.
  2. Use ^ con cadenas para salida condicional . Es decir, en lugar de a>0?"Hi":""usar "Hi"^(a>0)para guardar un byte, o para booleano a, usar "Hi"^apara guardar tres bytes.
  3. (a veces) Almacene pequeños vectores de tamaño fijo como variables separadas . Por ejemplo, en lugar de hacerlo a=split("Hi there"," "), puede evitar a[1]y a[2]usar a,b=split("Hi there"," "), lo que se puede hacer referencia a ay b, guardando tres bytes para cada uso, a costa de solo dos caracteres adicionales en la asignación. Obviamente, no haga esto si puede trabajar con operaciones vectoriales.
  4. Acceda al primer elemento de la matriz con[] : para las matrices, la expresión A[]es equivalente a A[1]. Tenga en cuenta que esto no funciona para las cadenas si desea obtener el primer carácter, o para las tuplas.
  5. No use isempty para matrices, tuplas o cadenas ; en su lugar, use ==[]para matrices y ==()tuplas; de manera similar, para lo negativo, use !=[]y !=(). Para cadenas, use ==""para vacío, pero use >""para no vacío, ya que "" es lexicográficamente antes que cualquier otra cadena.
  6. Use el operador booleano de cortocircuito correcto en lugar de "if" . Puede ser un poco menos específico de Julia, pero vale la pena tenerlo en cuenta. x<=1&&"Hi"se puede escribir como x>1||"Hi"guardar un carácter (siempre que el retorno del booleano no sea importante).
  7. No use contiene para verificar la presencia de caracteres en la cadena ; si está restringido al ASCII básico, use en in('^',s)lugar de contains(s,"^"). Si puede usar otros caracteres, puede ahorrar un poco más con '^'∈s, pero tenga en cuenta que son 3 bytes en UTF-8.
  8. ¿Busca valores mínimos / máximos en una matriz? No use mínimo o máximo , en lugar de usar minimum(x)o maximum(x), usar min(x...)o max(x...), para eliminar un carácter de su código, si sabe xque tendrá al menos dos elementos. Alternativamente, si sabe que todos los elementos xserán no negativos, use minabs(x)omaxabs(x)
  9. Donde sea posible y permitido por el desafío, use una nueva línea real en lugar de \ n - tenga en cuenta que esto hará que su código sea más difícil de leer, y puede significar que necesita proporcionar una versión "no oculta" para que las personas puedan entender eso.
  10. Ponga opciones después de la cadena de expresiones regulares : si desea tener una cadena de expresiones regulares en modo multilínea, por ejemplo, no escriba r"(?m)match^ this", escriba r"match^ this"m, guardando tres caracteres.
  11. Invertir arrays 1-D utilizando flipud - reverse(x)es un byte más largo que flipud(x)y llevará a cabo la misma operación, por lo que el último es mejor.
  12. Siempre que sea posible, ¡use la concatenación de matriz en lugar de presionar! - para matrices normales, esto se puede hacer fácilmente. Para matrices de tipo Any con elementos de matriz, necesitará llaves entre las matrices agregadas (es decir {[1,2]}, no {1,2}); para Julia 0.4, necesitaría Any[[1,2]].
  13. Use la indexación de matriz para obtener el tamaño de una matriz o cadena : cuando se usa enddentro de la indexación de matriz, se convierte automáticamente a la longitud de la matriz / cadena. Por lo tanto, en lugar de k=length(A)utilizar A[k=end]para guardar 3 caracteres. Tenga en cuenta que esto puede no ser beneficioso si desea usar k inmediatamente. Esto también funciona en un caso multidimensional A[k=end,l=end], obtendrá el tamaño de cada dimensión de A, sin embargo, (k,l)=size(A)es más corto en un byte en este caso, así que úselo solo si desea acceder inmediatamente al último elemento al mismo tiempo.
  14. Obtenga un iterador de índice usando la indexación de matriz : similar a 13, también puede obtener un iterador que coincida con la longitud de una matriz usando A[k=1:end], en cuyo caso ktendrá una coincidencia de iterador 1:length(A). Esto puede ser útil cuando desea utilizar también la matriz Aal mismo tiempo.
  15. No use collect para convertir una cadena en una matriz de caracteres ; en lugar de collect(A)use [A...], lo que hará lo mismo y ahorrará 4 bytes.
  16. ¿Necesita un número convertido en una cadena? Use "$(s[i])"o dec(s[i])para expresiones o variables de varios caracteres, y "$i"para variables de un solo carácter.
  17. Use en ?:lugar de &&o ||para la asignación condicional , es decir, si desea realizar una asignación solo en alguna condición, puede guardar un byte escribiendo en cond?A=B:1lugar de cond&&(A=B), o en cond?1:A=Blugar de cond||(A=B). Tenga en cuenta que 1, aquí, es un valor ficticio.
  18. Usar uniono en lugar deunique - union(s)hará lo mismo unique(s)y guardará un byte en el proceso. Si puede usar caracteres no ASCII, ∪(s)hará lo mismo y solo costará 3 bytes en lugar de los 5 bytes union.
Glen O
fuente
2
Oh, cómo me encantaría ese primer truco en Python.
seequ
Puede dividir en espacios usando simplemente split("Hi there")porque el argumento del patrón por defecto es un espacio.
Alex A.
@AlexA. - Lo sé, pero no es el punto de la punta, y la punta se aplica igualmente bien de cualquier manera.
Glen O
El punto 12 ha cambiado en 0.5.
Lyndon White el
@Oxinabox: no estoy sorprendido, estoy bastante seguro de que algunos de ellos ya no están actualizados. Originalmente escribí la mayoría de los consejos para 0.3, creo.
Glen O
15

Redefinir operadores para definir funciones

La redefinición de operadores puede guardar muchos bytes entre paréntesis y comas.

Operadores unarios recursivos

Para un ejemplo unario, compare las siguientes implementaciones recursivas de la secuencia 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

Pruébalo en línea!

El operador redefinido conserva su precedencia inicial.

Tenga en cuenta que no podríamos simplemente cambiar a !favor de ~, ya ~que ya está definido para enteros, mientras !que solo está definido para booleanos.

Operadores binarios

Incluso sin recurrencia, redefinir un operador es más corto que definir una función binaria. Compare las siguientes definiciones de una prueba de divisibilidad 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

Pruébalo en línea!

Operadores binarios recursivos

A continuación se ilustra cómo redefinir un operador binario para calcular la función de 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

Pruébalo en línea!

Tenga en cuenta que ^es incluso más largo que usar un identificador regular, ya que su precedencia es demasiado alta.

Como se mencionó antes

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

no funcionaría para argumentos enteros, ya |que ya está definido en este caso. La definición de enteros se puede cambiar con

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

Pero eso es prohibitivamente largo. Sin embargo, se hace el trabajo si se pasa un flotador como argumento izquierdo y un entero como argumento de la derecha.

Dennis
fuente
11
  1. No se deje seducir fácilmente por el factor (n) La función de biblioteca base tentadora factor(n)tiene un defecto fatal: devuelve la factorización de su entero en un Dicttipo desordenado . Por lo tanto, requiere costosos collect(keys())y collect(values())potencialmente también a caty a sortpara obtener los datos que desea obtener de ellos. En muchos casos, puede ser más barato simplemente factorizar por división de prueba. Triste pero cierto.

  2. Usar el mapa map es una gran alternativa al bucle. Tenga en cuenta la diferencia entre mapy map!explote la funcionalidad in situ de este último cuando pueda.

  3. El uso de mapreduce mapreduce extiende la funcionalidad del mapa aún más y puede ser un importante protector de bytes.

  4. ¡Las funciones anónimas son geniales! ..especialmente cuando se pasa a las mapfunciones antes mencionadas .

  5. Funciones formación acumulativa cumprod , cumsum, la sabrosa cumminy las otras funciones de nombre similar permiten operaciones acumulados a lo largo de una dimensión especificada de una matriz n-dimensional. (O * un * especificado si la matriz es 1-d)

  6. Notación corta para Cualquiera Cuando desee seleccionar todas las dimensiones particulares de una matriz multidimensional (o Dict), por ejemplo A[Any,2], puede guardar bytes utilizandoA[:,2]

  7. Utilice la notación de una sola línea para las funciones. En lugar de function f(x) begin ... endhacerlo, a menudo puede simplificarf(x)=(...)

  8. Utilice el operador ternario Puede ser un ahorrador de espacio para construcciones If-Then-Else de una sola expresión. Advertencias: Si bien es posible en algunos otros idiomas, no puede omitir la porción después del colon en Julia. Además, el operador tiene un nivel de expresión en Julia, por lo que no puede usarlo para la ejecución condicional de bloques completos de código.
    if x<10 then true else false endvs
    x<10?true:false

Jonathan Van Matre
fuente
3
¿Cómo demonios es "usar el operador ternario" algo específico para Julia? Es relevante para cada idioma que lo tiene.
Peter Taylor
55
Es relevante que lo tenga. Muchos idiomas también tienen funciones de mapa, anónimas o puras, algún tipo de taquigrafía para cualquiera / todos, funciones acumulativas, etc. Si redujéramos cada hilo de sugerencias a solo las características absolutamente exclusivas de ese idioma, habría muy poco contenido de sugerencias. .
Jonathan Van Matre
3
Gosh, solo todos los funcionales para empezar, donde todo devuelve un valor, por lo que una operación ternaria sería redundante. Si debe tener ejemplos específicos: Go, Haskell, Scala, Lua, VB, Prolog, PL / SQL ... incluso Python no lo hizo para muchas versiones. De las casi docenas de idiomas cuyos hilos de consejos mencionan a su operador ternario, ¿hay alguna razón por la que solo eliges ser provincial en el mío?
Jonathan Van Matre
3
Bueno, oye, al menos eres un cascarrabias de igualdad de oportunidades. ヘ ( ̄ ー  ̄ ヘ)
Jonathan Van Matre
1
¿Puedo sugerir ajustar la punta 3? mapreduce es más largo que mapfoldl o mapfoldr, y puede tener un comportamiento variable según la implementación. mapfoldl y mapfoldr son asociativos izquierda y derecha (respectivamente) de manera consistente, y por lo tanto son una mejor opción. Esto también se aplica de manera más general para reducir (use foldl o foldr).
Glen O
9

Iterar sobre funciones

Esto también es posible en otros idiomas, pero generalmente es más largo que el método directo. Sin embargo, la capacidad de Julia para redefinir sus operadores unarios y binarios lo hace bastante golfoso.

Por ejemplo, para generar la tabla de suma, resta, multiplicación y división para los números naturales del 1 al 10, se podría usar

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

que redefine el binario operador |como +, -, *y ÷, a continuación, calcula x|ypara cada operación y xy yen los intervalos deseados.

Esto también funciona para operadores unarios. Por ejemplo, para calcular números complejos 1 + 2i , 3-4i , -5 + 6i y -7-8i , sus negativos, sus conjugados complejos y sus inversos multiplicativos, uno podría usar

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

que redefine el unario operador ~como +, -, conjy inv, a continuación, calcula ~xpara todos los números complejos deseados.

Ejemplos en concursos reales

Dennis
fuente
6
  1. Las palabras clave a veces pueden seguir inmediatamente a las constantes sin la necesidad de un espacio o punto y coma. Por ejemplo:

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

    Tenga en cuenta la falta de un espacio entre 1y end. Esto también es cierto para endocurrir después de un parentesco cercano, es decir )end.

  2. Realice la división de enteros utilizando en ÷lugar de div()o sobrecargando un operador. Tenga en cuenta que ÷vale 2 bytes en UTF-8.

  3. Use vec()o A[:](para alguna matriz A) en lugar de reshape()siempre que sea ​​posible.

  4. Cree funciones en lugar de programas completos cuando lo permitan las reglas de desafío. Es más corto definir una función que acepte entradas en lugar de definir variables leyendo desde stdin. Por ejemplo:

    n->(n^2-1)
    n=read(STDIN,Int);n^2-1
  5. Las variables se pueden incrementar dentro del argumento de una función. Por ejemplo, la siguiente es mi respuesta al desafío Encontrar el siguiente número binario escaso 1 :

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

    Esto es más corto que incrementar ndentro del bucle.

Alex A.
fuente
6
  1. No escribirreturn f(x)=x+4 es idéntico pero más corto que f(x)=return x+4. Julia siempre devuelve el resultado de la última declaración.
  2. Use = en lugar de in . [x for x in 1:4]es 3 caracteres más largo que, pero equivalente a[x for x=1:4]
gggg
fuente
5

Usar llamadas de función de difusión.

Introducido en Julia 0.5. Es como un mapa, pero usa menos caracteres y realiza el comportamiento de transmisión sobre sus argumentos, lo que significa que puede escribir menos lambda para lidiar con las cosas.

Más bien que:

  • map(f,x) - 8 personajes.
  • f.(x) - 5 caracteres

Mejor aún:

  • map(a->g(a,y),x) - 16 caracteres
  • g.(x,[y]) - 9 caracteres
Lyndon White
fuente