Советы по игре в гольф в К


17

K - это язык программирования в семействе APL, разработанный Артуром Уитни. В то время как официальный переводчик является закрытым исходным кодом и коммерческим, на веб-сайте Kx Systems можно найти пробную версию с ограничением рабочего пространства в 32 бита адресного пространства (что не должно создавать проблем для кода гольфа) . Эта версия, входящая в состав базы данных kdb +, в разговорной речи называется «K4». Доступны также реализации K с открытым исходным кодом, в том числе Kona , основанный на K3, и мой собственный интерпретатор, называемый oK , основанный на K5 и имеющий REPL на основе браузера .

В Kx Systems есть вики с информацией о K4 / kdb + / Q, а на странице Kona GitHub также есть отличная коллекция справочных материалов. Я начал писать руководство для oK / k5, которое может быть полезным справочным материалом.

Как и J и APL, K - очень лаконичный и мощный язык, и он часто может хорошо показывать себя в код-гольфе. Пожалуйста, поделитесь советами, уловками и идиомами, которые вы обнаружите, и если вы еще не пробовали K, подумайте об этом! Отправьте один совет за ответ, пожалуйста!

Ответы:


5

Вызов диады

Предполагая, что у вас есть двоичная функция (2 аргумента) f:

f: {x+2*y}

Вы бы обычно назвали это так:

f[3;47]

Вы можете сохранить символ, вместо этого каррируя в первом аргументе, а затем применив полученную частичную функцию ко второму аргументу путем сопоставления:

f[3]47

То же самое естественно работает для индексации массива:

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

  z[1;2]
92

  z[1]2
92

5

Печать новых строк

Если ваш вывод должен иметь новую строку, вы можете испытать искушение сделать это:

`0:whatever,"\n"

Не . K2 (и, вероятно, другие версии) имеет удобную функцию, где вы можете напечатать список строк:

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

Итак, если вам нужно добавить новую строку в вывод, просто выполните:

`0:,whatever

3

Изменяется

Обычно, если вы хотите создать вектор последовательных чисел, которые вы используете !:

  !5
0 1 2 3 4

Если вы хотите создать диапазон, который начинается с номера, отличного от нуля, вы должны добавить смещение к результирующему вектору:

  10+!5
10 11 12 13 14

Есть несколько необычных подходов, которые могут работать лучше для конкретной ситуации. Например, если ваша база и смещение уже являются членами списка, вы можете использовать «где» дважды:

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

Для более медленно растущих последовательностей, попробуйте объединить «где» с «взять»:

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

Если вы хотите создать диапазон, кратный результату, вы можете умножить результат !или отсканировать ( \) список копий с размером шага:

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

Если вы пытаетесь избежать скобок, первое лучше, если длина последовательности является переменной, а размер шага фиксированным, а второе - лучше, если размер шага имеет тенденцию изменяться. Выбор правильного варианта может сохранить 1 или 2 символа. Разница между ними может также работать в вашу пользу.


2

Броски из струн стоят дорого. Просто используйте Eval. Эта:

0.0$a

может стать именно таким:

. a

В К5 это на байт короче:

.a

2

Каждое Право

Иногда вы можете написать (или прийти к упрощению) выражение в скобках, примененное через каждую монаду:

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

Это один символ короче, чтобы преобразовать этот шаблон в приложение каждого права:

  2#/:3 4 5
(3 3
 4 4
 5 5)

1

Циклические перестановки

Дядька !в К3 / К4 "вращается":

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

Когда «scan» ( \) содержит монадический глагол, он действует как оператор с фиксированной запятой. В K операторы с фиксированной точкой неоднократно применяют свой глагол к значению, пока начальное значение не будет пересмотрено или значение не перестанет изменяться. Сочетание поворота и сканирования с фиксированной точкой обеспечивает очень удобный способ вычисления набора циклических перестановок списка:

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

Вы можете использовать карри в !скобках или в скобках, чтобы создать последовательность глаголов (1!):

![1]\
(1!)\

(Обратите внимание, что у 1!\него совершенно другое поведение!) Каждый из них эквивалентен по длине, но первый может быть более желательным, если шаг вращения отличается от 1; в этом случае скобки разделяют заключенное в скобки подвыражение «бесплатно».

В качестве примера, вот короткая программа, которая проверяет методом грубой силы, содержит ли строка x подстроку y (циклически!):

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

Пользователи K5 остерегаются! К5 изменил значение диадического !, поэтому эта техника не так проста. Это будет работать как положено в Kona.


1

Избегайте условных выражений

K имеет условную конструкцию ( :[), которая эквивалентна Lisp-стилю cond:

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

Вы можете иметь столько условий, сколько хотите, и, если ни одно из них не соответствует значению по умолчанию, возвращается.

Иногда (как в рекурсивных программах или программах, которые в противном случае полагаются на последовательность побочных эффектов), использование одного из них невозможно обойти. Однако в ситуациях, когда вы можете позволить себе немного проделать дополнительную работу, вы часто можете заменить «cond» индексированием списка.

Рассмотрим печально известную программу fizzbuzz . Написанный в традиционном императивном стиле программирования, мы могли бы пойти с:

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

Здесь довольно много повторений в тестах делимости. Другой подход признает, что существует 4 случая (число, делимость только на 3, делимость только на 5, делимость на 3 и 5) и пытается непосредственно вычислить индекс, который выбирает один из этих случаев из списка:

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

Два символа короче, и лучше использовать язык. Зная, что литералы списка оцениваются справа налево, мы также получаем некоторые дополнительные возможности игры в гольф для объединения повторных подвыражений. Мы не могли бы легко сделать это в версии на основе cond, поскольку строковые регистры вообще не оцениваются, если они не выбраны:

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

Теперь мы сохранили всего 5 символов. Кстати, этот конкретный пример работает еще лучше в k5, поскольку у нас есть перегрузка «pack» для /обработки шага умножения на вектор коэффициентов и суммирования:

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

Также обратите внимание, что поведение «find» ( ?), которое создает индекс после конца списка ключей, если элемент не найден, специально разработано для поддержки обработки случая «по умолчанию» в этом типе индексации. Рассмотрим этот фрагмент, чтобы преобразовать гласные в верхний регистр:

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

По сравнению с одним из:

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

(Я знаю, что я тоже предпочел бы прочитать!)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.