Consejos para jugar golf en K

17

K es un lenguaje de programación de la familia APL diseñado por Arthur Whitney. Si bien el intérprete oficial es de código cerrado y comercial, se puede encontrar una versión de prueba con un límite de espacio de trabajo de 32 bits de espacio de direccionamiento (que no debería plantear problemas para el golf de código) en el sitio web de Kx Systems . Esta versión incluida como parte de la base de datos kdb + se conoce coloquialmente como "K4". También hay implementaciones K de código abierto disponibles, incluyendo Kona , que se basa en K3, y mi propio intérprete llamado oK , que se basa en K5 y tiene un REPL basado en navegador .

Kx Systems tiene una wiki con información de K4 / kdb + / Q, y la página de Kona GitHub también tiene una excelente colección de materiales de referencia. He comenzado a escribir un manual para oK / k5 que puede ser una referencia útil.

Al igual que J y APL, K es un lenguaje muy conciso y poderoso, y a menudo puede mostrarse bien en código golf. ¡Comparte consejos, trucos y modismos que descubras, y si no has probado K antes, considera darle una vuelta! Publique un consejo por respuesta, por favor!

JohnE
fuente

Respuestas:

5

Llamando a una diada

Suponiendo que tuviera una función diádica (2 argumentos) f:

f: {x+2*y}

Normalmente lo llamarías así:

f[3;47]

Puede guardar un carácter al cursar en el primer argumento y luego aplicar la función parcial resultante al segundo argumento por yuxtaposición:

f[3]47

Lo mismo funciona naturalmente para la indexación de matrices:

  z: (12 17 98;90 91 92)
(12 17 98
 90 91 92)

  z[1;2]
92

  z[1]2
92
JohnE
fuente
5

Imprimir nuevas líneas

Si su salida debe tener una nueva línea, puede verse tentado a hacer esto:

`0:whatever,"\n"

No hacerlo . K2 (y probablemente otras versiones) tiene una característica ordenada donde puede imprimir una lista de líneas:

  `0:("abc";"def")
abc
def

Entonces, si necesita agregar una nueva línea a la salida, simplemente haga lo siguiente:

