2012年11月2日星期五

3 定义函数

3 定义函数

这是对<Learning J>(by Roger Stokes)的尝试翻译。

Roger Stokes的版权声明如下:
Copyright © Roger Stokes 2012. This material may be freely reproduced, provided that this copyright notice is also reproduced.
感谢作者的无私奉献,请您转载时也注明以上版权,也请同时注明转载地址http://corwindong.blogspot.com,我将不胜感激。

----------------------------------------------------------------------------------

J有一些内置函数,比如我们之前看到的 *+。在本节中我们看看如何通过各种各样的方法将这些内置函数组合起来,来构造我们自己的函数。

3.1 重命名

最简单的定义函数方式就是为内置函数起一个我们想要的名字。这种定义就是一个赋值式。例如,定义和内置 *: 一样的square函数
   square =: *:
   
   square 1 2 3 4
1 4 9 16 
当我们想要更好记的函数名时就可以这么做。我们还可以给一个内置函数两个不同的名字,
一个代表一元模式,另一个代表二元模式。 
   Ceiling =: >.
   Max     =: >.
Ceiling 1.73 Max 4
24

 

3.2 插入

回忆一下(+/ 2 3 4),它表示2+3+4,同样地,(*/ 2 3 4)表示2*3*4。我们可以通过赋值定义一个sum函数:
   sum =: + /
   
   sum 2 3 4
9
sum =: + /向我们展示了 +/ 表达式本身就是一个函数。 
表达式 +/ 可以解读为:“插入”(/)应用在 + 函数上,生成了一个列表求和函数。
也就是说,/ 本身是这样一种函数:它接受一个左参数,它的参数和应用结果都是函数。 

3.3 术语:动词,运算符和副词

我们已经看到了两类函数。第一类是“普通函数”,例如 +*,操作数据在J中这类函数称为“动词”。

第二类函数,比如 / ,以函数为参数,计算结果也是函数。这类函数称为“运算符”,以区分“动词”。

接受一个参数的运算符称为“副词”。副词在左边接受参数。因此对于表达式 (+ /),我们说副词 / 应用于动词 +,生成一个列表求和动词。

这些术语来自于英语语法:动词作于用事物,而副词修饰动词。

3.4 交换

我们已经看过了副词(/),现在看看另一个副词。副词 ~ 的作用是交换动词的左右参数。
'a' , 'b''a' ,~ 'b'
abba

对于二元函数f,参数为x和y,那么,
        x f~ y 相当于 y f x

再看个例子,“剩余”动词 |:2 | 7表示“7 模 2”。我们可以这样定义“求模”动词:
   mod =: | ~
   
7 mod 22 | 7
11


下面我来用几幅图说明一下副词。首先是函数f 应用于参数y 得到结果(f y)。在图中f 用矩形表示,箭头表示参数输入给函数,或者函数输出结果。每个箭头上都注明相应的表达式:












接下来是二元函数f应用于x和y得到结果(x f y):














现在来看看函数(f~),它可以展示为自身内部包含了函数f,同时交换了f的参数。














3.5 绑定

假设我们要定义一个动词double,double表示两倍(double x就是x * 2)。我们可以这样定义:
   double =: * & 2
   
   double 3
6
在这里我们绑定二元 * 的一个参数(double中的2),生成了一元函数。
运算符 & 用来绑定函数和一个参数。规则是:如果f 是一个二元函数,
k是提供给f的右参数,那么
        (f & k) y    相当于    y f k
类似地,如果我们绑定了左参数,那么规则就是:
        (k & f)  y   相当于    k f y 
 
例如,假设购物税是10%,那么通过销售价格计算税额的函数就可以定义为:
   tax =: 0.10 & *
   
   tax 50
5
 
下面的图用来说明函数k&f:


 


  





3.6 术语:连词和名词

表达式(*&2)的含义是:运算符 & 是一个函数,接受两个参数(动词 * 和数字2),得到的结果是动词“doubling”。
接受两个参数的运算符(如 &)在J中称为“连词” ,因为它将两个参数连接起来。与之相比回忆一下“副词”,副词就是只接受一个参数的运算符。

