Как отфильтровать список в J? - PullRequest
19 голосов
/ 19 мая 2010

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

Предположим, у меня есть произвольный список 3 2 2 7 7 2 9, и я хочу удалить 2, но оставлю все остальное без изменений, то есть мой результат будет 3 7 7 9. Как же я могу это сделать?

Ответы [ 4 ]

31 голосов
/ 19 мая 2010

Краткий ответ

   2 (~: # ]) 3 2 2 7 7 2 9
3 7 7 9


Длинный ответ

У меня есть ответ для вас, но прежде чем вы должны ознакомиться с некоторыми деталями. Вот и мы.

Монады, диады

Существует два типа глаголов в J: монады и диады . Первые принимают только один параметр, вторые принимают два параметра.

Например, передача единственного аргумента в монадический глагол #, называемый tally , подсчитывает количество элементов в списке:

   # 3 2 2 7 7 2 9
7

Глагол #, который принимает два аргумента (левый и правый), называется copy , это dyadic и используется для копирования элементов из правого списка столько, сколько время, указанное соответствующими элементами в левом списке (в списке также может быть единственный элемент):

   0 0 0 3 0 0 0 # 3 2 2 7 7 2 9
7 7 7

вилка

Есть понятие fork в J, которое представляет собой серию из 3 глаголов, применяемых к их аргументам, двоично или монадически.

Вот диаграмма вида fork , который я использовал в первом фрагменте:

 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

] ( identity ) может быть монадой или dyad , но он всегда возвращает правильный аргумент, переданный глаголу (есть противоположный глагол, [ который возвращает .. Да, левый аргумент!:)

Пока все хорошо. 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 с.

Ссылки

Немного другой вид вилка , крючок и другие примитивы (включая вышеупомянутые) описаны в этих двух документах:

Другими полезными источниками информации являются сайт Jsoftware с их вики и несколько архивов списков рассылки в интернете.

6 голосов
/ 14 октября 2011

Просто чтобы быть уверенным, что это понятно, прямой способ - ответить на оригинальный вопрос - это:

   3 2 2 7 7 2 9 -. 2

Возвращает

3 7 7 9

Более сложный метод - генерация логического значения и использование его для сжатия вектора - более APLish.

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

      ({. , {. +/ .= ]) 1 4 1 4 2 1 3 5
1 3

Это форк, использующий "{." чтобы получить первый элемент, "{. + /. =]", чтобы сложить число раз, которое первый элемент равен каждому элементу, и "," как средний глагол, чтобы объединить эти две части.

4 голосов
/ 19 октября 2010

Также:

   2 ( -. ~ ]) 3 2 2 7 7 2 9
3 7 7 9
1 голос
/ 16 сентября 2011

Есть миллион способов сделать это - меня смутно беспокоит, что эти вещи не оцениваются строго справа налево, я старый программист 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, как только она ломает и терпит неудачу, и нули выходят.

Я подумал, что мог бы затем добавить еще один набор паренов, снова получить ведущий элемент из списка и каким-то образом записать эти числа, в конечном итоге идея состояла бы в том, чтобы иметь еще одну стадию, где я применяю инверсию вектора к исходному list, а затем используйте $: чтобы вернуться для рекурсивного применения того же глагола. Вроде как пример быстрой сортировки, который, как мне показалось, я понял, но думаю, что нет.

Но я даже не могу подобраться. Я задам это как отдельный вопрос, чтобы люди получали должную оценку за ответы.

...