Как отфильтровать список в J?
В настоящее время я изучаю увлекательный язык программирования J, но я не смог понять, как отфильтровать список.
Предположим, у меня есть произвольный список 3 2 2 7 7 2 9
и я хочу удалить 2s, но оставить все остальное без изменений, т. е. мой результат будет 3 7 7 9
, Как же я могу это сделать?
4 ответа
Краткий ответ
2 (~: # ]) 3 2 2 7 7 2 9
3 7 7 9
Длинный ответ
У меня есть ответ для вас, но прежде чем вы должны ознакомиться с некоторыми деталями. Вот так.
Монады, диады
Есть два типа глаголов в J: монады и диады. Первые принимают только один параметр, последние принимают два параметра.
Например, передача единственного аргумента монадическому глаголу #
Вызванный tally подсчитывает количество элементов в списке:
# 3 2 2 7 7 2 9
7
Глагол #
, который принимает два аргумента (левый и правый), называется копией, он двоичный и используется для копирования элементов из правого списка столько раз, сколько указано соответствующими элементами в левом списке (может быть единственный элемент в список также):
0 0 0 3 0 0 0 # 3 2 2 7 7 2 9
7 7 7
вилка
В J есть понятие форка, который представляет собой серию из 3 глаголов, применяемых к их аргументам, двоично или монадически.
Вот диаграмма вида вилки, который я использовал в первом фрагменте:
x (F G H) y
G
/ \
F H
/ \ / \
x y x y
Он описывает порядок, в котором глаголы применяются к их аргументам. Таким образом, эти приложения происходят:
2 ~: 3 2 2 7 7 2 9
1 0 0 1 1 0 1
~:
(не равно) является двоичным в этом примере и приводит к списку логических значений, которые являются истинными, когда аргумент не равен 2
, Это был F
применение по схеме.
Следующее приложение H
:
2 ] 3 2 2 7 7 2 9
3 2 2 7 7 2 9
]
(тождество) может быть монадой или диадой, но оно всегда возвращает правильный аргумент, переданный глаголу (есть противоположный глагол, [
который возвращает.. Да, левый аргумент!:)
Все идет нормально. F
а также H
после того, как приложение вернуло эти значения соответственно:
1 0 0 1 1 0 1
3 2 2 7 7 2 9
Единственный шаг для выполнения G
приложение глагола.
Как я уже отмечал ранее, глагол #
, который является двоичным (принимает два аргумента), позволяет нам дублировать элементы из правого аргумента столько раз, сколько указано в соответствующих позициях левого аргумента. Следовательно:
1 0 0 1 1 0 1 # 3 2 2 7 7 2 9
3 7 7 9
Мы только что отфильтровали список 2
s.
Ссылка
Слегка различный вид вилки, крючка и других примитивов (включая вышеупомянутые) описаны в этих двух документах:
- Краткий J Ссылка (175 КиБ)
- Легко-J. Введение в самый замечательный в мире язык программирования (302 КиБ)
Другими полезными источниками информации являются сайт Jsoftware с их вики и несколькими архивами списков рассылки в интернете.
Просто чтобы убедиться, что это понятно, прямой способ - ответить на оригинальный вопрос - это:
3 2 2 7 7 2 9 -. 2
Это возвращает
3 7 7 9
Более сложный метод - генерация логического значения и использование его для сжатия вектора - более APLish.
Чтобы ответить на другой вопрос в очень длинном посте, чтобы вернуть первый элемент и количество раз, которое это произошло, просто так:
({. , {. +/ .= ]) 1 4 1 4 2 1 3 5
1 3
Это вилка, использующая "{." чтобы получить первый элемент, "{. +/ . = ]" для суммирования числа раз, которое первый элемент равен каждому элементу, и "," в качестве среднего глагола для объединения этих двух частей.
Есть миллион способов сделать это - смутно меня беспокоит, что эти вещи не оцениваются строго справа налево, я старый программист APL и считаю вещи справа налево, даже если они не,
Если бы это было то, что я собирался поместить в программу, где я хотел бы извлечь какое-то число, а число было константой, я бы сделал следующее:
(#~ 2&~:) 1 3 2 4 2 5
1 3 4 5
Я думаю, это что-то вроде крючка. Правая половина выражения генерирует вектор истинности, который не равен 2, и тогда у октоторпа слева свои аргументы меняются местами так, что вектор истинности является левым аргументом для копирования, а вектор - правым аргументом. Я не уверен, что ловушка быстрее или медленнее, чем форк с копией аргумента.
+/3<+/"1(=2&{"1)/:~S:_1{;/5 6$1+i.6
156
Эта программа отвечает на вопрос: "Для всех возможных комбинаций костей Yatzee, сколько из 4 или 5 совпадающих чисел в одном броске?" Он генерирует все перестановки в блоках, сортирует каждый блок по отдельности, распаковывает их как побочный эффект и извлекает столбец 2, сравнивая блок с их собственным столбцом 2, в единственном успешном форке или хуке, которые мне когда-либо удавалось написать. Теория состоит в том, что, если есть число, которое появляется в списке 5, три или более раз, при сортировке списка среднее число будет числом, которое появляется с наибольшей частотой. Я пробовал несколько других хуков и / или вилок, и каждый потерпел неудачу, потому что есть кое-что, чего я просто не получаю. В любом случае эта таблица истинности сводится к вектору, и теперь мы точно знаем, сколько раз каждая группа из 5 кубиков соответствовала срединному числу. Наконец, это число сравнивается с 3, и подсчитывается количество успешных сравнений (больше 3, то есть 4 или 5).
Эта программа отвечает на вопрос: "Для всех возможных 8-значных чисел, сделанных из символов с 1 по 5, с повторением, сколько делится на 4?"
Я знаю, что вам нужно только определить, сколько из первых 25 делится на 4 и умножается, но программа запускается более или менее мгновенно. В какой-то момент у меня была гораздо более сложная версия этой программы, которая генерировала числа в базе 5 так, чтобы отдельные цифры были между 0 и 4, добавляла 1 к сгенерированным таким образом числам, а затем помещала их в базу 10. Это было что-то вроде 1+(8$5)#:i.5^8
+/0=4|,(8$10)#. >{;/ 8 5$1+i.5
78125 Пока у меня есть только глагольные поезда и отбор, у меня нет проблем. Когда я начинаю повторять свой аргумент в глаголе, чтобы меня заставили использовать вилки и крючки, я начинаю терять.
Например, вот что я не могу заставить работать.
((1&{~+/)*./\(=1&{))1 1 1 3 2 4 1
Я всегда получаю ошибку индекса.
Смысл состоит в том, чтобы вывести два числа, одно из которых совпадает с первым числом в списке, второе - с количеством повторений этого числа.
Так что это много работает:
*./\(=1&{)1 1 1 3 2 4 1
1 1 1 0 0 0 0
Я сравниваю первое число с остальной частью списка. Затем я делаю вставку и сжатие - и это дает мне 1, пока у меня есть непрерывная строка 1, как только она ломает и терпит неудачу, и нули выходят.
Я подумал, что мог бы затем добавить еще один набор паренов, снова получить ведущий элемент из списка и каким-то образом записать эти числа, в конечном итоге идея состояла бы в том, чтобы иметь другой этап, на котором я применяю инверсию вектора к исходному списку, и затем используйте $: чтобы вернуться для рекурсивного применения того же глагола. Вроде как пример быстрой сортировки, который, как мне показалось, я понял, но думаю, что нет.
Но я даже не могу подобраться. Я задам это как отдельный вопрос, чтобы люди получали должную оценку за ответы.