`0:,whatever
kirbyfan64sos
fuente
3

Rangos

Normalmente, si desea crear un vector de números secuenciales, use !:

  !5
0 1 2 3 4

Si desea crear un rango que comience en un número que no sea cero, entonces agregaría un desplazamiento al vector resultante:

  10+!5
10 11 12 13 14

Hay algunos enfoques inusuales que podrían funcionar mejor para una situación particular. Por ejemplo, si su base y su desplazamiento ya son miembros de una lista, puede usar "where" dos veces:

  &10 5
0 0 0 0 0 0 0 0 0 0 1 1 1 1 1
  &&10 5
10 11 12 13 14

Para secuencias de crecimiento más lento, considere combinar "where" con "take":

  5#2
2 2 2 2 2
  &5#2
0 0 1 1 2 2 3 3 4 4

Si desea crear un rango de múltiplos, puede multiplicar el resultado de !o puede escanear ( \) una lista de copias del tamaño del paso:

  2*!5
0 2 4 6 8
  +\5#2
2 4 6 8 10

Si está tratando de evitar los paréntesis, el primero es mejor si la longitud de la secuencia es variable y el tamaño del paso es fijo, mientras que el segundo es mejor si el tamaño del paso es lo que tiende a variar. Elegir la variación correcta puede guardar 1 o 2 caracteres. La diferencia de uno por uno también podría funcionar a su favor.

JohnE
fuente
2

Los moldes de cuerdas son caros. Solo usa eval. Esta:

0.0$a

puede convertirse en esto:

. a

En K5, es un byte más corto:

.a
kirbyfan64sos
fuente
2

Cada derecho

Ocasionalmente, puede encontrarse escribiendo (o llegando por simplificación) una expresión entre paréntesis aplicada a través de cada mónada:

  (2#)'3 4 5
(3 3
 4 4
 5 5)

Es un carácter más corto para convertir este patrón en una aplicación de cada derecho:

  2#/:3 4 5
(3 3
 4 4
 5 5)
JohnE
fuente
1

Permutaciones Cíclicas

Dyadic !en K3 / K4 es "rotar":

  2!"abcd"
"cdab"
  -1!"abcd"
"dabc"

Cuando "scan" ( \) se proporciona con un verbo monádico, actúa como un operador de punto fijo. En K, los operadores de punto fijo aplican repetidamente su verbo a un valor hasta que se revise el valor inicial o el valor deje de cambiar. La combinación de rotación con exploración de punto fijo proporciona una manera muy conveniente de calcular un conjunto de permutaciones cíclicas de una lista:

  ![1]\1 2 4 8
(1 2 4 8
 2 4 8 1
 4 8 1 2
 8 1 2 4)

Puedes hacer curry !entre paréntesis o paréntesis para crear el tren de verbos (1!):

![1]\
(1!)\

(¡Tenga en cuenta que 1!\tiene un comportamiento completamente diferente!) Cada uno de estos tiene una longitud equivalente, pero el primero puede ser más deseable si la zancada de rotación no es 1; en este caso, los corchetes delimitan una subexpresión entre paréntesis "gratis".

Como ejemplo, aquí hay un programa corto que prueba a través de la fuerza bruta si una cadena x contiene la subcadena y (cíclicamente):

{|/(y~(#y)#)'![1]\x}

Los usuarios de K5 tengan cuidado! K5 ha cambiado el significado de diádico !, por lo que esta técnica no es tan fácil. Funcionará como se esperaba en Kona.

JohnE
fuente
1

Evitar condicionales

K tiene una construcción condicional ( :[) que es equivalente a un estilo Lisp cond:

:[cond1;result1; cond2;result2; cond3;result3; default]

Puede tener tantas condiciones como desee, y si ninguna coincide, se devuelve el valor predeterminado.

A veces (como en los programas recursivos o programas que de otra manera dependen de una secuencia de efectos secundarios), no hay forma de evitar el uso de uno de estos. Sin embargo, en situaciones en las que puede permitirse hacer un poco de trabajo extra, a menudo puede reemplazar un "cond" con indexación de listas.

Considere el infame programa fizzbuzz . Escrito en un estilo de programación imperativo convencional, podríamos ir con:

{:[~x!15;"FizzBuzz";~x!3;"Fizz";~x!5;"Buzz";x]}'1+!100

Hay bastante repetición aquí en las pruebas de divisibilidad. Un enfoque diferente reconoce que hay 4 casos (un número, divisibilidad por solo 3, divisibilidad por solo 5, divisibilidad por 3 y 5) e intenta calcular directamente un índice que elige uno de estos casos de una lista:

{(x;"Fizz";"Buzz";"FizzBuzz")@+/1 2*~x!/:3 5}'1+!100

Dos caracteres más cortos y un mejor uso del lenguaje. Sabiendo que los literales de lista se evalúan de derecha a izquierda, también obtenemos algunas oportunidades de golf adicionales para combinar subexpresiones reutilizadas. No podríamos haber hecho esto fácilmente en la versión basada en cond, ya que los casos de cadena no se evalúan si no se seleccionan:

{(x;4#t;4_ t;t:"FizzBuzz")@+/1 2*~x!/:3 5}'1+!100

Ahora hemos guardado 5 caracteres en general. Por cierto, este ejemplo en particular funciona aún mejor en k5, ya que tenemos la sobrecarga del "paquete" para /manejar el paso de multiplicar por un vector de coeficientes y sumar:

{(x;4_t;4#t;t:"FizzBuzz")@2 2/~3 5!\:x}'1+!100

También tenga en cuenta que el comportamiento de "find" ( ?), que produce un índice más allá del final de la lista de claves si no se encuentra el elemento, está específicamente diseñado para admitir el manejo de un caso "predeterminado" en este tipo de indexación. Considere este fragmento para convertir vocales a mayúsculas:

{("AEIOU",x)"aeiou"?x}'

Versus uno de:

{t:"aeiou"?x;:[t<5;"AEIOU"t;x]}'
{:[~4<t:"aeiou"?x;"AEIOU"t;x]}'

(¡Sé cuál prefiero leer también!)

JohnE
fuente