阅读J语言代码的最佳策略

14
我已经使用J语言几个月了,我发现阅读不熟悉的代码(例如我没有自己编写的代码)是该语言最具挑战性的方面之一,特别是当它是暗示时。经过一段时间,我想出了以下策略:
1)将代码片段复制到Word文档中。
2)从(1)中取出每个运算符并将其放在单独的一行上,以便垂直阅读。
3)将每个运算符替换为词汇页中的口语描述。
4)从J语法粗略地翻译成英语语法。
5)使用翻译来识别概念相关的组件,并用换行符将它们分开。
6)用简明易懂的英语散文写下(5)中每个组件应该做什么的描述。
7)根据(6)编写整个程序应该做什么的描述。
8)解释为什么可以说(1)中的代码代表(7)中的设计概念。
虽然我从这个过程中学到了很多东西,但我觉得它相当费时费力 - 特别是如果有人使用我从未遇到过的概念来设计他们的程序。所以我想知道:J社区中的其他人是否有喜欢解析晦涩代码的方法?如果有,这些方法的优缺点是什么?
编辑:
以下是需要分解的代码示例:
binconv =: +/@ ((|.@(2^i.@#@])) * ]) @ ((3&#.)^:_1)

我自己写了这个程序,所以我知道它需要一个数字输入,将其重新解释为三进制数组,并将结果解释为最多有一个重复的二进制数的表示形式。(例如,binconv 5 = (3^1)+2*(3^0) -> 1 2 -> (2^1)+2*(2^0) = 4。)但是如果没有任何先前的历史记录或文档,发现它的功能将是一个不容易的练习。
5个回答

13

我想补充一下Jordan的回答:如果您没有启用框显示,可以使用5!:2来明确格式化内容。

   f =. <.@-:@#{/:~
   5!:2 < 'f'
┌───────────────┬─┬──────┐
│┌─────────┬─┬─┐│{│┌──┬─┐│
││┌──┬─┬──┐│@│#││ ││/:│~││
│││<.│@│-:││ │ ││ │└──┴─┘│
││└──┴─┴──┘│ │ ││ │      │
│└─────────┴─┴─┘│ │      │
└───────────────┴─┴──────┘

此外还有一种树形显示:

   5!:4 <'f'
              ┌─ <.
        ┌─ @ ─┴─ -:
  ┌─ @ ─┴─ #       
──┼─ {             
  └─ ~ ─── /:     

请查看词汇页面的5!:表达式9!:全局参数以更改默认设置。

此外,就我个人而言,阅读J语言的方法是手动输入表达式,从右到左逐步构建,并在需要时使用恒等函数形成临时训练,并进行查找。

例如:

   /:~ i.5
0 1 2 3 4
   NB. That didn't tell me anything
   /:~ 'hello'
ehllo
   NB. Okay, so it sorts. Let's try it as a train:
   [ { /:~ 'hello'
┌─────┐
│ehllo│
└─────┘
   NB. Whoops. I meant a train:
   ([ { /:~) 'hello'
|domain error
|       ([{/:~)'hello'
   NB. Not helpful, but the dictionary says
   NB. "{" ("From") wants a number on the left.
   (0: { /:~) 'hello'
e
   (1: { /:~) 'hello'
h
   NB. Okay, it's selecting an item from the sorted list.
   NB. So f is taking the ( <. @ -: @ # )th item, whatever that means...
   <. -: # 'hello'
2
   NB. ??!?....No idea. Let's look up the words in the dictionary.
   NB. Okay, so it's the floor (<.) of half (-:) the length (#)
   NB. So the whole phrase selects an item halfway through the list.
   NB. Let's test to make sure.
   f 'radar' NB. should return 'd'
d
   NB. Yay!

附录:

   NB. just to be clear:
   f 'drara' NB. should also return 'd' because it sorts first
d

11

建议先将动词分解成其组成部分,然后看看它们的作用。而且,不必总是参考词汇表,您可以简单地尝试在数据上使用一个组件,看看它的作用,并尝试理解它。为了了解动词的结构,最好知道正在查看哪些词性,以及如何识别基本的构造,例如fork(当然,在更大的内隐结构中,要用括号分开)。只需将该动词键入ijx窗口并按Enter键即可分解出结构,并可能有所帮助。

考虑以下简单示例:<.@-:@#{/:~

我知道<. -: # {/: 都是动词,~ 是副词,@ 是连接词(请参见词汇表中的词性链接)。因此,我可以看到这是一个fork结构,左边的动词是<.@-:@#,右边的动词是/:~,二元运算符是{。这需要一些练习才能看清楚,但有一个更简单的方法,让J将结构显示出来,只需将其键入ijx窗口并按Enter键即可。

   <.@-:@#{/:~
+---------------+-+------+
|+---------+-+-+|{|+--+-+|
||+--+-+--+|@|#|| ||/:|~||
|||<.|@|-:|| | || |+--+-+|
||+--+-+--+| | || |      |
|+---------+-+-+| |      |
+---------------+-+------+

在这里,您可以看到动词的结构(当您习惯了观察后)。如果您无法识别组成部分,请尝试操作它们以了解其作用。

   10?20
15 10 18 7 17 12 19 16 4 2
   /:~ 10?20
1 4 6 7 8 10 11 15 17 19
   <.@-:@# 10?20
5
你可以进一步细分并进行实验以找出规律(这个小例子是一个中位动词)。
J语言将大量的代码压缩在少量字符中,而大的点形式动词可能会让有经验的用户感到非常吓人。通过尝试分解大型复杂动词,你可以更快地进行实验,比记录方法更有效,并且你可以真正通过尝试来学习J语言。我认为推荐集中精力尝试看到语法结构,然后逐步找出组成部分,逐步构建(因为这是最终编写点形式动词的方式)。

有趣。我以前从未明确地按照词类来分解事物 - 至少在我练习J语言短语书中的隐式动词或尝试弄清楚Roger Hui在编写他的Project Euler解决方案时在想些什么时,我的思考通常停留在结构与实质运算符的层面上。 - estanford
要更改动词的显示方式(框、树或线性),请使用外部连接符9!:3 - Micha Wiedenmann

3

我将这篇文章放在回答部分而不是编辑问题,因为问题看起来已经很长了。

我刚刚在 jsoftware网站上发现了一篇优秀的论文,它与Jordan的答案和我在问题中描述的方法结合使用效果很好。作者提出了一些相关观点:

1)被副词修饰的动词是一个动词。

2)超过三个连续动词的列是一系列分支,可能有一个单独的动词或者钩子在最左边,这取决于有多少动词。

这加快了将隐含表达式翻译成英语的过程,因为它让您将动词和副词分组成概念单元,然后使用嵌套的分支结构快速确定运算符的实例是单态还是双态的。这是我使用精细方法进行的翻译示例:

d28=: [:+/\{.@],>:@[#(}.-}:)@]%>:@[

[: +/\

{.@] ,

>:@[ #

(}.-}:)@] %

>:@[
  • cap (加中缀前缀)

    (头部叠加右参数) 展平

    (增量叠加左参数) 计数

    (去掉头部减去尾部) 叠加右参数

    除以

    增量叠加左参数

  • 由右参数定义的序列的部分和

    与右参数的第一项展平在一起,

    (左参数加一)次复制

    右参数除了第一个元素之外的所有元素减去(除了最后一个元素之外的所有元素),

    除以(左参数加一)。

  • 由右参数派生的点的连续副本附加到从相同初始点开始定义的序列的部分和中,

    通过从其后继者中减去每个前任来进行

    并将结果除以要制作的副本数量

  • 在y的项之间插入x个值进行插值

2

我想谈谈我是如何阅读这段代码的:

首先,我知道如果这是一个函数,在命令行中输入时必须输入:

(<.@-:@#{/:~)

现在我看着括号里的内容。我看到了/:~,它返回其参数的排序列表,{从列表中选择一个项,#返回列表中的项目数,-:一半,和< .,向下取整...然后我开始考虑它可能是中位数,即列表中项目数的一半向下取整,但是#如何获得它的参数呢?我看了看@符号——意识到那里有三个动词——所以这是一个分叉。列表从右边进入并进行排序,然后在左边,分叉将列表传递给#以获取参数数量,然后我们知道它将半数向下取整。所以现在我们有执行顺序:

排序,并将输出作为右参数传递给中间动词。

取列表元素数量的半数向下取整,成为中间动词的左参数。

执行中间动词。

这就是我的方法。我同意有时短语中有太多奇怪的东西,你需要查找它们,但我总是在J即时命令行中解决这些问题。


1

就我个人而言,我将 J 代码看作是它所执行的功能 - 如果我没有任何示例参数,我很快就会迷失方向。如果我有示例,通常很容易看出子表达式在做什么。

当难度加大时,这意味着我需要在字典中查找单词,或者可能需要学习其语法。

通过阅读这里的说明,我得出的想法是,这与其他人使用语言的方式并没有太大区别。

也许我们应该称之为“测试驱动的理解”?


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