如何在J中过滤列表?

23

我目前正在学习引人入胜的J编程语言,但有一件事情我还没能弄清楚,那就是如何筛选列表。

假设我有一个任意的列表3 2 2 7 7 2 9,我想要删除其中的2,但保留其他所有元素,即我的结果应该是3 7 7 9。我该怎么做呢?

4个回答

40

简短回答

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


长答案

我可以给你答案,但在此之前你需要了解一些细节。我们开始吧。

单子、双子

J语言有两种类型的动词:单子和双子。前者只接受一个参数,后者接受两个参数。

例如,将参数传递给单子动词#,也称为tally,会计算列表中元素的数量:

   # 3 2 2 7 7 2 9
7

一个名为copydyadic动词(接受左和右两个参数), 用于将右侧列表中的元素根据左侧列表相应的元素指定的次数进行复制(列表中也可以只有一个元素):

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

分叉

J语言中有一个概念叫做“分叉”,它是将三个动词分别应用于它们的参数,可以使用二元或一元方式。

下面是我在第一个片段中使用的某种类型的分叉的图示:

 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) 可以是一个单子双子,但它总是返回传递给动词的右参数(有一个相反的动词[会返回...没错,就是左参数! :)

目前为止,一切正常。 应用后的FH分别返回这些值:

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 的列表。

参考资料

这两个文档描述了稍微不同类型的 forkhook 和其他基本操作(包括上述内容):

其他有用的信息来源是 Jsoftware 网站 以及互联网上的一些邮件列表档案和维基


6
J 确实是一种巧妙的语言。 - Gregory Higley
我看了你的例子,并且一直在尝试。例如,如果要找到1到1000之间所有可以被3整除的数字,可以使用 3 ((|=0:)#]) 1+i.1000(使用两个强大的二元分叉符号),等等。非常有用! - Gregory Higley
@yasir - 我理解你想要更多的声望,但这并不是正确的方法。如果你是唯一的编辑者,我建议向管理员发出标记或发送电子邮件给团队以重置社区维基状态,而不是添加一个包含旧答案内容的新答案到问题中。 - tvanfosson
@tv:好的好的,我是新手,谢谢,已经把一切都弄回来了,只是不知道有8个最大版本。 :) - YasirA
很抱歉,我没有“非维基”按钮。它并不存在。 - Marc Gravell
很好的解释!+1。虽然(#~ ~:&2)3 2 2 7 7 2 9对我来说更容易一些。 - defhlt

9

为了确保清晰明了,回答原问题的直接方法如下:

   3 2 2 7 7 2 9 -. 2

这将返回:

这将返回

3 7 7 9

更加复杂的方法——生成布尔值并使用它来压缩向量——更符合APL的风格。
回答长帖中的另一个问题,返回第一个元素和它出现的次数,只需这样做:
      ({. , {. +/ .= ]) 1 4 1 4 2 1 3 5
1 3

这是一个使用"{."获取第一个项目,"{. +/ . = ]"计算第一个项目等于每个元素的次数,并使用","作为中间动词将这两部分连接起来的分叉。

5
同时:
   2 ( -. ~ ]) 3 2 2 7 7 2 9
3 7 7 9

5
实际上,我认为你只需要说“2 -.~ 3 2 2 7 7 2 9”。 - Gregory Higley

2

有许多方法可以做到这一点 - 它让我感到不安,模糊地感觉这些事情没有严格按从右到左的顺序进行评估,我是一个老APL程序员,即使它们不是从右到左,我也会把它们想象成从右到左。

如果我要将其放入程序中并想要提取某个常数数字,我会执行以下操作:

(#~ 2&~:)  1 3 2 4 2 5
1 3 4 5 

我认为这是一种钩子排序。表达式的右半部分生成关于哪些不是2的真实向量,然后左边的井号交换其参数,以便真实向量成为复制的左参数,而向量成为右参数。我不确定使用钩子是否比带有参数副本的叉更快或更慢。

  +/3<+/"1(=2&{"1)/:~S:_1{;/5 6$1+i.6

这个程序回答了一个问题:“对于所有可能的Yahtzee骰子组合,有多少在一次掷骰中有4或5个匹配数字?”它生成所有排列组合,将它们放入盒子中并分别排序每个盒子,作为副作用取消盒子,并提取第2列,将其与自己的第2列进行比较,在我成功编写的唯一一个fork或hook中。理论是,如果有一个数字在一个由5个数字组成的列表中出现了三次或更多次,如果你对列表进行排序,中间的数字将是出现频率最高的数字。我尝试过许多其他的钩子和/或叉子,但每一个都失败了,因为有些东西我就是不明白。无论如何,那个真值表被简化为一个向量,现在我们知道每组5个骰子匹配中位数的次数。最后,将该数字与3进行比较,并计算成功比较的次数(大于3,即4或5)。

这个程序回答了一个问题:“对于使用1到5的符号重复制成的所有可能的8位数字,有多少能够被4整除?”

我知道你只需要确定前25个数字中有多少个能被4整除并相乘,但程序几乎可以立即运行。曾经我有一个更复杂的版本,它以5为基数生成数字,因此单个数字介于0和4之间,在生成的数字上加1,然后将它们放入10进制。那是类似于1+(8$5)#:i.5^8+ / 0 = 4 |,(8 $ 10)#。 > {;/8 5 $ 1 + i.5 78125 只要我只有动词训练和选择,我就没有问题。当我开始不得不在动词内重复我的参数,以至于我被迫使用forks和hooks时,我开始迷失方向。

例如,这里有一些我无法使其工作的东西。

((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,一旦它断开,and操作失败并出现0。
我认为我可以添加另一组括号,再次从列表中获取前导元素,并以某种方式记录这些数字,最终的想法是在另一个阶段应用向量的逆向于原始列表,然后使用$:来实现对同一动词的递归应用。就像快速排序示例一样,我认为我有点理解,但我想我不懂。
但我甚至无法接近。我将此作为单独的问题提问,以便人们获得正确的答案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接