J语言
来自维基百科,自由的百科全书
J语言,是一种阵列编程语言,由肯尼斯·艾佛森和许国华于1990年代初发明。J语言是APL语言的一种方言[5][6],延续了APL鲜明的简洁性,它在数学和统计学程序设计上十分高效,特别是在需要进行矩阵运算的场合。
J语言最初起步于肯尼斯·艾佛森在1987年发表的《APL字典》[7],它实现了其中至关重要的秩的概念[8]。J语言提供隐式定义机制包括秩、钩子[9]、叉子[10]和多种函数复合[11],并介入了作为头等对象的动名词,用以建立控制结构[12],它常被作为隐式编程的典范之一[13]。
简介
Ken Iverson(右)和Roger Hui在1996年的照片[14]
J语言的运算符,承袭APL传统,没有优先级并且最右先行,2 * 3 + 4
按照2 * (3 + 4)
来运算。以历史上APL使用的典型符号为例,符号/
被用来指示折叠函数foldr1
,所以+/1 2 3
等价于1 + (2 + 3)
;在APL中,除法被表示为数学除号÷
,它将减号和冒号一起重复打印在EBCDIC和ASCII二者的纸质文本终端上;J语言使用%
表示除法,是对除号的一种近似或提示。
为了避免APL使用特殊的字符而遇到的问题,J语言只需基本的ASCII字符集,但使用点号.
和冒号:
作为“屈折”[15]。点号和冒号除了前导着空白字符的情况之外,都与紧前字符形成类似双字符组的短字。多数“基础”或“原始”的J单字,都充当数学符号,通过点号或冒号来扩展这些可用基本字符的含义。在其他语言中经常是成对的很多字符,比如[] {} "" `` <>
,在J语言中被当作单独的字,或者在有屈折的时候,作为多字符字的单字符字根。
J语言不再支持从1968年的APL\360就有的[;]
形式的方括号索引,转而支持叫做“来自”(from)的索引机制[16],它起源自Kenneth E. Iverson于1978年在《算子和函数》中提出的,依据基数解码定义[17],并用符号⌷
表示的索引[18]。
J语言承袭IBM APL\360采用了平坦阵列模型[19],不支持由NARS(嵌套阵列研究系统)于1981年介入[20],并被IBM APL2所采纳的嵌套阵列模型[21];J语言增加了Kenneth E. Iverson于1978年在《算子和函数》中提出的盒装数据类型[22],它由SHARP APL于1981年介入,并于1983年在I. P. Sharp协会研究报告《理性化APL》中,列入与APL2相比较的“限定子集”(RS)而着重强调[23]。
J语言支持AVX2指令集进行SIMD运算[24]。为了包装用面向对象编程语言开发的API和框架,J语言提供了层级命名空间机制[25],这里所有名字都存在于特定语境(locale)中[26],可以避免软件包之间的名字冲突,并能有效的用作基于类的面向对象编程的框架[27]。
J语言解释器默认装载标准库[28]。通过包管理器[29],还可以安装各种插件[30],如果是在管理员权限下安装的J语言解释器,则安装插件也需要同样的管理员权限。J语言拥有常规调试机制,还有叫做Dissect的可视调试器[31]。除了科学计算和统计分析,它还被用于关系数据库管理系统如Jd[32]、极限编程[33]和网络性能分析[34]。
2011年3月,J语言采用了GNU通用公共许可证版本3,从而成为自由和开源软件[35],人们还可以在Jsoftware的商业许可证下利用源代码[36]。
文档与词类
J语言的文档包括在官网的NuVoc中[37],在其中将主要的字罗列为“J原语”,并使用颜色标示出它们分别的词类[38]。早期的文档还有入门和字典。在J语言中的字,被识别为名词[39]、动词[40]、定语[41](副词和连词)、系词、标点、控制字。一个程序或例程,如果接受数据作为输入并产生数据作为输出,则被称为“动词”,与之相对,数据参数被称为“名词”。
J术语 | APL术语 |
---|---|
名词 | 阵列 |
动词 | 函数 |
副词,连词 | 算子 |
字母 | 字符集 |
单词构成 | 词法分析(lexing) |
标点 | 控制结构,括号界定等 |
句子 | 表达式 |
字典 | 参考手册 |
隽语(epigram) | 单行代码(one-liner) |
动词有两种形式:只有右侧一个参数的一元(monad)形式,和有左右两侧参数的二元(dyad)形式,例如在-1
中减号是一元动词,而在3-2
中减号是二元动词。J语言预定义了很丰富的动词,它们都自动的作用于多种数据类型之上。用户定义的程序可以自行命名,并用在任何允许使用原始动词的地方。无论原始动词还是派生动词,它们的一元定义与二元定义,在很大程度上是独立的。
起步示例
J语言可以写出非常精简的程序,特别是存在重度的对符号的函数重载,以至于一些编程者将它称为难以阅读的只写语言。在计算机的终端上执行ijconsole
,即可进入J语言的REPL解释器界面。
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传统的两步骤方式[42],使用一元动词/:
“升序索引”(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
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. 10x ^50 NB. π乘以扩展精度10的50次幂
314159265358979323846264338327950288419716939937510
这里采用一元动词o.
“π乘以”(pi times),和一元动词<.
“下取整”(floor)二者的复合,得到预期的结果[44]。位的列表可以使用一元动词#.
“基数2”(base 2)解码成整数[17]。整数可以使用一元动词#:
“反基数2”(antibase 2)编码为位的列表。
J语言还支持文字即字符类型。文字包围在撇号'
之间,比如'a'
或'b'
。文字的列表,通过将多个字符用撇号包围起来的常规字符串约定来表示,比如'abcdefg'
。在字符串内的''
表示'
字符本身。单个的文字,典型的是8
位宽即单字节的ASCII字符,此外J语言还支持Unicode文字。
不支持在文字上的数值和布尔运算,但支持面向搜集的运算,比如旋转等。使用动词".
“执行·数值”(do·numbers),将字节阵列转换成数值;使用动词":
“缺省格式·格式”(default format·format),将数值转换成字节阵列。
盒装类型的值是0维标量[22],而不管所包含的是怎样的数据结构。使用一元动词<
“盒装”(box),将数据放置入盒子中;使用一元动词>
“打开”(open),打开盒子中取出其中数据。还可以通过二元动词;
“链接”(link)建立盒子的列表,通过一元动词;
“拆除”(raze)移除一层盒子的列表。盒子内可以装入其他盒子,还可以通过二元动词$
“重制形状”(reshape)和二元动词#
“计件复制”(copy)等操作盒子及其列表。
J语言的阵列,具有同质(homogeneous)的项目类型,例如列表1 2 3
是整数的列表,尽管1
还可以是一个位。这种类型问题,在极大程度上对于编程者是透明的。只有特定的特殊运算,会显露出在类型上的不同。例如列表1.0 0.0 1.0 0.0
,对大多数运算,将被当作是完全同于列表1 0 1 0
。
J语言支持数值稀疏阵列,通过它们的下标存储非零数值。这在非零数值相对很少的情况下,是有效率的机制。
简要词汇表
下面的表格简要列出了常用词汇。如果含义中用了间隔号( · )分隔,通常前者是只有一个右侧参数的一元含义,后者是左右两侧都有参数的二元含义。列出的对应APL符号,是Dyalog等现代APL所采用的符号。
定语
定义
索引
示例
参见
引用
外部链接
Wikiwand - on
Seamless Wikipedia browsing. On steroids.