2012年10月28日星期日

1 基础

这是对<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,我将不胜感激。

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

1 基础

1.1 交互式使用

用户在键盘上敲击一行,输入的可以是表达式,例如:2+2。当该行内容被输入(按了“输入”或者“回车”键),表达式的值将被计算同时显示在下一行上。
   2+2
4 
然后解释器将提示等待输入新的行你可以在距离左边界几个空格的地方看到提示光标。
因此在本书中由几个空格缩进的行代表用户的输入,接下来的那一行(没有缩进),
代表相应的输出。
 

1.2 算术

乘法符号是 *(星号)
    2*3
6
如果我们再试一次,这次输入:2 space * space 3
    2 * 3
6
结果和之前一样,表示这里的空格是可选的。空格可以使表达式可读性好一些。

除法符号是 %(百分号)
    3 % 4
0.75

减法符号依然是我们熟悉的 -
    3 - 2
1
下面这个例子展示负数是如何表示的。负号是在负数开头来一个 _(下划线),下划线和数字之间没有空格。负号不是一个数值函数:它就是一种数字记法,和十进制小数点一样,只是一种记法。
    2 - 3
_1  

求负数的符号也是 - ,和减法符号一样:
    - 3
_3 

幂函数符号是 ^(插入符)。2的立方是8:
    2 ^ 3
8 

平方函数的符号是 *:(星号+冒号)。
    *: 4
16

1.3 一些术语:函数(Function)、参数(Argument)、应用(Application)和值(Value)

以表达式2 * 3为例。我们说乘法函数 * 被应用于它的参数。左参数是2,右参数是3。同时,2和3也被称为参数的值。

1.4 列表(List)型值

有时我们想要把同一个运算应用在一些不同的参数上。例如,一列数字可以表示为 1 2 3 4,数字之间用空格分隔。想要求每个数字的平方,我们可以:
    *: 1 2 3 4
1 4 9 16
我们可以看到“平方”函数(*:)应用到列表中的每个值上。如果把两个列表作为一个函数(例如 +)的参数,那么该函数将应用到每对相应的元素上:   
    1 2 3 + 10 20 30
11 22 33
如果一个参数是列表,另一个是单元素,那么单元素将按照需要被复制:
   1 + 10 20 30
11 21 31
      
   1 2 3 + 10
11 12 13  
 
这个特性有时很有用,比如我们遇到一个新函数,想要看看一列参数的模式能在结果中引
起什么样的模式。
例如,7除以2,商3余1。J内置求余数的函数是 |(竖线),叫做“剩余”函数。参数和
结果的模式如下:
   2 | 0 1 2 3 4 5 6 7
0 1 0 1 0 1 0 1
      
   3 | 0 1 2 3 4 5 6 7
0 1 2 0 1 2 0 1
“剩余”函数和“求模”函数类似,只不过我们写的是(2 | 7)而非(7 mod 2) 
 

1.5 圆括号

表达式可以包含括号,括号的含义和通常一样;括号中实际上就是一个分隔出来的小运算,如:
    (2+1)*(2+2)
12
括号并非必须的。然而,考虑J表达式:3*2+1。它意味着(3*2)+1,等于7吗?还是意味着3*(2+1)等于9呢?
   3 * 2 + 1
9
学校的数学课中我们学到的规则是:乘法优先级比加法高。该规则使得我们可以减少括号
的数量。
但是在J中,没有乘法优先级比加法高这样的规则。如果需要,我们得加上括号。在J中,
有一条括号省略规则,一如3*2+1展示的。该规则是:在没有括号的情况下,算术函数的
右参数是所有它右边的。因此在3*2+1中,*的右参数是2+1。再例如: 
   1 + 3 % 4 
1.75
可以看到 % 先于 +,也就是说,最右边的函数先应用。
 
“最右边优先”的规则和通常的“乘法先于加法”不同,但是扮演的角色是相同的。它只
是一种便利性约定:你可以忽略它转而使用括号。它的便利性在于:在J中,有很多(大概
100个)函数用于数值计算,试图记住哪个函数比哪个函数优先级高是不可能的事。
 
