J语言,是一种阵列编程语言,由肯尼斯·艾佛森和於1990年代初發明。J语言是APL語言的一种方言,延续了APL鲜明的簡潔性,它在數學和统计学程式設計上十分高效,特別是在需要进行矩陣运算的场合。
J语言最初起步于肯尼斯·艾佛森在1987年发表的《APL字典》,它实现了其中至关重要的秩的概念。J语言提供隐式定义机制包括秩、钩子、叉子和多种,并介入了作为头等对象的动名词,用以建立控制结构,它常被作为隱式編程的典范之一。
简介
J语言的运算符,承袭APL传统,没有优先级并且最右先行,2 3 + 4按照2 (3 + 4)来运算。以历史上APL使用的典型符号为例,符号/被用来指示折叠函数foldr1,所以+/1 2 3等价于1 + (2 + 3);在APL中,除法被表示为数学除号,它将减号和冒号一起在EBCDIC和ASCII二者的纸质文本终端上;J语言使用%表示除法,是对除号的一种近似或提示。
為了避免APL使用特殊的字符而遇到的問題,J语言只需基本的ASCII字符集,但使用点号.和冒号:作为“屈折”。点号和冒号除了前导着空白字符的情况之外,都与紧前字符形成类似双字符组的短字。多数“基础”或“原始”的J单字,都充当数学符号,通过点号或冒号来扩展这些可用基本字符的含义。在其他语言中经常是成对的很多字符,比如[] {} "" `` <>,在J语言中被当作单独的字,或者在有屈折的时候,作为多字符字的单字符字根。
J语言不再支持从1968年的APL\360就有的[;]形式的方括号索引,转而支持叫做“来自”(from)的索引机制,它起源自Kenneth E. Iverson于1978年在《算子和函数》中提出的,依据基数解码定义,并用符号⌷表示的索引。
J语言承袭IBM APL\360采用了平坦阵列模型,不支持由NARS(嵌套阵列研究系统)于1981年介入,并被IBM APL2所采纳的嵌套阵列模型;J语言增加了Kenneth E. Iverson于1978年在《算子和函数》中提出的盒装数据类型,它由SHARP APL于1981年介入,并于1983年在研究报告《理性化APL》中,列入与APL2相比较的“限定子集”(RS)而著重强调。
J语言支持AVX2指令集进行SIMD运算。为了包装用面向对象编程语言开发的API和框架,J语言提供了层级命名空间机制,这里所有名字都存在于特定语境(locale)中,可以避免软件包之间的名字冲突,并能有效的用作基于类的面向对象编程的框架。通过包管理器,还可以安装各种插件,如果是在管理员权限下安装的J语言解释器,则安装插件也需要同样的管理员权限。J语言拥有常规调试机制,还有叫做Dissect的可视调试器。除了科学计算和统计分析,它还被用于关系数据库管理系统如Jd、极限编程和网络性能分析。
2011年3月,J语言采用了GNU通用公共许可证版本3,从而成为自由和开源软件,人们还可以在Jsoftware的商业许可证下利用源代码。
文档与词类
J语言的文档包括在官网的NuVoc中,在其中将主要的字罗列为“J”,并使用颜色标示出它们分别的词类。早期的文档还有入门和字典。在J语言中的字,被识别为名词、动词、定语(副词和连词)、系词、标点、控制字。一个程序或例程,如果接受数据作为输入并产生数据作为输出,则被称为“动词”,与之相对,数据参数被称为“名词”。
动词有两种形式:只有右侧一个参数的一元(monad)形式,和有左右两侧参数的二元(dyad)形式,例如在-1中减号是一元动词,而在3-2中减号是二元动词。J语言预定义了很丰富的动词,它们都自动的作用于多种数据类型之上。用户定义的程序可以自行命名,并用在任何允许使用原始动词的地方。无论原始动词还是派生动词,它们的一元定义与二元定义,在很大程度上是独立的。
起步示例
J语言可以写出非常精简的程序,特别是存在重度的对符号的函数重载,以至于一些编程者将它称为难以阅读的只写语言。在计算机的终端上执行ijconsole,即可进入J语言的REPL解释器界面。
Hello, World!
J语言的“Hello, World!”程序:
'Hello, world!'
Hello, world!
这个Hello World的实现反映了J语言的传统用法,就是把程序录入到J解释器会话中,显示出表达式结果。还可以准备J脚本来作为独立程序来执行,比如在Linux系统上,可以编辑如下内容的一个文本文件,并命名为比如test01.ijs:
#!/usr/bin/ijconsole
echo 'Hello, world!'
exit *
注意第一行的#!必须顶头,这里的echo和exit,是与Unix shell中同名命令功能类似的动词。然后在终端界面中执行这个文件:
$ ijconsole test01.ijs
Hello, world!
$ chmod +x test01.ijs # 另一种执行方式,授予这个文件可执行权限
$ ./test01.ijs
Hello, world!
平均
在J语言中函数一般称为动词,例如定义一个叫做avg的动词,计算一序列数的平均:
avg=: +/ % #
avg 1 2 3 4
2.5
一元动词#“计数”(tally),总计阵列中项目的总个数。动词+“加”(plus)和副词/“插入”(insert),派生出的动词+/,合计这个阵列的项目的总和。二元动词%“除”(divide)将这个总和除以这个总个数。而用户定义的动词avg,用到了由连串(strand)的三个动词(+/、%和 #)构成的一个“叉子”(fork)。叉子(f g h) y↔(f y) g (h y),这里的f、g和h指示动词,而y指示一个名词。
使用avg的一些例子:
]a=: ?. 20 $100 NB. 产生100以内20个随机整数的一个向量
94 56 8 6 85 48 66 96 76 59 33 72 63 1 89 52 17 20 9 65
avg a
50.75
4 avg\ a NB. 周期大小为4的移动平均
41 38.75 36.75 51.25 73.75 71.5 74.25 66 60 56.75 42.25 56.25 51.25 39.75 44.5 24.5 27.75
]b=: ?. 4 5 $50 NB. 产生50以内20个随机整数的一个矩阵
44 6 8 6 35
48 16 46 26 9
33 22 13 1 39
2 17 20 9 15
avg b
31.75 15.25 21.75 10.5 24.5
avg"1 b NB. 应用avg于m的每个秩为1的子阵列
19.8 29 21.6 12.6
一元副词/“插入”(insert),接受位于它左侧的一个运算元,并产生将这个动词应用于其参数的每个项目之间的一个动词。就是说,+/是一个动词,定义为应用+于给它的参数的各个项目之间。计算移动平均用到的二元副词\“中缀”(infix),将作为数据参数的列表划分成一系列的指定大小的连续项目的子列表,将所修饰动词应用于其上,并将这些结果形成一个列表。
一元动词]“相同”(same),恒等于给它的单一右参数,常像这样用来在赋值之后显示变量的内容。一元动词?.“掷骰/固定种子”(roll/fixed seed),不同于一元动词?“掷骰”(roll),在生成数据参数项目所指定大小范围内的随机数之时,采用固定的种子。这里确定对矩阵按行还是按列进行平均,用到了连词"“秩”(rank),它在后面的定语章节和单独条目中论述。
查找与排序
二元动词i.“出现索引”(index of),和二元动词i:“最后出现索引”(index of last),在任何大小的阵列内查找匹配者,并返回它的位置索引,如果未找到匹配者,则返回这个阵列的大小。例如:
a=: 3 1 4 1 5 9
a i. 1 2 NB. 找到1和2的第一次出现的索引
1 6
a i: 1 2 NB. 找到1和2的最后一次出现的索引
3 6
在J语言中,排序可以按APL传统的两步骤方式,使用一元动词/:“升序索引”(grade up)或\:“降序索引”(grade down),和用二元副词~“被动”修饰的二元动词{“出自”(from),二者连串(strand)形成的一个“钩子”来完成。一元钩子(f g) y↔y f (g y);副词~“反身·被动”,其一元定义为f~ y↔y f y,二元定义为x f~ y↔y f x。J语言还提供专用的二元动词/:“上升排序”(sort up)或\:“下降排序”(sort down)。下面是用例:
a=: 2 0 4 7 15 9 8 0 4 9 18 8 1 18
/: a NB. 产生参数阵列的升序索引
1 7 12 0 2 8 3 6 11 5 9 4 10 13
({~ /:) a NB. 从参数阵列中按升序索引选取出各个项目
0 0 1 2 4 4 7 8 8 9 9 15 18 18
/:~ a
0 0 1 2 4 4 7 8 8 9 9 15 18 18
(a - 10) /: a
_10 _10 _9 _8 _6 _6 _3 _2 _2 _1 _1 5 8 8
CSV插件
安装用来支持CSV文件的插件:
load 'pacman' NB. 加载包管理器
'install' jpkg 'tables/csv' NB. 安装CSV文件插件
'showinstalled' jpkg * NB. 查看已经安装插件
一个CSV文件简单用例:
load 'tables/csv' NB. 加载CSV插件
a=: i. 2 3
a writecsv jpath '~/test01.csv' NB. 将一个阵列写入一个CSV文件
12
]b=: readcsv jpath '~/test01.csv' NB. 从一个CSV文件读入一个盒子阵列
┌─┬─┬─┐
│0│1│2│
├─┼─┼─┤
│3│4│5│
└─┴─┴─┘
]c=: makenum b NB. 尽可能的将盒子阵列转换成数值阵列
0 1 2
3 4 5
下面演示使用J语言编写在管道中的过滤器,例如,在具有隐式编程机制Unix管道的Linux系统中,建立如下内容的文本文件,并命名为比如filter01.ijs:
#!/usr/bin/ijconsole
load 'tables/csv'
stdout makecsv 10 + makenum fixcsv stdin *
exit *
然后在终端界面中执行如下命令行:
$ cat test01.csv | ijconsole filter01.ijs
10,11,12
13,14,15
数据类型
J语言支持三种简单类型:
- 数值
- 文字(字符)
- 盒装
其中数值有很多变种。J语言提供的唯一搜集(collection)类型,是任意维度的阵列。多数算法可以使用这些阵列来简洁的表达。
数值
J语言的数值类型之一是“位”。位有两个值:0和1。位还可以形成列表,例如1 0 1 0 1 1 0 0,是8个位的列表。在语法上,J分析器将位当作一个字。空格字符被识别为字形成字符,它处在属于其他数值字的字符之间。
J语言支持任意长度的列表。J语言进一步的在这些位列表之上,支持所有常见二元运算,比如动词*.“与”(and)、+.“或”(or)、-.“非”(not)、|.“反转·旋转”(reverse·rotate)、|.!.f“移位”(shift)等。J语言还支持位的二维、三维等阵列。上面的运算同样运行在这些阵列之上。
其他数值类型包括整数(比如3、42)、浮点数(3.14、8.8e22)、複數(0j1、2.5j3e88)、扩展精度整数(12345678901234567890x)和(扩展精度)有理分数(1r2、3r4)。同位一样,它们可以形成列表或任意维度的阵列。同位的情况一样,运算可以在一个阵列的所有数值之上。下面例子展示π的前50位,超出了IEEE 754双精度浮点数的53位二进制尾数能精确表示的最大范围,这就要用到J语言的扩展精度整数:
0j15 ": o. 1 NB. π在双精度浮点数下精确值的位数
3.141592653589793
这里采用一元动词o.“π乘以”(pi times),和一元动词“下取整”(floor)二者的复合,得到预期的结果。位的列表可以使用一元动词#.“基数2”(base 2)解码成整数,但是它们并非数据类型,实际上使用盒装文字来提及类和对象,它们实为命名语境和编号语境。
此外还有一个伪类型即内存映射文件,它将变量映射到文件系统中的持久文件。
简要词汇表
下面的表格简要列出了常用词汇。如果含义中用了间隔号( · )分隔,通常前者是只有一个右侧参数的一元含义,后者是左右两侧都有参数的二元含义。列出的对应APL符号,是Dyalog等现代APL所采用的符号。
基本
数学
&派生的动词经常作为一元动词使用,即或;如果作为二元动词使用,即或,左参数表示应用这个派生动词于右参数的次数。需要确保通过&定义的一元动词,不出现在能够取用左右两个的参数的上下文中;如此定义的二元动词,也不应该出现在只能取用一个右参数的上下文中。需要注意m&v、v/和v\等的左参数的作用域,有时遇到其左侧的复合连词会产生并非预期的效果,经常需要将表达式整体加以圆括号包围。
J语言还提供连词&.:“底下”(under)和&.“对偶”(dual)。下面定义中的动词幂^:_1表示逆运算:
@、&和&.合称为“紧密复合”(close composition)。现代APL中另有¨“每个”(each),f¨相当于J语言中的f &.>。例如:
1 2 + &.:> 0.1 0.2
┌───────┐
│1.1 2.2│
└───────┘
1 2 + &.> 0.1 0.2
┌───┬───┐
│1.1│2.2│
└───┴───┘
在J语言中,孤立的动词序列叫做“列车”(train)。
与现代APL如Dyalog等对照,复合连词@:对应于同秩连词共享APL符号⍤的“顶上”(atop),而&:对应于⍥“上方”(over),共享&符号的“粘上”,对应于APL中的“绑上”(bind),APL的“绑上”和“边上”(beside)共享符号∘,“边上”的一元形式同于⍤,而二元形式同于钩子。在《APL字典》中,@对应其⍥,而&列入秩连词⍤之内,钩子对应于符号⍩“枝条”(withe),&.对应于符号¨。
用例
下面的简单例子是计算欧几里得范数,和生成数位与维度坐标一致的整数:
]d=: (1 1),(1 1 1),:(3 4)
1 1 0
1 1 1
3 4 0
norm=: %: @ (+/) @: :"1 NB. 它可加圆括号为((%: @ (+/)) @: :)"1
norm d
1.41421 1.73205 5
coor=: 10&#. @ > @ { @: (: @ i."0)
coor 2 3 4
111 112 113 114
121 122 123 124
131 132 133 134
211 212 213 214
221 222 223 224
231 232 233 234
在coor中采用的圆括号包围,使得@:处在整个表达式的最外层,从而形成了两步骤运算;右侧的第一步是圆括号包围的子表达式,它的完全加圆括号(fully-parenthesized)形式为:(((:) @ i.)"0);左侧的第二步是{与复合到其上诸运算构成的子表达式,它的完全加圆括号形式为:(((10 & #.) @ >) @ {)。一元动词{“目录汇编”(catalogue),应用在盒装列表的列表之上,是接受可变数目的变长参数的典型的动词。
下面通过对圆括号包围的子表达式加以变化,辨析秩连词和复合连词的特性。这里的动词],划分开了给连词或副词的名词运算元,和给所生成的动词的名词参数:
]a=: : @ i."0 ] 2 3 4 NB. 运算式可加圆括号为((:) @ i.)"0
┌───┬─────┬───────┐
│1 2│1 2 3│1 2 3 4│
└───┴─────┴───────┘
a -: : @ i.)"0 ] 2 3 4 NB. 运算式可加圆括号为(: @ i.))"0
1
a -: : @ i."0) 2 3 4 NB. 运算式可加圆括号为: @ i.)"0)
1
: @ i."0 ] 2 3 4 NB. 运算式可加圆括号为((:) @ i.)"0
┌─┬─┬─┬─┐
│1│2│ │ │
├─┼─┼─┼─┤
│1│2│3│ │
├─┼─┼─┼─┤
│1│2│3│4│
└─┴─┴─┴─┘
: @ i."0) 2 3 4 NB. 运算式可加圆括号为: @ i.)"0)
┌───────┐
│1 2 0 0│
│1 2 3 0│
│1 2 3 4│
└───────┘
下面的例子展示并联电阻电路计算:\frac{1}{R_\mathrm{total}} = \frac{1}{R_1} + \frac{1}{R_2} + \cdots + \frac{1}{R_n},它可以如下这样表达:
Rtotal=: +/ &.: %
Rtotal 10 5 15
2.72727
动词幂
连词^:动词幂”(power of verb),有两种形式:
*^:n,是运算元为名词的“固定幂”。
*^:v,是运算元为动词的“动态幂”。
对于固定幂u ^:n,如果x缺席,u ^:n y在以y为运算对象的迭代中,将动词u应用n次;如果x存在,x u ^:n y在以y为运算对象的迭代中,将动词x&u应用n次。如果n是阵列,则按每个原子项目都执行一次动词幂,结果的框架为这个阵列的形状;如果n是取值为0或1的变量,则形成布尔值条件执行;如果n是_1,则进行u的逆运算;如果n是_,则意味着“收敛”(converge),即反复应用u直到结果不再变化。例如:
(1+*&3) ^:0 1 ] 1 2 3 4
1 2 3 4
4 7 10 13
' ' , ^:4 'abc'
abc
(1+*&3) ^:_1 ] 4 7 10 13
1 2 3 4
(-:@(]+%)) ^:_ &1 ] 0.25 3 25 NB. 以巴比伦方法即一种牛顿法特例来计算平方根
0.5 1.73205 5
对于动态幂u ^:v,如果x缺席,u ^:v y在以y为运算对象的迭代中,将动词u应用v y次;如果x存在,x u ^:v y在以y为运算对象的迭代中,将动词x&u应用x v y次。动词幂可以形成动态条件执行,这里的动词v必须总是产生布尔值结果,应用动词u当且仅当v返回1。进而u ^:v ^:_ y可以形成while循环构造,只要v返回1,就反复的执行u,直到v返回0,或者u将它的参数无变化的返回。例如:
-&2 ^:(>&4) "0 ] 1 3 6 12 NB. 对大于阈值4的列表项目减去2
1 3 4 10
4 (0.25&@[+(1-0.25)&@]) ^:
用例
下面的例子用来辨析秩指定与框架划分及汇集的性质,其中涉及的直接定义等内容可见于后面的定义章节:
itemize=: ,: : "(0 _) NB. 定义具有一元和二元两种形式的项目化扩秩运算
@
动名词
在J语言中,动名词(gerund)是叫做“原子表示”的特殊盒子的一个列表,这种盒子可以像任何其他盒子一样使用,并可以最终转变回到要执行的动词。关于动名词的运算有:
*连词`“连结动名词”(tie or gerund),建立动名词。
*连词`:“唤起动名词”(evoke gerund),在加以运算元之后成为:
**`:6,将动名词转变成动词列车;
**`:0,将转变回来的这些动词分别单独应用,并将它们的结果收集入一个列表。
*连词@.“议程”(agenda),m @. n从动名词m中,选择出第n个原子表示,将它转变回到动词并执行它。
下面是动名词简单示例和考拉兹猜想示例:
grd =: * ` (+&2) NB. 建立一个动名词
grd NB. 显示原子表示,这里的符号'0'标识名词
┌─┬─────────────┐
│*│┌─┬─────────┐│
│ ││&│┌─┬─────┐││
│ ││ ││+│┌─┬─┐│││
│ ││ ││ ││0│2││││
│ ││ ││ │└─┴─┘│││
│ ││ │└─┴─────┘││
│ │└─┴─────────┘│
└─┴─────────────┘
{. grd NB. 动名词可以像普通盒子一样操纵
┌─┐
│*│
└─┘
grd `:6 i. 4 NB. 将动名词转换成动词列车来执行
0 3 8 15
grd `:0 i. 4 NB. 将动名词转换成并行执行的动词
0 1 1 1
2 3 4 5
Collatz=: -: ` (1+*&3) @. (2&|) NB. 考拉兹猜想的算式
Collatz "0 ] 1 2 3 4 5 6 7
4 1 10 2 16 3 22
>./@(Collatz^:(>&1)^:_"0@>:@?@$&1e6) 1000 NB. 取1000个在1e6内的随机数测试考拉兹猜想
1
定义
J语言支持用户进行显式定义,和形式的。下面以五种复合作为显式定义的例子:
at=: conjunction define
u (v y)
:
u (x v y)
)
atop=: conjunction def '(u at v)"v'
beside=: conjunction define
u (v y)
:
x u (v y)
)
appose=: conjunction define
u (v y)
:
(v x) u (v y)
)
compose=: conjunction def '(u appose v)"(0{v b.0)'
下面以矩阵乘法作为直接定义的例子,并顺带演示秩连词的用法:
a=: ? @ $&1000 @: >: 4?10
b=: |: a
p=: a (+/ . *) b
matmul=:
(a (+/ matmul *) b) -: p
1
inner=:
(a (+/ inner * 0&|:) b) -: p
1
outer=:
(a (+/ outer *) b) -: p
1
revmul=:
(a (+/ revmul * 0&|:) b) -: p
1
APL传统上的将内积解释为,J语言的矩阵乘法要求写为,不隐含的为左运算元f附加一元/。转置也是有较大的运算,不同的有不同的参照局部性。
*matmul通过"(1 _)将u@:v的右参数从向量扩展为一般阵列,它用于右参数为阵列的情况。在向量与向量列表二者诸项之间逐对的进行乘积累加运算,是BLAS的标准算法。
*inner采用了两向量之间的点积运算u@:v"1,它适宜直接用于右参数为阵列的情况。在右参数为行主序阵列之时,需要如例子代码这样,对右参数阵列进行二元转置0&|:,这里的0指示将第一轴安排至最后位置而其他轴保持原序前移。这种基于点积的实现,通常需要进一步加以。
*outer可以看作matmul的变体,它首先对左参数阵列进行转置,然后进行多组的向量与一般阵列之间二元的张量积运算v/"_1,最后在各组结果的列表上进行u计算,每组运算之后参与其中的左右两阵列的元素不会被其他组的运算再次访问。
*revmul同inner一样适宜直接用于右参数为列主序阵列的情况,但采用了同matmul类似的计算方法,它在乘法之前对左参数阵列和在乘法之后对结果要做转置,matmul与revmul的关系如同\,(\mathrm{AB})^\mathsf{T} = \mathrm{B}^\mathsf{T}\mathrm{A}^\mathsf{T}\,。matmul一次性访问左参数阵列,反复多次访问右参数阵列;revmul一次性访问右参数阵列,反复多次访问左参数阵列。
在隐式定义中,递归定义可以不通过名字引用自身,转而使用动词$:“自引用”。例如递归的计算斐波那契数列:
fibonacci=: 1: ` ($:@-&2 + $:@&2) "0 : [:
fibonacci >:i.9
1 1 2 3 5 8 13 21 34
_9:到9:是常量动词。动词[:“遮帽”,用在连词u : v“一元与二元定义”所应用的动词位置上,即充任了一元动词u或二元动词v,可分别在如下两种情况下报错:定义的是二元动词,却不适当的被用作一元动词;或定义的是一元动词,却不适当被用作二元动词。
在显式定义和直接定义中,提供了类似其他过程语言的控制结构。这里列出的是每个范畴内的代表性控制字:
索引
J语言的索引机制采用二元{“来自”(from)动词来完成,它的秩为0 _,它有两种形式,分别为左参数为索引阵列的主轴索引,和左参数为二层或一层盒装结构的逐轴索引,二者分别对应APL中,方括号内为单个轴的主轴索引选取,和方括号内为;分隔的多个轴的逐轴索引选取。
主轴索引
索引阵列的每个项目指定对主轴的项目单元的一个选取,将它们的结果再汇合为一个阵列。负值索引表示从末尾往前记数。在APL中,这种形式的索引被称为“来自”(from),也叫做“选取”(select)或幽默地称为“明智”(sane)索引,最早出现在SAX(SHARP APL for UNIX)对其@“来自”索引的扩展中,部份现代APL,将它表示为符号⊇。例如:
i. 2 3
0 1 2
3 4 5
1 0 { i. 2 3
3 4 5
0 1 2
1 0 {"1 i. 2 3
1 0
4 3
(i. 2 3) { 'abcdefg'
abc
def
_1 { 'abcdefg'
g
逐轴索引
逐轴选取可以形成子阵列,它被称为“可能是APL编程者最后掌握的原始运算之一”。J语言的二元转置对轴次序的指定,不涉及将多个轴映射到结果中的一个轴的情况,使用了普通的索引形式的置换向量。在置换向量的元素个数小于阵列轴的数目之时,J语言在置换结果中将其指定的诸轴安排在尾部,而其他轴保持原来相对次序前移。
针对向量的索引运算,是不加盒装的主轴索引。要访问一般阵列的特定原子项目,其选取向量需要一层盒装,从而对其进行逐轴索引。置换向量p所对应的逆向置换向量是:
*两次连续的二元转置,可以变换成等价形式: ↔ {{code|(p{q)|:A|lang="J"}},即先将后者置换向量p对前者置换向量q进行置换,然后用结果的置换向量做一次二元转置。
*对二元转置后的阵列,进行原子项目的选取,可以变换成等价形式:{{code|(|:A|lang="J"}} ↔ {{code|(p的逆置换向量/:p,对选取向量k进行置换,然后用结果的选取向量来选取未转置阵列。
下面是演示例子代码:
a=: ? @ $&1000 @: >: 4?10
n=: # @ $ a
p=: ?~ n
q=: ?~ n
(/:/:p) -: p
1
((/:p) { p) -: i. n
1
(/: p{q) -: (/:q) { /:p
1
(p |: q|:a) -: (p{q) |: a
1
k=: ? $ p |: a
((
示例
帕斯卡三角
下面例子形成帕斯卡三角的二项式系数的直接定义,并顺带展示基本的矩阵与盒装运算:
pascal=: "0
pascal @ i. ] 5
1 0 0 0 0
1 1 0 0 0
1 2 1 0 0
1 3 3 1 0
1 4 6 4 1
({. (+//. @: pascal @ i.)) 9 NB. 斐波那契数列
1 1 2 3 5 8 13 21 34
((+/ . *) |:) @: pascal @ i. ] 5 NB. 帕斯卡矩阵
1 1 1 1 1
1 2 3 4 5
1 3 6 10 15
1 4 10 20 35
1 5 15 35 70
}:@,@:(,.&a:@) t
┌─┬─┬─┬─┬─┬─┬─┬─┬─┐
│ │ │ │ │1│ │ │ │ │
├─┼─┼─┼─┼─┼─┼─┼─┼─┤
│ │ │ │1│ │1│ │ │ │
├─┼─┼─┼─┼─┼─┼─┼─┼─┤
│ │ │1│ │2│ │1│ │ │
├─┼─┼─┼─┼─┼─┼─┼─┼─┤
│ │1│ │3│ │3│ │1│ │
├─┼─┼─┼─┼─┼─┼─┼─┼─┤
│1│ │4│ │6│ │4│ │1│
└─┴─┴─┴─┴─┴─┴─┴─┴─┘
二项式系数也可以写成隐式定义形式:
pascal=: ((0&, + ,&0)@] ^:[ 1:)"0 : [:
简易图表
下面是基于二元副词/“形成表格”(table),制作条形图和散点图的简易图表例子:
barChartH=: {&('.',u:16b2584) @ (>/ i.@(>./)) : [:
barChartH 3 1 4 1 5 9 2 6 5
▄▄▄......
▄........
▄▄▄▄.....
▄........
▄▄▄▄▄....
▄▄▄▄▄▄▄▄▄
▄▄.......
▄▄▄▄▄▄...
▄▄▄▄▄....
barChartV=: {&('.',u:16b258c) @ (./)) : [:
barChartV 3 1 4 1 5 9 2 6 5
.....▌...
.....▌...
.....▌...
.....▌.▌.
....▌▌.▌▌
..▌.▌▌.▌▌
▌.▌.▌▌.▌▌
▌.▌.▌▌▌▌▌
▌▌▌▌▌▌▌▌▌
scatterChart=: {&('.',u:16b2588) @ (=/~ >:@|.@i.@(>./)) : [:
scatterChart 3 1 4 1 5 9 2 6 5
.....█...
.........
.........
.......█.
....█...█
..█......
█........
......█..
.█.█.....
这里用到的Unicode方块元素字符也出现在IBM PC代码页437之中。
这里的scatterChart可以使用二元副词}“修改”(amend)来实现:
scatterChart=: {&('.',u:16b2588) @ ((>./ , #) {{
1 y } $&0 x}} (#~ ./) ,. i.@#)) : [:
对角线选取
APL的二元转置,在多个轴映射到结果中的一个轴的情况下,将其依次安排到前导位置上并进行对角线选取。下面基于一元动词I.“真值位置索引”,定义进行对角线选取的动词diag,它的左参数是布尔值列表,其中的真值1指示与其位置对应的轴,要依次安排在前导位置上并进行对角线选取,其他的假值0所对应的轴相对位置不变。
diag=: {{
s=. (I. , I.@:-.) @ ({.~ #@$)
t=.
这里局部定义了s,它的左右参数同于给diag的参数,它生成二元转置需要的置换向量,这是由要安排到前导位置上的那些轴的位置索引,和余下其他轴的位置索引串接而成。这里的{.~ #@$以右参数的诸轴数目选取左参数,用来在左参数的真值和假值的总数小于右参数的诸轴数目之时,对左参数填充上假值。
接着局部定义了t,它的左参数是给diag的布尔值列表中真值1的个数,右参数是要对其指定数目的前导轴进行对角线选取的阵列,它生成对角线选取所需要的一层盒装选取列表。这里的(选取出要进行对角线选取的前导诸轴的最小长度,用i.形成这个长度的整数数列,再用#&1@[形成其长度为左参数的全为1的列表,通过*/在二者之上形成表格矩阵。
最后的表达式先进行指定的二元转置,再对其结果进行相应的对角线选取。下面是简单用例:
>:i. 3 5
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
1 1 diag >:i. 3 5
1 7 13
i. 2 3 2
0 1
2 3
4 5
6 7
8 9
10 11
1 1 diag i. 2 3 2
0 1
8 9
0 2 1 |: i. 2 3 2
0 2 4
1 3 5
6 8 10
7 9 11
1 0 1 diag i. 2 3 2
0 2 4
7 9 11
快速排序
J语言提供的排序机制基于了稳定排序算法,下面的例子代码是快速排序的直接定义:
cmp=: * @ -
quicksort=: {{
if. 1 >: #y do. y return. end.
s=. y u y {~?# y
(u quicksort (s0)#y)}}
这里定义一个动词cmp,它通过逐个做两个数的差并取其符号,得到取值为_1、0或1的平衡三进制值。cmp将作为左运算元传递给副词quicksort。
在quicksort的定义中,向局部变量s赋值的表达式,第一步随机选择支点(pivot)运算,首先计算?#y,生成在数据总个数范围内的随机数,接着在其上计算{~,选择出在随机数指定的位置上的支点值;它的第二步运算,将运算元u,应用到其左参数的数据列表,和右参数的支点值二者之上。
随后是串接分治运算结果,首先将平衡三进制值列表,分别与0做逐项的三分法比较,得到三个布尔值列表;然后以这种列表中的0和1作为件数,复制出数据列表的符合这个条件一个新的子列表,其中两个作为参数传递给递归调用进行排序。
下面的快速排序实现,展示了隐式编程,即将函数复合在一起,而不显式的引用任何变量,不提及要应用于其上的形式参数。这里将前面代码中向局部变量s赋值时所求值的表达式改为隐式定义,进而以作为钩子的第一步运算的方式,代入引用这个变量的表达式之中,并且采用钩子的参数复制机制消隐了形式参数y:
cmp=: * @ -
quicksort=: {{((($:@#~ &0)) (u ({~ ?@#))) ^: (1
提供给连词^:的左侧运算元,外层是个一元钩子,它将提供给它的单一右数据参数,重复放置在它的左数据参数位置上。这个外层一元钩子的第一步运算,是生成平衡三进制值列表的嵌套的二层一元钩子(u ({~ ?@#));而外层一元钩子的第二步运算,将生成的三个子列表串接起来。生成三个子列表的表达式,以数据列表是作为左参数,以平衡三进制值列表作为右参数;这里的三个二元钩子首先生成布尔值列表,接着进行对换了左右参数位置的二元复制运算,最后它们中有两个通过自引用$:进行了递归调用。
这个定义中的$:是在这个副词的私有语境内调用的动词,所以不像前面直接定义那样需要加上运算元u以副词形式来调用。这里没有对字符串长度小于等于1的情况进行处理,这是因为迭代运算在条件不满足时返回初始值,也就是返回这个字符串本身。将cmp的表达式,代入定义中的左运算元u,就能定义出动词,同时也不再需要外在的采用直接定义的形式。
将一元钩子替代为左分支为]的叉子,形成的动词列车更具可读性:
quicksort=: {{(] (($:@#~ &0)) ] u ] {~ [:?#) ^: (1
下面的例子定义基于一元/:进行字符串比较的cmp:
cmp=: -/ @ (-.@-: * /:@;)
'alpha' cmp 'beta'
_1
'beta' cmp 'alpha'
1
'beta' cmp 'beta'
0
t=: ' the heart has its reasons that the reason does not know'
]words=: quicksort words
┌────┬───┬─────┬───┬────┬───┬──────┬───────┬────┬───┬───┐
│does│has│heart│its│know│not│reason│reasons│that│the│the│
└────┴───┴─────┴───┴────┴───┴──────┴───────┴────┴───┴───┘
在这个cmp定义中,/:@;先将两个字符串参数进行盒装串接,然后一元/:给出二者的升序索引,二者之间为升序或相同时为0 1,而二者为降序时为1 0;至此是升序还是相同仍需区分,-.@-:判断两参数是否为“不相同”,不相同时为1,而相同时为0。这里的副词;._1“自有区间”,使用字符串的第一个字符作为分隔符,对字符串进行划分并去除分隔符,然后应用所修饰的动词于这些子字符串之上。
全排列
下面的例子给出n个项目的所有置换的有次序的矩阵。首先定义名词p3,它是置换长度为3的全排列矩阵:
]p3=: (i.@! A. i.) 3
0 1 2
0 2 1
1 0 2
1 2 0
2 0 1
2 1 0
这里用到了动词:A.“易位词”(anagram),x A. y的左参数x,指定了长度为#y的所有置换中给特定一个置换的编号,其对应的置换向量在组合数学中被称为逆序向量,据此编号置换y的项目。这里求全排列的数目,用到了一元动词!“阶乘”。接着在p3的基础上,实现置换长度为4的全排列矩阵:
=/~ @ i. ] 4 NB. 生成4×4单位矩阵
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
\:"1 @ (=/~) @ i. ] 4 NB. 生成第1列为0 1 2 3,每行后3个元素升序的4×4“奇妙”矩阵
0 1 2 3
1 0 2 3
2 0 1 3
3 0 1 2
0&,. @: +&1 p3 NB. 对3元素全排列的每个元素增1,并在每个排列头部添0
0 1 2 3
0 1 3 2
0 2 1 3
0 2 3 1
0 3 1 2
0 3 2 1
perm0=: (0&,.@:+&1 p3) {"(_ 1) \:"1@(=/~)@i.
这里的{"(_ 1),其左侧是从6×3全排列矩阵加工而成6×4选取矩阵,用它分别对其右侧的4×4“奇妙”矩阵的每一行进行主轴索引运算,即将选取矩阵每行第1个元素保持为“奇妙”矩阵此行的第1个元素,而每行后面3个元素是对“奇妙”矩阵此行后面3个元素的排列。这一步写出的排列动词的结果是4×6×4的三维阵列,将它重制形状为24×4全排列矩阵:
perm1=: (,~ !) $ ,@((0&,.@:+&1 p3) {"(_ 1) \:"1@(=/~)@i.)
$ p4=: perm1 4
24 4
二元动词$的左参数的值为(!y) , y,这里的y是给排列动词的数据参数;而它的右参数是用一元动词,将三维阵列散开后形成的一个向量。
然后将其中的p3,替代为递归的自引用$:@-&1;并通过名词秩,形成常量动词(1 0$0)"_,设置长度为0时的基础值为1 0$0:
perm=: (1 0$0)"_ ` ((,~ !) $ ,@(0&,.@:+&1@$:@-&1 {"(_ 1) \:"1@(=/~)@i.)) @. (>&0) : [:
(perm 4) -: p4
1
$ perm 0
1 0
$ perm 1
1 1
invertVec=: I.@(, -:"1 perm@#) : ({ perm)
1 _1 invertVec 4
0 1 3 2
3 2 1 0
invertVec 3 2 1 0
23
最后将递归形式改为迭代形式,可采用连词F..“单结果正向折叠”(fold single forward),使用它需要事先安装插件dev/fold:
life=:
glider=: ".;._2 noun define
0 0 1 0 0
1 0 1 0 0
0 1 1 0 0
0 0 0 0 0
0 0 0 0 0
)
life glider
0 1 0 0 0
0 0 1 1 0
0 1 1 0 0
0 0 0 0 0
0 0 0 0 0
{&('.',u:16b2596) &.> (i.8) &.>
这里的实现算法,将生命游戏规则合并入一个单一表达式中,它由Arthur Whitney提出。算法的第一步骤用+/@,合计每个格点及其周围格点中1数目,并将它局部保存在s中。第二步骤将为前面的结果与4做相等比较,再与当前状态y做逻辑与运算,只有在当前格点为1,而且周围格点合计为3的情况下,当前格点得到真值1。第三步骤用s减去前面的结果真值,再与3做相等比较。在这个最终结果中,当前格点得到真值1有两类情况:⑴它在s中的值为4,并减去了真值1,这就是第二步骤运算所选定的情况;⑵它在s中的值为3,并减去第二步骤运算在这种情况下必然得出的假值0,这又可细分为两种情况:当前格点为0,而且周围格点合计为3;或者当前格点为1,而且周围格点合计为2。
这个算法可以写为隐式定义形式:
life=: (=&3@] +. (*. =&4)) (+/@,) stencil 3 3 : [:
在J语言中,实现以递推关系定义的序列,避免出现\,O(n^2)\,的时间复杂度,可以采用连词F:.“多结果正向折叠”(fold multiple forward),使用它需要事先安装插件dev/fold。这个连词所形成的动词,不输出左参数的初始值,如果提供的右参数为空值,它的输出为空值,否则它输出每次迭代的结果。上例中的迭代算式可以改写为:
load 'dev/fold/foldr'
{&('.',u:16b2588) &.> (
如果不采用连词F:.,可以自行实现输出中间结果的正向折叠算子:
foldlist=:
{&('.',u:16b2588) &.> (
在这里的foldlist中,将r=. * [ a=. x改为r=. u a=. x,则(可改为。
键分组
在J语言中,提供了二元副词/.“键分组”(key),它按左参数中的唯一键,对右参数进行分组(group)或称为分区(partition),并将所修饰动词应用到这些分组之上。分组次序同于一元动词~.“唯一值”处理结果的次序,它除去匹配前面出现过的项目的任何项目。下面在它的基础之上,定义对应现代APL使用的二元算子⌸“键分组”(key)的一个副词,它将所修饰动词应用在唯一键和相应的分组二者之上,它还具有一元和二元两种形式:
key=:
它的二元形式的左参数含有唯一键,而右参数是要分组的数据;它的一元形式的右参数含有唯一键,所分组的是右参数诸项目的索引值。下面是这个键分组副词所适用的动词的简单示例:
x=: 'Mississippi'
[ key x
┌─┬─┬─┬─┐
│M│i│s│p│
└─┴─┴─┴─┘
] key x
┌─┬────────┬───────┬───┐
│0│1 4 7 10│2 3 5 6│8 9│
└─┴────────┴───────┴───┘
,. key x
┌─┬────────┐
│M│0 │
├─┼────────┤
│i│1 4 7 10│
├─┼────────┤
│s│2 3 5 6 │
├─┼────────┤
│p│8 9 │
└─┴────────┘
([ ,. #&.>@]) key x
┌─┬─┐
│M│1│
├─┼─┤
│i│4│
├─┼─┤
│s│4│
├─┼─┤
│p│2│
└─┴─┘
下面的例子通过键分组来找到一个单词列表中的易位词:
]a=: a
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│apst│apst│aest│aest│apst│aest│apst│aest│aest│aest│arst│aest│aest│
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
(]key~ /:~&.>) a
┌─────────────────────┬─────────────────────────────────────────┬──────┐
│┌────┬────┬────┬────┐│┌────┬────┬────┬────┬────┬────┬────┬────┐│┌────┐│
││pats│spat│taps│past│││teas│sate│etas│seat│eats│tase│east│seta│││star││
│└────┴────┴────┴────┘│└────┴────┴────┴────┴────┴────┴────┴────┘│└────┘│
└─────────────────────┴─────────────────────────────────────────┴──────┘
随机数
在J语言中,提供了二元动词I.“区间索引”,它左参数x必须是有次序的,从而定义了1+#x个区间,除了最后一个之外的每个区间,都含有并结束于x的一个项目,而最后一个结束于正无穷,第一个开始于负无穷;它应用于右参数y,给出y所位于的区间的索引。
下面的例子产生符合指定离散概率分布的随机数列表,这里定义了动词ran,它依据左参数x给出的正实数向量中的这些权重,从i.#x中选取出由右参数y指定个数的随机数列表:
ran=: [: : ((I.~ (+/\ % +/))~ ?@$&0)
wt=: 7 5 6 4 7 2 0.4
# t=: wt ran 1e6
1000000
10 {. t
0 1 1 5 0 1 3 4 4 0
] r=: wt (+/@(=/ i.@#)~ % #@]) t NB. 实测的出现比率
0.222618 0.159083 0.19152 0.127394 0.222795 0.06378 0.01281
] p=: (% +/) wt NB. 期望的出现概率
0.22293 0.159236 0.191083 0.127389 0.22293 0.0636943 0.0127389
0j6 ": r - p
_0.000312 _0.000153 0.000437 0.000005 _0.000135 0.000086 0.000071
这里首先通过?@$&0,生成指定数目的在区间(0,1)中的随机浮点数,它也可以写为等价的?@($ 0:)。然后在叉子+/\ % +/中,使用一元副词\“前缀”修饰动词+/,从而计算权重向量的,再用前缀和除以总和得出累积分布函数。最后通过区间索引,在有随机浮点数落入特定区间的时候,生成这个区间对应的随机整数。
动词ran的表达式是个二层二元钩子,外层钩子的第一步运算应用到右参数上,它的第二步运算即内层钩子,整体修饰了二元副词~“被动”而对换了两个参数的位置。内层钩子的第一步运算+/\ % +/所应用的右参数实际上是外层钩子的左参数,它的第二步运算I.修饰了~,从而将它所面对的内层钩子的左右两参数,再次对换回到外层钩子即整体表达式原先的位置上。
下面是将区间索引和键分组结合起来的例子,演示了-莱维中心极限定理:
histogram =: {{
{&('.',u:16b258c) @ ((|.i.x)&(.@*&x) @ (% >./) y}}
summary=: {{ l=. 0 [ r=. 1
-&1@#/.~ @ ((i.x)&,) @ ((l+(}.i.x)*%&x(r-l))&I.) y}}
sampleMean=:
24&histogram @ (80&summary) @ (10 sampleMean (?@$&0)) 1e6
......................................▌▌▌▌......................................
.....................................▌▌▌▌▌▌.....................................
....................................▌▌▌▌▌▌▌▌....................................
....................................▌▌▌▌▌▌▌▌....................................
...................................▌▌▌▌▌▌▌▌▌▌...................................
..................................▌▌▌▌▌▌▌▌▌▌▌▌..................................
..................................▌▌▌▌▌▌▌▌▌▌▌▌..................................
.................................▌▌▌▌▌▌▌▌▌▌▌▌▌▌.................................
.................................▌▌▌▌▌▌▌▌▌▌▌▌▌▌.................................
................................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌................................
................................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌................................
...............................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌...............................
...............................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌...............................
..............................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌..............................
..............................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌..............................
.............................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌.............................
............................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌............................
............................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌............................
...........................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌...........................
..........................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌..........................
.........................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌.........................
........................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌........................
......................▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌......................
.........▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌........
lineChart=: {{
s=. (|.i.x)&(.@*&x) @ (% >./)
t=. >&0 @ (2&(-/\)) @ (2&(-~/\))"1 @ ,.&1 @ (1&,.)
{&('.',u:16b2588) @ (2&(-~/\)@(0&,) +. t) @ s y}}
24&lineChart @ (+/\) @ (80&summary) @ (10 sampleMean (?@$&0)) 1e6
....................................................████████████████████████████
..................................................██............................
................................................██..............................
...............................................█................................
..............................................█.................................
............................................██..................................
............................................█...................................
...........................................█....................................
..........................................█.....................................
.........................................█......................................
........................................█.......................................
.......................................█........................................
.......................................█........................................
......................................█.........................................
.....................................█..........................................
....................................█...........................................
...................................█............................................
...................................█............................................
.................................██.............................................
................................█...............................................
...............................█................................................
.............................██.................................................
...........................██...................................................
.........██████████████████.....................................................
这里定义的summary局部赋值了两个局部变量l和r,可以将这两个局部赋值去掉,并将这两个局部变量的出现替代为运算元m和n,如此对它的调用将变成(80&(0 summary 1))这样的形式。接下的代码结合前面两个例子,采用了不同于上例连续型均匀分布的其他分布作为独立同分布,和不同的:
ratio=: [: : (-&1@#/.~@(,~ i.)~ % #@])
10&histogram @ ((#wt)&ratio) @ (wt&ran) 1e6
random=: [: : (#@[ %~ (?@$&0@] + ((I.~ (+/\ % +/))~ ?@$&0)))
20&lineChart @ (80&summary) @ (wt&random) 1e6
20&lineChart @ (80&summary) @ (2 sampleMean (wt&random)) 1e6
20&lineChart @ (80&summary) @ (4 sampleMean (wt&random)) 1e6
SQLite插件
操纵SQLite数据库的需要安装相应插件:
load 'pacman' NB. 加载包管理器
'install' jpkg 'data/sqlite' NB. 安装SQLite数据库插件
load 'data/sqlite' NB. 加载SQLite数据库插件
getbin_psqlite_ * NB. 安装SQLite数据库的共享库
SQLite数据库的简单用例:
load 'data/sqlite' NB. 加载SQLite数据库插件
db=: sqlopen_psqlite_ '~addons/data/sqlite/db/sandp.db' NB. 打开样例数据库文件
sqltables__db * NB. 查看所有表格名字
┌─┬─┬──┐
│p│s│sp│
└─┴─┴──┘
sqlmeta__db 's' NB. 查看表格s的结构
┌───┬──────┬────┬───────┬──────────┬──┐
│cid│name │type│notnull│dflt_value│pk│
├───┼──────┼────┼───────┼──────────┼──┤
│0 │sid │text│0 │NULL │1 │
│1 │name │text│0 │NULL │0 │
│2 │status│int │0 │NULL │0 │
│3 │city │text│0 │NULL │0 │
└───┴──────┴────┴───────┴──────────┴──┘
ds=: sqlread__db 'select * from s' NB. 读取表格s,结果表格形状扁长不适合直接展示
dict=: |: @:> NB. 以字典方式显示表格数据
] rs=: dict ds
┌──────┬──────────────────────────────────┐
│sid │┌──┬──┬──┬──┬──┐ │
│ ││s1│s2│s3│s4│s5│ │
│ │└──┴──┴──┴──┴──┘ │
├──────┼──────────────────────────────────┤
│name │┌─────┬─────┬─────┬─────┬─────┐ │
│ ││smith│jones│blake│clark│adams│ │
│ │└─────┴─────┴─────┴─────┴─────┘ │
├──────┼──────────────────────────────────┤
│status│20 10 30 20 30 │
├──────┼──────────────────────────────────┤
│city │┌──────┬─────┬─────┬──────┬──────┐│
│ ││london│paris│paris│london│athens││
│ │└──────┴─────┴─────┴──────┴──────┘│
└──────┴──────────────────────────────────┘
rs -: sqldict__db 's'
1
cols=: {: @:> NB. 表格数据的诸列列表
cs=: cols ds
cs -: sqlexec__db 's'
1
('s_'&, &.> @ {. @:> ds) =: cs NB. 将表格的诸列并行赋值给添加了表名前缀的诸列名
s_status
20 10 30 20 30
s_sid
┌──┬──┬──┬──┬──┐
│s1│s2│s3│s4│s5│
└──┴──┴──┴──┴──┘
({. @:> ds) -: sqlcols__db 's'
1
reads=: ({. , (,@> &.>)@}.) @:> NB. 格式化显示表格数据
] rs=: reads ds
┌───┬─────┬──────┬──────┐
│sid│name │status│city │
├───┼─────┼──────┼──────┤
│s1 │smith│20 │london│
│s2 │jones│10 │paris │
│s3 │blake│30 │paris │
│s4 │clark│20 │london│
│s5 │adams│30 │athens│
└───┴─────┴──────┴──────┘
$ @ (2&{:: @ {:) rs NB. 第3列数据的形状
5 1
rs -: sqlreads__db 's'
1
readm=: ({. ; @ >)@{:) @:> NB. 以矩阵显示表格数据
] rs=: readm ds
┌──────────────────────┬────────────────────┐
│┌───┬────┬──────┬────┐│┌──┬─────┬──┬──────┐│
││sid│name│status│city│││s1│smith│20│london││
│└───┴────┴──────┴────┘│├──┼─────┼──┼──────┤│
│ ││s2│jones│10│paris ││
│ │├──┼─────┼──┼──────┤│
│ ││s3│blake│30│paris ││
│ │├──┼─────┼──┼──────┤│
│ ││s4│clark│20│london││
│ │├──┼─────┼──┼──────┤│
│ ││s5│adams│30│athens││
│ │└──┴─────┴──┴──────┘│
└──────────────────────┴────────────────────┘
rs -: sqlreadm__db 's'
1
cp=: '~addons/data/sqlite/db/sandp.db' ; '~/test_sandp.db'
db=: sqlcopy_psqlite_ cp NB. 复制数据库并打开复本
cls=: sqlcols__db 's' NB. 得到表格s的列名列表
dat=: ('s6';'s7') ; ('brown';'eaton') ; 40 10 ;
字典类
J语言采用命名语境实现类,采用编号语境实现对象,下面示例建立字典类:
cocurrent 'Dict'
create=:
get=:
set=:
del=:
pop=:
default=:
filt=: {~ I.@:({.@('ITEM_'&E.)@>)
len=:
list=: {{5&}.&.> filt namelist 0}}
in=:
clear=:
copy=:
destroy=: codestroy
cocurrent 'base'
在家目录中建立一个dict.ijs文件并录入上述代码,接着以如下代码建立字典对象并对其进行检视和简单操作:
load '~/dict.ijs'
conl 0 NB. 检视命名语境
┌────┬────┬─┬────────┬──────┬─────┬─┐
│Dict│base│j│jcompare│jregex│jtask│z│
└────┴────┴─┴────────┴──────┴─────┴─┘
namelist_Dict_ 3 NB. 检视Dict类的动词
┌─────┬────┬──────┬───────┬───┬───────┬────┬───┬──┬───┬────┬───┬───┐
│clear│copy│create│default│del│destroy│filt│get│in│len│list│pop│set│
└─────┴────┴──────┴───────┴───┴───────┴────┴───┴──┴───┴────┴───┴───┘
d=: create_Dict_ *
d NB. 变量保存的是盒装字符串
┌─┐
│0│
└─┘
namelist__d 0 NB. 检视d对象的名词
┌─────────┬───────┐
│COCREATOR│DEFAULT│
└─────────┴───────┘
conl 1 NB. 检视编号语境
┌─┐
│0│
└─┘
copath
参见
- 秩 (J编程语言)
引用
外部連結
*[http://www.jsoftware.com JSoftware],J的官方網站
- – Repository
*[http://www.jsoftware.com/help/learning/contents.htm Learning J] – An Introduction to the J Programming Language by Roger Stokes
*[https://www.jsoftware.com/help/jforc/contents.htm J for C Programmers] by Henry Rich
*[https://www.jsoftware.com/papers/j4apl.htm J for the APL Programmer] by Chris Burke and Roger Hui
*[https://www.jsoftware.com/ioj/ioj.htm An Implementation of J] by Roger K.W. Hui
*[https://code.jsoftware.com/wiki/APL2JPhraseBook APL to J Phrasebook]
*[https://aplwiki.com/wiki/J J - APL Wiki]
*[http://faculty.ndhu.edu.tw/~pkuo/computer/dicttw/main.htm 郭平欣譯J字典]
评论 (0)