系统地从 J 表达式中提取名词参数

Systematically extract noun arguments from J expression

从 J 中的表达式中提取名词作为参数的系统方法是什么?明确地说,包含两个文字的表达式应该成为二元表达式,使用左右参数而不是文字。

我正在尝试学习默示风格,所以如果可以避免的话,我宁愿不使用命名变量。

一个具体的例子是我制作的一个简单的骰子模拟器:

   >:?10#6    NB. Roll ten six sided dice.
2 2 6 5 3 6 4 5 4 3
   >:?10#6
2 1 2 4 3 1 3 1 5 4

我想系统地将参数 10 和 6 提取到表达式的外部,以便它可以掷任意数量的任意大小的骰子:

   d =. <new expression here>
   10 d 6  NB. Roll ten six sided dice.
1 6 4 6 6 1 5 2 3 4
   3 d 100  NB. Roll three one hundred sided dice.
7 27 74

请随意使用我的示例进行说明,但我希望能够遵循任意表达式的过程。

编辑:我刚刚发现使用 x 和 y 的引用版本可以自动转换为默认形式,例如使用13 : '>:?x#y'。如果有人可以告诉我如何找到 13 : 的定义,我也许可以回答我自己的问题。

13 : 记录在 :(显式)下的 vocabulary or NuVoc 中。

基本思路是,你想要的值x变成[,你想要的值y变成]。但是一旦最右边的token从名词(值)变成了动词,比如[或者],整个语句就变成了火车,你可能需要用到动词[: 或连词 @@: 以恢复您之前的组合行为。

您也可以将值替换为实际名称 xy,然后将整个内容包裹在 ((dyad : ' ... ')) 中。即:

>:?10#6    NB. Roll ten six sided dice.

可以变成:

10 (dyad : '>: ? x # y') 6  NB. dyad is predefined. It's just 4.

如果只需要y参数,可以使用monad,预定义为3。名字 verb 也是 3。当我同时提供单子和二元版本时,我倾向于使用 verb :,而当我只需要单子含义时,我倾向于使用 monad

如果你的动词是这样的单行,你可以有时通过将34替换为自动将其转换为默认形式13

我有一些 notes on factoring verbs in j 可以帮助您进行逐步转换。

附录:用于将语句转换为默认二元组的伪代码

这仅涵盖单个语句(一行代码),如果您尝试提取的常量值被传递给连词或副词,则可能不起作用。

此外,该语句不得引用其他变量。

  • [ x=. xVal [ y =. yVal 附加到语句。
  • 用适当的值替换 xValyVal
  • 根据新的 xy 重写原始表达式。
  • 重写statement [ x=. xVal [ y=. yVal为:

newVerb =: (4 : 0)
  statement ] y   NB. we'll fill in x later.
)
(xVal) newVerb yVal

现在您有了 xy 的明确定义。将它放在多行而不是使用 x (4 : 'expr') y 的原因是如果 expr 仍然包含字符串文字,您将不得不 fiddle 转义单引号。

转换第一个名词

因为你之前只有管道,所以statement里面最右边的表达式一定是名词。使用以下规则将其转换为分叉:

  • y(])
  • x]x ([)
  • _, __, _9 ... 9(_:), (__:), (_9:) .. . (9:)
  • nn"_(对于任何其他任意名词)

这使整体含义保持不变,因为您刚刚创建的动词会立即被调用并应用于 [ y

无论如何,括号中的这个新默认动词将成为您将要建造的火车的核心。从这里开始,您将使用语句中最右边的表达式,并将其移到括号内。

分叉范式

从现在开始,我们将假设我们创建的默认动词始终是叉子。

这个新的默认动词实际上不是叉子,但我们会假装它是,因为任何单标记动词都可以使用以下规则重写为叉子:

v → ([: ] v).

没有理由实际进行此转换,只是为了简化下面的规则并始终将其称为叉子。

我们不会使用钩子,因为任何钩子都可以用规则重写为叉子:

(u v) → (] u [: v ])

下面的规则应该会自动生成这种形式的火车。

转换剩余代币

现在我们可以使用以下规则转换原始管道的其余部分,一次将一个项目移动到分支中。

对于所有这些规则,(]x)? 不是 J 语法。这意味着 ]x 可能存在也可能不存在。在不更改代码含义的情况下转换 x 的用法之前,您不能将 ] x 放入。转换 x 的实例后,需要 ]x

遵循J约定,uv表示任意动词,n是任意名词。请注意,这些包括动词

tokens y u (]x)? (fork) ] y  →  tokens   (]x)? (]  u fork) ] y
tokens x u (]x)? (fork) ] y  →  tokens    ]x   ([  u fork) ] y
tokens n u (]x)? (fork) ] y  →  tokens   (]x)? (n  u fork) ] y
tokens u v (]x)? (fork) ] y  →  tokens u (]x)? ([: v fork) ] y