在本书中,我有时会展示一些含有括号的表达式,这些括号在“最右边优先”规则下是不
需要的。这么做的目的在于强调表达式的结构,通过括号分开表达式的各个部分,使之更
具可读性。

1.6 变量和赋值

汉语语句——让x为100——在J中可以表达为:
    x =: 100
该表达式称为赋值,使得值100被赋予一个名字x。我们创建了变量x并使之具有值100。如果一行输入只包含了一个赋值,当把该行输入解释器时,响应中不会有任何显示。

被赋值的名字在接下来的计算中随时可用。
    x - 1
99
 
赋值式中的值本身也可以通过表达式计算:
    y =: x - 1  
此时y被用来记录x-1的计算结果。要看一个变量被赋予了什么值,直接输入该变量的名字
即可。变量名也是一个表达式,只不过形式特别简单:
    y
99 
 
一个变量可以被多次赋值,新值将覆盖旧值:
   z =: 6
   z =: 8
   z
8
变量的值也可以用来计算该变量的新值:
   z =: z + 1
   z
9
之前说过只包含赋值式的行输入时,响应中没有任何值。然而,赋值本身是表达式:它有
一个值,可以用在更大的表达式中。
   1 + (u =: 99)
100
   u
99
 
下面是一些赋值式例子,用来我们展示如何挑选变量名:
   x                       =: 0
   X                       =: 1
   K9                      =: 2
   finaltotal              =: 3
   FinalTotal              =: 4
   average_annual_rainfall =: 5   
每个名字都必须以字符开头。名字只可以包含字符(大小写均可),数字和下划线(_)。
注意大小写是区分的,x和X代表的变量不同。
   x
0
   X
1    

1.7 术语:一元和二元

只接受右边一个参数的函数称为一元函数,或者简称monad。例如“平方”,(*:)。接
受两个参数(一左一右)的函数称为二元函数或者dyad。例如 + 。

减法和求负展示了同一个符号(-)代表了两个不同的函数。换句话说,我们可以说 - 有
一个一元模式(求负)和一个二元模式(减法)。基本上所有的J语言内置函数都有一元
和二元两种模式。再举个例子:除法函数 %,二元模式是除法,一元模式求导数。
   % 4
0.25 
 

1.8 更多内置函数

本节的目的是通过观察一些内置的J函数来展示J语言编程的感觉。
看看汉语表达式:将数字2,3,4一起加起来,或者简洁地说:
加 一起 2 3 4
我们期望得到的结果9. 这个表达式在J中为:
   + / 2 3 4
9
对比汉语和J,“加”表示为 +,“一起”表示为 /。类似的,表达式: 
乘 一起 2 3 4
的结果为24。该表达式用J表示为:
   * / 2 3 4
24
我们发现+/2 3 4意味着2+3+4;*/2 3 4意味着2*3*4。符号 / 称为“插入”,因为它将左
侧的任何函数插入右侧列表的各个元素之间。通用规则为:如果F是一个二元函数,L是数字a,
b,c,... y,z组成的列表,那么:
    F / L     表示    a F b F ... F y F z 
 
让我们继续前行,看看新的函数。考虑如下三个命题:
    2比1大  (毋庸置疑)
    2和1相等(假的)
    2比1小  (假的) 
在J中,数字1表示“真”,数字0表示假。上面的三个命题在J中表示为:
    2 > 1
1
   
    2 = 1
0
    
    2 < 1
0
 
如果x是一列数字,比如:
    x =: 5 4 1 9    
我们想知道:x中哪些数字比2大?
    x > 2
1 1 0 1
第一个,第二个和最后一个结果为真。是不是x中的所有数字都大于2呢?
    * / x > 2
0  
不是,因为我们看到x>2结果为1 1 0 1。任一个0(假)都使得乘法(1*1*0*1)结果不可能
为1。
 
x中有多少元素大于2?我们将x>2中的1加起来:
   + / x > 2
3
 
x中有多少数字呢?我们可以将x=x中的1加起来:
   x
5 4 1 9
   
   x = x
1 1 1 1
   
   +/ x = x