J中的每一个函数,无论是内置的还是用户自定义的,都属于以下四类之一:一元动词,二元动词,副词和连词。我们以符号 - 为例,- 代表了两个不同的动词:一元的“求负”和二元的“减法”。

J中的每一个表达式都是某个类型的值,所有不是函数的值都算作数据(实际上就是阵列,就像前面章节看到的)。

在J中,数据值,也就是阵列,按照英语语法的类比,称为“名词”。我们说某个事物是名词时强调它不是动词,说它是阵列时强调它有多个维度。

3.7 函数的组合

让我们看看汉语表达式:求数字1 2 3的平方和,即(1+4+9)等于14。我们之前定义过动词sum和suqare,现在就可以直接使用:
   sum square 1 2 3
14

也可以通过组合sum和square得到一个单独的求平方和函数:
   sumsq =: sum @: square
   
   sumsq 1 2 3
14
符号 @:(at符 冒号)叫做“组合”运算符。该运算符的规则是,如果f和g都是动词,那么
对于任何参数y:        
        (f @: g) y    相当于  f (g y)
 
下图用来说明该规则: 

读者可能会好奇,为什么不简单地使用(f g)来代表组合,而要使用(f @: g)呢?这是因
为(f g)另有含义,我们一会儿就会看到。
 
再来一个组合的例子:华氏温度可以通过减去32然后乘以5%9来转换成摄氏温度:
   s       =: - & 32
   m       =: * & (5%9)
   convert =: m @: s
 
s 212m s 212convert 212
180100100


为了清晰性,上例中用两个有名字的函数组合起来,其实我们也可以直接组合表达式:
   conv =: (* & (5%9)) @: (- & 32) 
   conv 212
100
 
我们还可以直接应用代表了函数的表达式,而不需要有名字:
   (* & (5%9)) @: (- & 32)  212
100  

上例中展示了组合两个一元函数。下面我们来展示一下组合一元函数和二元函数。通用规则为:       
         x (f @: g) y   相当于    f (x g y)
举个例子:要计算订单的总价,那么可以把每种物品的单价乘以数量的到物品的总价,再将
所有物品的总价加起来,得到订单总价:
   P =:  2 3        NB. prices
   Q =:  1 100      NB. quantities 
   
   total =: sum @: *
   
PQP*Qsum P * QP total Q
2 31 1002 300302302

关于组合的更多信息,请参考第8章。


3.8 动词链

看一下这个表达:“no pain, no gain”。这是一个简洁的习语,尽管语法结构不很完整但是很好理解。它不是一个句子,没有主动词。J也有类似记法:一种简洁的惯用结构,基于某种内定的规则,这规则定义了几个函数连接在一起时的语义。

3.8.1 挂钩(Hooks) 

回忆一下之前定义的tax动词,用来计算一次购物的税额(10%):
   tax =: 0.10 & *
 
一次购物要付的总金额就是销售价格加上税额。用来计算总金额的动词可以这么定义:
   payable =: + tax
 
如果销售价格是50美元,那么:
tax 5050 + tax 50payable 50
55555


在定义(payable =: + tax)中,我们使用了动词序列:+ 后面跟着tax。这个动词序列是孤立的,通过放在赋值式的右边达到孤立效果。这样一个孤立的动词序列叫做一个“链”,2个动词的链称为“挂钩”。

我们也可以通过将两个动词孤立在括号中来形成挂钩:
   (+ tax) 50
55

挂钩的通用规则是:如果f是二元函数,g是一元函数,那么对于任意的参数y,有
        (f g) y 相当于 y f (g y)

该规则可以用下图说明:

再看一个例子,回忆一下“下取整”动词 <.(计算其参数的整数部分)。测试一个数字是不是整数,可以通过测试它是否和自身取整后的结果相等来完成。动词“是否等于下取整”可以用挂钩来定义 (= <.):
   wholenumber  =:  = <.
   
y =: 3 2.7<. yy = <. ywholenumber y
3 2.73 21 01 0