副词或连词没有规则,因为您应该将它们视为动词的一部分。例如 +:^:3 应被视为单个动词。同样,括号中的任何内容都应单独作为一个短语。

无论如何,请继续应用这些规则,直到 运行 没有令牌为止。

清理

你应该得到:

newVerb =: (4 : 0)
  ] x (fork) ] y
)
(xVal) newVerb yVal

这可以重写为:

(xVal) (fork) yVal

大功告成。

如果您的目标是学习隐式风格,最好从头开始学习它,而不是试图记住一个明确的算法——J4C and Learning J 是很好的资源——因为 将表达式从显式转换为默认的一般情况是棘手的.

即使忽略自J4以来没有默认连词的规定,在动词的显式定义中你可以(1)使用control words,(2)使用和修改全局变量,( 3) 将包含 x and/or y 的表达式作为副词或连词的操作数,以及 (4) 引用自身。在一般情况下,解决 (1)、(3) 或 (4) 非常困难,而 (2) 则完全不可能。*

如果你的 J 句子是一小部分 class 表达式中的一个,有一种简单的方法可以应用分叉规则使其默认 ,这就是或多或少是 13 : 中实现的。回想一下

  • (F G H) y(F y) G (H y)x (F G H) y(x F y) G (x H y)(Monad/Dyad叉)
  • ([: G H) yG (H y)x ([: G H) yG (x H y)Monad/Dyad Capped Fork
  • x [ yxx ] yy[ y] y都是yLeft/Right)

注意 forks 如何使用它们的中心动词作为 'outermost' 动词:Fork 给出了 g 的二元应用,而 Capped Fork 给出了一个单子的。这恰好对应于 J 中动词的两种应用模式,一元的和二元的。因此,对于 F G H 动词和 N 名词,使 "dyadic" 表达式默认的快速算法可能如下所示:

  1. x 替换为 (x [ y),将 y 替换为 (x ] y)。 (Left/Right)
  2. 将任何其他名词 n 替换为 (x N"_ y)
  3. 如果您看到模式 (x F y) G (x H y),请将其替换为 x (F G H) y。 (分叉)
  4. 如果您看到模式 G (x H y),请将其替换为 x ([: G H) y。 (*加盖叉子()
  5. 重复 1 到 4,直到您获得 x F y 形式,此时您获胜。
  6. 如果无法进行更多的简化并且您还没有获胜,那么您就输了。

可以为"monadic expressions"导出类似的算法,表达式仅依赖于y。这是一个示例推导。

<. (y - x | y) % x                          NB. start
<. ((x ] y) - (x [ y) | (x ] y)) % (x [ y)  NB. 1
<. ((x ] y) - (x ([ | ]) y)) % (x [ y)      NB. 3
<. (x (] - ([ | ])) y) % (x [ y)            NB. 3
<. x ((] - ([ | ])) % [) y                  NB. 3
x ([: <. ((] - ([ | ])) % [)) y             NB. 4 and we win

这忽略了一些明显的简化,但达到了目标。您可以混合使用其他各种规则来简化,例如长火车规则——如果 Train 是奇数长度的火车,则 (F G (Train)) 等价于 (F G Train)——或者 x ([ F ]) yx F y 是等价的。学习规则后,修改算法得到结果[: <. [ %~ ] - |应该不难,也就是13 : '<. (y - x | y) % x'给出的

只要包含 x and/or y 的表达式是副词或连词的操作数,就会达到失败条件。有时可以通过一些深度重构以及对 ^:} 的动词和动名词形式的了解来恢复默认形式,但我怀疑这是否可以通过编程方式完成。

这就是使 (1)、(3) 和 (4) 变得困难而不是不可能的原因。有了 $: 工作原理的知识,默认程序员可以毫不费力地找到 Ackermann function 的默认形式,聪明的程序员甚至可以重构它以提高效率。如果你能找到一个算法来做到这一点,你就会避免程序员,句号。

   ack1 =: (1 + ])`(([ - 1:) $: 1:)`(([ - 1:) $: [ $: ] - 1:)@.(, i. 0:)
   ack2 =: $: ^: (<:@[`]`1:) ^: (0 < [) >:
   3 (ack1, ack2) 3
61 61
   TimeSpace =: 6!:2, 7!:2@]  NB. iterations TimeSpace code
   10 TimeSpace '3 ack1 8'
2.01708 853504
   10 TimeSpace '3 ack2 8'
0.937484 10368

* 这是一个谎言。你可以通过一些高级的巫毒魔法重构涉及这样一个动词的整个程序,cf。 Pepe Quintana's talk at the 2012 J Conference。不好看