4
其实不需这么麻烦,有一个内建函数 #(总数)可以给出一个列表的长度:
   # x
4 
 

1.9 并排显示

当我们输入J表达式时,表达式和结果在屏幕上挨着向下显示。我再展示一下之前的那几行:
   x
5 4 1 9
   x = x
1 1 1 1
   +/ x = x
4
   # x
4
在本书中,我有时会并排显示一些行而不是竖着向下显示,比如:
xx = x+/ x = x# x
5 4 1 91 1 1 144
这表示:在处理过程中,你输入x看到响应5 4 1 9.你输入x = x看到1 1 1 1,等等。并排
显示不是J系统的一项特性,只是本书中使用的图表。它们在第一行显示表达式,在第二行
显示相应的响应。

当你输入一个赋值式(x =:something)时,J系统并不显示值。然而,赋值式也是一个表
达式,也有值。有时能够看到赋值式的值是很便利的,因此我会在并排显示中列出赋值式
的值。例如:
x =: 1 + 2 3 4x = x+/ x = x# x
3 4 51 1 133


现在回到内置函数。假设我们有一个列表,我们可以通过说例如“yes, yes, no, yes,
no”从列表中轮流选取元素。选取序列为1 1 0 1 0。这样的0/1列表也被称为位串(或者
位表,位向量)。有一个函数,二元 #,接受一个位串(选取序列)作为它的左参数去
选取右参数中相应的元素。
y =: 6 7 8 9 101 1 0 1 0 # y
6 7 8 9 106 7 9
我们可以从y中选取满足某种条件的元素,例如:那些大于7的项:
yy > 7(y > 7) # y
6 7 8 9 100 0 1 1 18 9 10

1.10 注释

在一行J代码中,符号NB.(大写的N和B,以及点号)引入注释,注释直到行尾,不会
被求值。例如:
   NB.   this is a whole line of annotation
   
   6 + 6   NB. ought to produce 12
12 
 

1.11 内置函数的命名规则

每个内置的J函数都有一个非正式名字和一个正式名字。例如:正式名为 + 的函数有个非正式名“加法”。而且,函数还有一元模式和二元模式,因此正式名为 - 的函数有两个非正式名“求负”和“减法”。

非正式名实际上是函数的简短描述,通常一个词。J系统并不认识非正式名,也就是说,J中的表达式一定使用函数的正式名。在本书中,非正式名将被引号引起来,比如:“减法”。

基本上所有的内置函数都有一个字符或两个字符的正式名。例如 * 和 *: 函数。第二个字符往往是 :(冒号)或者 .(点号)。

两字符的名字往往意味着和单字符的基础函数有些关系。比如“平方”(*:)和“倍数”(*)。

因此内置的J函数往往意味着一组函数(6个相关函数)。有一元和二元模式,每个模式又分基础函数,冒号函数和点号函数。这里展示一下 > 函数组。

二元 > 表示“大于”,我们已经见过。

一元 > 后面再说。

一元 >. 将它的参数向上取整。注意是向上取整,不是取最接近的整数。因此非正式名为“上限”
   >. _1.7 1 1.7
_1 1 2
 
二元 >. 选择两个参数中较大的值
   3 >. 1 3 5 
3 3 5
我们通过在数据项之间插入“较大”函数(用 /)来找最大值。例如:1 6 5中的最大值可
以用(>. / 1 6 5)找到。下面的几行将解释结果为何是6,其中注释解释了每行的结果为什
么相同:
   >. / 1 6 5
6
   1 >. 6 >. 5      NB. by the meaning of /
6
   1 >. (6 >. 5)    NB. by rightmost-first rule
6
   1 >. (6)         NB. by the meaning of >.
6
   1 >. 6           NB. by the meaning of ()
6
   6                NB. by the meaning of >.
6 

一元 >: 非正式名为“递增”。它对其参数进行加一操作:
   >: _2 3 5 6.3
_1 4 6 7.3
 
二元 >: 表示“大于等于”
   3 >: 1 3 5 
1 1 0
 
第一章到此结束。   
    


没有评论:

发表评论