3.8.2 分叉(Forks)

现在要计算一列数字L 的算术平均数:用数列L的和除以L的元素个数(还记得吗?求元素数可以使用一元动词 #)。
L =: 3 5 7 9sum L# L(sum L) % (# L)
3 5 7 92446

用来求算术平均数的动词可以这样定义,将三个动词链接起来:sum % # 。
   mean =: sum % #
   
   mean L
6
孤立的三动词序列称为分叉。通用规则为:如果f是一元动词,g是二元动词,h是一
元动词,那么对于任意参数y,有:
        (f g h) y     相当于   (f y) g (h y) 

该规则可以用下图说明:


我们再用一个例子展示一下分叉:一个数列的范围可以用分叉  
smallest , largest 来表示(中间的动词是“追加” ,)。
回忆一下,在第1章中,求一列数字的最大值用动词 >./,最小值则用 <./
   range =: <./  ,  >./
   
Lrange L
3 5 7 93 9


挂钩和分叉是动词序列,也叫做“动词链”。关于动词链的更多内容,请参考第9章。

 

3.9 综合回顾

我们用一个大一点的例子把上面学到的概念串起来。
这个例子的目的是定义一个动词,用来展示一列数字,显示每个数字占总数的百分比。

一开始我先给出完整的程序,这样后面就可以清楚地知道我们到了哪一步。现在不需要探究具体的细节,因为在下面会给出程序的解释。现在只需要知道我们看到的是一个6行的程序,定义了一个叫做 display 的动词以及一些辅助动词。
   percent  =: (100 & *) @: (% +/)
   round    =: <. @: (+&0.5)
   comp     =: round @: percent
   br       =: ,.  ;  (,. @: comp)
   tr       =: ('Data';'Percentages') & ,
   display  =: (2 2 & $) @: tr @: br
 
我们从简单数据开始:
   data =: 3 5 
然后看一下display动词显示每个数字及其占总数的百分比(四舍五入):3是8的38%。
   display data
+----+-----------+
|Data|Percentages|
+----+-----------+
|3   |38         |
|5   |63         |
+----+-----------+

动词percent计算百分比,把每个数除以总数(通过挂钩 % +/)然后乘以100。为了免去来回
翻页的麻烦,在这里重复一下percent的定义:
   percent  =: (100 & *) @: (% +/)
看一下percent的效果:
data+/ datadata % +/ data(% +/) datapercent data
3 580.375 0.6250.375 0.62537.5 62.5


让我们把百分比四舍五入成整数:通过加上0.5然后用动词 <. 向下取整。动词round定义为:
   round    =: <. @: (+&0.5)
 
用来计算要显示的百分比的动词为:
   comp     =: round @: percent
   
datacomp data
3 538 63


我们想要数据竖着显示。要将一个列表转换成1列阵列,可以使用内置动词 ,.(逗号 点,叫做“散开元素”)。
data,. data,. comp data
3 53
5
38
63


为了构造显示结果中底下那一行,我们定义动词br,br是一个分叉,将数据和计算结果以列形式连接起来:
   br  =: ,.  ;  (,. @: comp)
   
databr data
3 5+-+--+
|3|38|
|5|63|
+-+--+

为了构造显示结果中上面那一行(标题栏),我们可以这么操作:底下那一行可以看作是两
个盒子的列表,在这个序列的前边我们在加入两个盒子用来表示标题栏,一共是4个盒子。
我们定义动词 tr 来完成上述操作:
 
   tr  =: ('Data';'Percentages') & , 

databr datatr br data
3 5+-+--+
|3|38|
|5|63|
+-+--+
+----+-----------+-+--+
|Data|Percentages|3|38|
|    |           |5|63|
+----+-----------+-+--+


剩下要做的事就是将4个盒子的列表转换成2x2的阵列:
   (2 2 & $)  tr br data
+----+-----------+
|Data|Percentages|
+----+-----------+
|3   |38         |
|5   |63         |
+----+-----------+
 
将这些整合起来:
   display =: (2 2 & $) @: tr @: br
   
   display data
+----+-----------+
|Data|Percentages|
+----+-----------+
|3   |38         |
|5   |63         |
+----+-----------+

display函数包括两大部分:函数comp完成计算部分(四舍五入后的百分数),剩下的函数完
成数据的显示工作。通过改变comp的定义,我们可以显示其他函数的计算结果。假如我们将
comp定义为内置的求平方根函数(%:):
   comp =: %:
同时替换标题栏,修改tr函数:
   tr   =: ('Numbers';'Square Roots') & ,
   
   display 1 4 9 16
+-------+------------+
|Numbers|Square Roots|
+-------+------------+
| 1     |1           |
| 4     |2           |
| 9     |3           |
|16     |4           |
+-------+------------+
 
总结一下,我们已经通过一小段程序看到了J的一些标志性的特性:绑定,组合,挂钩和分叉。
至于这整个程序,这里展示的只是众多写法中的一种。
 
本章中我们首先学习了如何定义函数。存在两类函数:动词和运算符。目前我们只关注如何
定义动词,在下一章中我们将看到另一种定义动词的方法。而在第13章中我们将看到如何定
义运算符。
 
好了,就到这里。 

2012年10月30日星期二

2 列表(Lists)和表格(Tables)

2 列表(Lists)和表格(Tables)

这是对<Learning J>(by Roger Stokes)的尝试翻译。

Roger Stokes的版权声明如下:
Copyright © Roger Stokes 2012. This material may be freely reproduced, provided that this copyright notice is also reproduced.
感谢作者的无私奉献,请您转载时也注明以上版权,也请同时注明转载地址http://corwindong.blogspot.com,我将不胜感激。

----------------------------------------------------------------------------------

计算需要数据,目前为止我们只看到单个数字或者一列数字。我们也可以把其他东西作为数据,例如表格。列表和表格统称为“阵列”。

2.1 表格

一个2行3列的表格可以通过 $ 函数构建:
   table =: 2 3   $   5 6 7  8 9 10
   table
5 6  7
8 9 10
表达式(x $ y)构造了一个表格。表格的维数通过列表x给出,x中第一个元素是表格的行数,
第二个元素是表格的列数。表格的元素由列表y给出。
 
y中的元素按顺序填充表格的第一行、第二行等等。列表y必须至少包含一个元素。如果y中
的元素数目不够填充整个表格,那么将从头开始重新使用y。
2 4 $ 5 6 7 8 92 2 $ 1
5 6 7 8
9 5 6 7
1 1
1 1

函数 $ 提供了一种构建表格的方法,还有很多其他的方式,参看第5章。

如同之前我们看到的列表一样,函数也可以应用于整个表格:
table10 * tabletable + table
5 6  7
8 9 10
50 60  70
80 90 100
10 12 14
16 18 20


也可以一个参数是表格,另一个是列表:
table0 1 * table
5 6  7
8 9 10
0 0  0
8 9 10
在这个例子中,列表0 1的元素和表格的行进行匹配,0匹配第一行,1匹配第二行。参数之间也存在其他的匹配方式,参见第7章。

2.2 阵列

表格有两个维度:行和列。类似的,列表可以看作只有一个维度。
我们还可以构造类似表格但维数大于2的数据。$ 函数的左参数可以是一个包含任意维数的列表。拥有维数的数据对象统称为“阵列”。下面示范一维、二维、三维阵列:
3 $ 12 3 $ 5 6 72 2 3 $ 5 6 7 8
1 1 15 6 7
5 6 7
5 6 7
8 5 6

7 8 5
6 7 8
上例中的三维阵列有2个平面,每个平面2行,3列,两个平面上下挨着显示。

回忆一下,一元函数 # 给出一个列表的长度:
# 6 7# 6 7 8
23
一元模式的 $ 给出阵列的维度表:
L =: 5 6 7$ LT =: 2 3 $ 1$ T
5 6 731 1 1
1 1 1
2 3
因此,如果x是一个阵列,表达式(# $ x)将得到x维度表的长度,也就是x的维数。列表的维数是1,表格的维数是2,等等。
L$ L# $ LT$T# $ T
5 6 7311 1 1
1 1 1
2 32


如果x只是一个数字,那么表达式(# $ x)的值为0:
   # $ 17
0
这意味着:尽管表格的维数是2,列表的是1,但单个数字没有维数,因为它的维度计数是0。一个维数为0的数据对象被称为标量。我们说“阵列”是有维数的数据对象,标量也是阵列,是维数为0的特殊阵列。

表达式(# $ 17)值为0。由此我们得出:既然标量的维数为0,那么它的维度表($ 17)必然长度为0(空列表)。既然一个长度为2的列表可以通过表达式2 $ 99来构造,那么长度为0的列表就可以通过 0 $ 99(或者 0 $ 任意数字)来构造。
空列表的值显示时没有输出:
2 $ 990 $ 99$ 17
99 99


需要注意的是,标量(例如17)和只有一个元素的列表(例如 $ 17),或者一个1行1列的表格(比如 1 1 $ 17)是不同的。标量维度为0,列表维度为1,表格维度为2。但是这三个显示时看起来一样:
   S =: 17
   L =: 1 $ 17
   T =: 1 1 $ 17
SLT# $ S# $ L# $ T
171717012


一个表格可能只有一列,但维数依然为2。例如:t有3行1列:
t =: 3 1 $ 5 6 7$ t# $ t
5
6
7
3 12


2.3 术语:秩(Rank)和形状(Shape)

我们之前所说的“维数”在J中叫做“秩”,因此单个数字称为0秩阵列,一列数字称为1秩阵列,等等。一个阵列的维度列表称为阵列的“形状”。

我们使用的“列表”和“表格”(元素为数字)在数学中称为“向量”和“矩阵”。有着3个或者更多维度的阵列(也可以说,秩为3或者更高的阵列)称为“报表”。

下面的表格总结了和阵列相关的术语与函数:
+--------+--------+-----------+------+
|        | Example| Shape     | Rank |
+--------+--------+-----------+------+
|        | x      | $ x       | # $ x|
+--------+--------+-----------+------+
| Scalar | 6      | empty list| 0    |
+--------+--------+-----------+------+
| List   | 4 5 6  | 3         | 1    |
+--------+--------+-----------+------+
| Table  |0 1 2   | 2 3       | 2    |
|        |3 4 5   |           |      |
+--------+--------+-----------+------+
| Report |0  1  2 | 2 2 3     | 3    |
|        |3  4  5 |           |      |
|        |        |           |      |
|        |6  7  8 |           |      |
|        |9 10 11 |           |      |
+--------+--------+-----------+------+ 
这张表格实际上是用一小段J代码生成的是一个真实的“表格”,也就是我们之前讨论的那
种表格。它的形状是6 4。然而,它不仅仅是数字表格,它还包含词语,数字列表等等。下来
我们讨论非数字阵列。  
 

2.4 字符阵列

字符包括字母、标点符号、数字字符等等。我们可以构造字符阵列,如同构造数字阵列一样。一列字符输入时使用单引号引起来,但是显示时没有单引号,例如:
   title =: 'My Ten Years in a Quandary'
   title
My Ten Years in a Quandary
一列字符称为字符串。字符串中的单引号需要输入两个连续的单引号来表示:
   'What''s new?'
What's new?


一个空的(长度为0)的字符串通过两个连续的单引号表示,显示时什么也没有:
''# ''
0


2.5 一些阵列函数

现在是时候探究一下操纵阵列的函数了。J有着非常丰富的阵列函数:这里我们仅仅展示一小部分。

2.5.1 连接(Joining)

内置函数 ,(逗号)称为“追加”。它将事物连接起来构成列表。
a =: 'rear'b =: 'ranged'a,b
rearrangedrearranged


“追加”函数连接列表或者单个元素。
x =: 1 2 30 , xx , 00 , 0x , x
1 2 30 1 2 31 2 3 00 01 2 3 1 2 3


“追加”函数也可以将两个表格首尾相连构成更长的表格:
T1=: 2 3 $ 'catdog'T2=: 2 3 $ 'ratpig'T1,T2
cat
dog
rat
pig
cat
dog
rat
pig

关于“追加”函数更多的信息请参考第5章。


2.5.2 元素

数字列表的元素是这些数字,表格的元素是表格的行。3维阵列的元素是它的平面。更一般地,我们说一个阵列的元素就是该阵列第一个维度上顺序出现的东西。阵列也就是阵列元素的列表。

回忆一下内置函数 #(“总数”)用来求列表的长度。
x# x
1 2 33


一般来说,# 计算阵列的元素数目,也就是说,它给出的是阵列的第一维度:
T1$ T1# T1
cat
dog
2 32


# T1也是T1维度表的第一个元素。标量,维度为0,被当作单独一个元素:
   # 6
1


再回到之前给出的“追加”例子:
T1T2T1 , T2
cat
dog
rat
pig
cat
dog
rat
pig
现在我们可以看出(x, y)实质上就是一个顺序包含了x和y元素的列表。

再来看一个展示“元素”这个概念用途的例子,回忆一下函数 +/+ 插入列表的元素之间。
+/ 1 2 31 + 2 + 3
66

现在我们可以说 +/ 其实+ 插入阵列的元素之间。下面这个例子中的元素是阵列的行:
T =: 3 2 $ 1 2 3 4 5 6+/ T1 2 + 3 4 + 5 6
1 2
3 4
5 6
9 129 12


2.5.3 选择

现在我们看看如何从列表中选取元素。列表的位置从0开始:0,1,2等等 。通过位置选择元素的函数是 { (左大括号,叫做“来自”)。
Y =: 'abcd'0 { Y1 { Y3 { Y
abcdabd


位置号也称为索引。{ 函数的左参数可以是单个索引,也可以是索引列表:
Y0 { Y0 1 { Y3 0 1 { Y
abcdaabdab


看看内置函数 i.(i点号)。表达式(i. n)从0开始生成n个连续数字:
i. 4i. 61 + i. 3
0 1 2 30 1 2 3 4 51 2 3


如果x是个列表,那么表达式(i. # x)生成列表x的所有索引:
x =: 'park'# xi. # x
park40 1 2 3


如果 i. 参数是列表,那么将生成相应的阵列:
   i. 2 3
0 1 2
3 4 5


i. 还有二元模式,叫做“求索引”。表达式(x i. y)找到y在x中的索引位置:
   'park' i. 'k'
3
如果y不在x中,那么将返回x的最大索引加1:
   'park' i. 'j'
4
 
更多的索引方法请参考第6章。
 

2.5.4 相等和匹配

如果我们要检查两个阵列是否相等,可以使用内置函数 -:(减号 冒号,叫做“匹配”)。“匹配”函数测试它的两个参数是否有相同的形状,以及相应的元素是否相同。
X =: 'abc'X -: XY =: 1 2 3 4X -: Y
abc11 2 3 40


不管“匹配”函数的参数是什么,它的结果一定是单个0或者1。

注意一下,空列表和空列表是匹配的。比如空的字符串和空的数字列表:
   '' -: 0 $ 0
1
因为两个列表有相同的形状,而且相应的元素也相同(两个都没有元素)。 

还有另一个函数 =(叫做“相等”)用来测试它的两个参数是否相等。= 逐个比较两个参数的元素,生成一个和参数的形状相同,只包含比较结果的阵列:
YY = YY = 2
1 2 3 41 1 1 10 1 0 0
因此,= 的两个参数形状必须相同(或者至少要兼容,比如 Y=2)。否则就会报错:
YY = 1 5 6 4Y = 1 5 6
1 2 3 41 0 0 1error



2.6 盒子(Boxes)阵列

2.6.1 拼接(Linking)

有一个内建函数 ;(分号,称为“拼接”)。它将两个参数拼接成一个列表,两个参数可以是不同的种类(这一点和“追加”函数不同)。 例如我们将字符串和数字拼接起来:
   A =: 'The answer is'  ;  42
   A
+-------------+--+
|The answer is|42|
+-------------+--+
A是一个长度为2的列表,是一个“盒子”的列表。A的第一个盒子中是字符串'The answer is',第二个盒子中是数字42。盒子在屏幕上显示为一个矩形,盒中的值显示在矩形中。
A0 { A
+-------------+--+
|The answer is|42|
+-------------+--+
+-------------+
|The answer is|
+-------------+


一个盒子是一个标量,不管里面装的是什么;因此盒子可以像数字一样装入阵列中。所以,A是一个标量列表。
A$ As =: 1 { A# $ s
+-------------+--+
|The answer is|42|
+-------------+--+
2+--+
|42|
+--+
0

盒子阵列的主要目的是为了能够将多种不同的值装入单个变量中。例如,一个保存购物单(日期,数量,描述)的变量就可以是一个盒子列表:
   P =: 18 12 1998  ;  1.99  ;  'baked beans'
   P
+----------+----+-----------+
|18 12 1998|1.99|baked beans|
+----------+----+-----------+


注意“拼接”和“追加”的不同。“拼接”可以连接不同种类的值,“追加”只能连接同一种类的值。也就是说,“追加”的两个参数要么都是数字阵列,要么都是字符阵列,要么都是盒子阵列,等等。否则就会报错:
'answer is'; 42'answer is' , 42
+---------+--+
|answer is|42|
+---------+--+
error


有时我们想要将字符串和数字结合在一起,比如要表达计算结果和一些描述。我们可以“拼接”描述和数字,就像上面看到的。但是更自然的方式是将数字转换成字符串,然后将两个字符串“连接”起来。
可以使用内置函数“格式化” ":(双引号 冒号)来将数字转换为字符串。下例中的n是一个数字,s是n的格式化结果—— 一个长度为2的字符串:
n =: 42s =: ": n# s'answer is ' , s
42422answer is 42


关于“格式化”的更多信息,请参考第19章。现在让我们回到盒子主题。因为盒子显示时有矩形轮廓,因此它们在屏幕上显示时可以达到表格的效果。
   p =: 4 1 $ 1 2 3 4
   q =: 4 1 $ 3 0 1 1
   
   2 3 $ ' p ' ; ' q ' ; ' p+q ' ;  p ; q ; p+q
+---+---+-----+
| p | q | p+q |
+---+---+-----+
|1  |3  |4    |
|2  |0  |2    |
|3  |1  |4    |
|4  |1  |5    |
+---+---+-----+

2.6.2 装盒和拆盒

内建函数 <(左尖括号,叫做“装盒”),可以用来构造盒子。将 < 应用于待装入的值将得到一个盒子。
   < 'baked beans'
+-----------+
|baked beans|
+-----------+


尽管盒子可以装数字,但是盒子本身并不是数字。想要对盒子中的值进行计算,就需要“拆开”盒子取出值。函数 >(右尖括号)就是“拆盒”。
b =: < 1 2 3> b
+-----+
|1 2 3|
+-----+
1 2 3


可以把 < 看作一个漏斗。流向宽的那头我们得到数据,流向窄的那头我们得到盒子,盒子是标量,没有维数,也可以看作一个点。同样的类比也适用于 >(方向相反)。既然盒子是标量,那么可以通过“追加”函数将盒子串起来形成列表。但是“拼接”函数更方便一些,因为它将“装盒”和串接一起完成。
(< 1 1) , (< 2 2) , (< 3 3)1 1 ; 2 2 ; 3 3
+---+---+---+
|1 1|2 2|3 3|
+---+---+---+
+---+---+---+
|1 1|2 2|3 3|
+---+---+---+

 

 

2.7 总结

总之,每一个数据对象在J中多是一个阵列,有0个,1个或者更多维度。一个阵列可以是数字阵列,字符阵列,或者盒子阵列(还有其他高级阵列)。

这里就到了第二章结尾了。


说明:
  1. join和link这两个词都有连接,连合之意,但是在J中指的是两种不同的操作,因此我将join翻译为连接,将link翻译为拼接,貌似比较合适。