Conseils pour jouer au golf à K

17

K est un langage de programmation de la famille APL conçu par Arthur Whitney. Alors que l'interprète officiel est de source fermée et commercial, une version d'essai avec une limite d'espace de travail de 32 bits d'espace d'adressage (qui ne devrait pas poser de problème pour le golf de code) peut être trouvée sur le site Web de Kx Systems . Cette version intégrée à la base de données kdb + est connue sous le nom de "K4". Il existe également des implémentations K open source, y compris Kona , qui est basé sur K3, et mon propre interprète appelé oK , qui est basé sur K5 et dispose d'un navigateur REPL .

Kx Systems possède un wiki avec des informations K4 / kdb + / Q, et la page Kona GitHub possède également une excellente collection de documents de référence. J'ai commencé à écrire un manuel pour oK / k5 qui peut être une référence utile.

Comme J et APL, K est un langage très laconique et puissant, et peut souvent faire bonne figure dans le golf de code. Veuillez partager des trucs, astuces et idiomes que vous découvrez, et si vous n'avez pas essayé K avant, pensez à lui donner un tour! Postez un conseil par réponse, s'il vous plaît!

JohnE
la source

Réponses:

5

Appeler une dyade

En supposant que vous disposiez d'une fonction dyadique (2 arguments) f:

f: {x+2*y}

Vous l'appeleriez normalement ainsi:

f[3;47]

Vous pouvez enregistrer un caractère en curry dans le premier argument puis en appliquant la fonction partielle résultante au deuxième argument par juxtaposition:

f[3]47

La même chose fonctionne naturellement pour l'indexation de tableaux:

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

  z[1;2]
92

  z[1]2
92
JohnE
la source
5

Impression de nouvelles lignes

Si votre sortie doit avoir une nouvelle ligne, vous pourriez être tenté de le faire:

`0:whatever,"\n"

Non . K2 (et probablement d'autres versions) a une fonctionnalité intéressante où vous pouvez imprimer une liste de lignes:

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

Donc, si vous devez ajouter une nouvelle ligne à la sortie, faites simplement:

`0:,whatever
kirbyfan64sos
la source
3

Gammes

Normalement, si vous souhaitez créer un vecteur de nombres séquentiels, vous utilisez !:

  !5
0 1 2 3 4

Si vous souhaitez créer une plage commençant par un nombre différent de zéro, vous devez ensuite ajouter un décalage au vecteur résultant:

  10+!5
10 11 12 13 14

Il existe quelques approches inhabituelles qui pourraient mieux fonctionner dans une situation particulière. Par exemple, si votre base et votre décalage sont déjà membres d'une liste, vous pouvez utiliser "où" deux fois:

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

Pour les séquences à croissance plus lente, envisagez de combiner "où" avec "prendre":

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

Si vous souhaitez créer une plage de multiples, vous pouvez multiplier le résultat de !ou vous pouvez numériser ( \) une liste de copies de la taille de l'étape:

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

Si vous essayez d'éviter les parenthèses, la première est meilleure si la longueur de la séquence est variable et la taille du pas est fixe, tandis que la seconde est meilleure si la taille du pas est ce qui a tendance à varier. Choisir la bonne variation peut sauver 1 ou 2 caractères. La différence au cas par cas pourrait également jouer en votre faveur.

JohnE
la source
2

Les moulages à partir de cordes sont chers. Utilisez simplement eval. Cette:

0.0$a

peut devenir juste ceci:

. a

En K5, c'est un octet plus court:

.a
kirbyfan64sos
la source
2

Chaque droit

Parfois, vous pouvez vous retrouver à écrire (ou à arriver par simplification) une expression entre parenthèses appliquée via each-monad:

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

C'est un caractère plus court pour convertir ce modèle en une application de chaque droit:

  2#/:3 4 5
(3 3
 4 4
 5 5)
JohnE
la source
1

Permutations cycliques

Dyadique !en K3 / K4 est "tourner":

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

Lorsque "scan" ( \) est fourni avec un verbe monadique, il agit comme un opérateur à virgule fixe. En K, les opérateurs à virgule fixe appliquent à plusieurs reprises leur verbe à une valeur jusqu'à ce que la valeur initiale soit réexaminée ou que la valeur cesse de changer. La combinaison de la rotation et du balayage à virgule fixe offre un moyen très pratique de calculer un ensemble de permutations cycliques d'une liste:

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

Vous pouvez soit curry !via des crochets ou entre parenthèses pour créer le train de verbes (1!):

![1]\
(1!)\

(Notez que le 1!\comportement est entièrement différent!) Chacun d'eux est de longueur équivalente mais le premier peut être plus souhaitable si la foulée de rotation est autre que 1; dans ce cas, les parenthèses délimitent une sous-expression entre parenthèses "gratuitement".

À titre d'exemple, voici un programme court qui teste par force brute si une chaîne x contient la sous-chaîne y (cycliquement!):

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

Attention aux utilisateurs de K5! K5 a changé le sens de dyadique !, donc cette technique n'est pas si facile. Cela fonctionnera comme prévu à Kona.

JohnE
la source
1

Évitez les conditions

K a une construction conditionnelle ( :[) qui est équivalente à un style Lisp cond:

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

Vous pouvez avoir autant de conditions que vous le souhaitez et si aucune ne correspond, la valeur par défaut est renvoyée.

Parfois (comme dans les programmes récursifs ou les programmes qui reposent autrement sur une séquence d'effets secondaires), il est impossible de contourner l'utilisation de l'un d'eux. Cependant, dans les situations où vous pouvez vous permettre de faire un peu de travail supplémentaire, vous pouvez souvent remplacer un "cond" par une indexation de liste.

Considérez le fameux programme fizzbuzz . Écrit dans un style de programmation impératif conventionnel, nous pourrions aller avec:

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

Il y a pas mal de répétition ici dans les tests de divisibilité. Une approche différente reconnaît qu'il y a 4 cas (un nombre, divisibilité par seulement 3, divisibilité par seulement 5, divisibilité par 3 et 5) et tente de calculer directement un indice qui choisit l'un de ces cas dans une liste:

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

Deux caractères plus courts, et une meilleure utilisation de la langue. Sachant que les littéraux de liste sont évalués de droite à gauche, nous gagnons également des opportunités de golf supplémentaires pour combiner des sous-expressions réutilisées. Nous n'aurions pas pu le faire facilement dans la version basée sur les conditions, car les cas de chaîne ne sont pas évalués du tout s'ils ne sont pas sélectionnés:

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

Maintenant, nous avons enregistré 5 caractères au total. Soit dit en passant, cet exemple particulier fonctionne encore mieux en k5, car nous avons la surcharge "pack" pour /gérer l'étape de multiplication par un vecteur de coefficients et de sommation:

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

Notez également que le comportement de "find" ( ?), qui produit un index après la fin de la liste de clés si l'élément n'est pas trouvé, est spécifiquement conçu pour prendre en charge la gestion d'un cas "par défaut" dans ce type d'indexation. Considérez ce fragment pour convertir les voyelles en majuscules:

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

Vers l'un des:

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

(Je sais que je préfère lire aussi!)

JohnE
la source