4 脚本(Scripts)和显性函数
这是对<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代码,这些代码完成某个计算,而且可以重复使用。本章的主题是:脚本、脚本定义的函数,以及脚本文件。
4.1 文本
先看一下对变量txt的赋值:txt =: 0 : 0 What is called a "script" is a sequence of lines of J. )
上式中的 0 : 0 表示“如下”,也就是说,0 : 0 是一个动词,它把接下来后面的所有行
作为自己的参数(直到遇到一个以单独")"开头的行为止),同时也作为自己的结果。
txt的值就是接下来那两行,这两行整体作为一个字符串。该字符串包含换行符(LF),
这使得字符串分为几行显示。txt有确定的长度,秩为1,也就是说,txt是一个列表,
包含两个换行符。
txt
What is called a "script" is
a sequence of lines of J.
$ txt | # $ txt | +/ txt = LF |
55 | 1 | 2 |
我们说txt是一个“文本”变量,就是—— 一个有着零个或多个换行符的字符串。
4.2 过程脚本
我们来看一个按步骤执行的计算过程。作为一个简单的示例,华氏摄氏温度转换可以用两步计算完成。假设T表示华氏温度:T =: 212第一步是将T减去32。得到结果t,
t =: T - 32第二步将t乘以5%9得到摄氏温度。
t * 5 % 9 100
假如我们要以不同的T值进行上述计算,我们可以将上述两行过程记录成脚本,脚本就可以重复使用。脚本由那几行J代码组成,并存储在文本变量中:
script =: 0 : 0 t =: T - 32 t * 5 % 9 )
这样的脚本可以通过内置的J动词—— 0 !: 111 ——来执行,我们称该动词为do,
do =: 0 !: 111
do script
然后,我们会在屏幕上看到脚本中的那些行:
t =: T - 32
t * 5 % 9
100
我们可以以不同的T值来再次运行脚本:
T =: 32
do script
t =: T - 32
t * 5 % 9
0
4.3 显式定义函数
可以通过脚本定义函数。这里有一个例子,华氏摄氏温度转换:
Celsius =: 3 : 0 t =: y - 32 t * 5 % 9 )
Celsius 32 212 | 1 + Celsius 32 212 |
0 100 | 1 101 |
接下来让我详细解释上述定义。
4.3.1 头部
函数以表达式 3 : 0 开头,表达式的含义为:“一个动词,定义如下”。(对照一下,0 : 0 表示“一个字符串,定义如下”。)3 : 0 中的冒号是一个连词,它的左参数(3)表示“动词”,右参数(0)表示“下面的行”。
更多的细节请参考第12章。一个以这种方法定义的函数被称为“显式定义”,或者“显式”。
4.3.2 含义
表达式 (Celsius 32 212) 将动词Celsius应用到参数32 212上,计算过程可以通过下面的模型描述:
y =: 32 212 t =: y - 32 t * 5 % 9 0 100
如上所示,除了第一行,计算按照脚本的定义进行。
4.3.3 参数变量
参数(32 212)通过变量y提供给脚本。一元函数中“参数变量”命名为y。(二元函数中,下面我们将看到,左参数命名为x,右参数命名为y)
4.3.4 局部变量
再看一下Celsius的定义:Celsius =: 3 : 0 t =: y - 32 t * 5 % 9 )可以看到它包含了对变量t的赋值。该变量只在Celsius执行过程中使用。不幸的是对t的赋值干扰了Celsius外部其他任何也叫做t的变量的值。例如:
t =: 'hello' Celsius 212 100 t 180
可以看到变量t的原始值('hello')在Celsius执行的过程中被改变了。为了避免这种事情,我们可以把Celsius内部的t声明为严格的私有变量,和其他也叫做t的变量区分开来。
为了达到这种效果,可以使用特殊形式的赋值—— =.(等号 点)。新版本的定义变为:
Celsius =: 3 : 0
t =. y - 32
t * 5 % 9
)
我们说Celsius中的t是局部变量,或者说t局部化于Celsius。作为对比,定义于函数外部的变量叫做全局变量。现在可以看到在Celsius内对t的赋值不会影响任何全局变量t:t =: 'hello' Celsius 212 100 t hello
参数变量y也是局部变量。因此(Celsius 32 212)的执行过程可以用下述模型更准确的描述:
y =. 32 212 t =. y - 32 t * 5 % 9 0 100
4.3.5 二元动词
Celsius是一个一元动词,以 3 : 0 开头,定义使用单个参数y。相应的,二元动词以 4 : 0 开头。左右参数命名为x和y。例如:两个数字的“正差”是大的数字减去小的数字:posdiff =: 4 : 0 larger =. x >. y smaller =. x <. y larger - smaller )
3 posdiff 4 | 4 posdiff 3 |
1 | 1 |
4.3.6 单行脚本
一个单行脚本可以写成一个字符串,将该字符串作为冒号连词的右参数。PosDiff =: 4 : '(x >. y) - (x <. y)' 4 PosDiff 3 1
4.3.7 控制结构
在目前我们看到的以脚本方式定义的函数中,程序从第一行的表达式开始执行,然后接着下一行,再下一行,直到最后。
这种笔直的执行路径不是唯一的可能路径。下一步要执行哪个表达式是可以选择的。
例如:这里有一个函数用来计算给定长宽高的长方体的体积。假如该函数先测试它的参数是不是一个长度为3的列表(长、宽和高)。如果是,那么计算体积,否则返回字符串'ERROR'。
volume =: 3 : 0 if. 3 = # y do. * / y else. 'ERROR' end. )
测试一下:
volume 2 3 4 | volume 2 3 |
24 | ERROR |
从 if. 到 end. 的所有东西构成一个“控制结构”。其中if. do. else. 和 end. 叫做
“控制词”。第12章可以看到更多的控制结构。
4.4 隐式函数与显式函数比较
我们已经看到两种不同的定义函数方式。显式定义,本章介绍的,之所以称为显式是因为它显式地涉及参数变量。如上面的volume函数,变量y就是显式参数。相应地,我们在前面章节看到的定义函数方式称为“隐式”,因为不涉及参数变量。例如:对比一下“正差”函数的显式和隐式定义:
epd =: 4 : '(x >. y) - (x <. y)' tpd =: >. - <.
很多隐式定义的函数可以显式定义,反之亦然。哪种方式更好取决于我们定义函数时觉得哪种最自然。我们要选择是将问题分解为作为脚本的步骤序列呢?还是一些小函数的组合?
隐式定义比较紧凑。因此隐式函数利于系统分析和转换。事实上,J系统能够为很大范围内的隐式函数自动生成类似“inverses”和“derivatives”这样的转换。
4.5 函数作为值
函数也是值,一个值可以通过键入表达式显示出来。这里的表达式可以是一个简单的名字。下面是一些隐式和显式函数的值:- & 32 +-+-+--+ |-|&|32| +-+-+--+ epd +-+-+-------------------+ |4|:|(x >. y) - (x <. y)| +-+-+-------------------+ Celsius +-+-+-----------+ |3|:|t =. y - 32| | | |t * 5 % 9 | +-+-+-----------+
每个函数的值显示为盒子结构。这是默认显示方式,我们也可以选择其他的显式方式:参考第27章。现在我只讨论“线性显示”,该显示方式将函数显示为一个字符序列,该序列可以用来再次输入产生函数。我们可以让系统以“线性显示”的方式显示函数,通过输入:
(9!:3) 5
然后我们再看函数的显示:
epd 4 : '(x >. y) - (x <. y)' 在后面的章节中,将经常以“线性显示”来展示函数的值。
4.6 脚本文件
我们已经看到了脚本(J代码行)被用来定义单个变量:文本变量或者函数。然而,一个J语言文件可以保存很多定义。 这样的文件被称为脚本文件,它的用途在于通过读取该文件可以执行它里面的所有定义。下面是一个例子。使用你的文本编辑器,创建一个文件,包含下面两行代码:
squareroot =: %:
z =: 1 , (2+2) , (4+5)J脚本文件通常以 .ijs 为后缀,假设创建的文件(Windows系统)的路径名为:c:\temp\myscript.ijs。
然后在J会话中,为了方便,我们定义一个变量F来保存文件名:
F =: 'c:\temp\myscript.ijs'
接下来就可以通过下面的命令来执行这个脚本:
0!:1 < F
然后我们将在屏幕上看到脚本文件中的行:
squareroot =: %:
z =: 1 ,(2+2), (4+5)
现在我们可以使用脚本文件中的定义了:
z
1 4 9
squareroot z
1 2 3
J会话中常见的活动基本上是:编辑脚本文件,载入(重载入)脚本文件中的定义,在键盘上发起计算。能从一个J会话传递到另一个J会话的只有脚本文件。J系统的状态,或者内存,在会话结束时统统消失;所有会话中输入的定义也随之消失。因此在J会话结束前,要确保脚本文件已经更新,也就是确保脚本文件包含了所有你想要保存的定义。
在会话开始时,J系统会自动载入一个特定的脚本文件,叫做“profile”。(更多细节参考第26章)。该配置文件可以编辑,因此是一个记录你自己常用定义的好地方。现在到了第4章的结尾,同时也是第一部分的结尾。下来的章节将更深入和细致地讨论我们在第一部分遇到的这些主题。
在会话开始时,J系统会自动载入一个特定的脚本文件,叫做“profile”。(更多细节参考第26章)。该配置文件可以编辑,因此是一个记录你自己常用定义的好地方。现在到了第4章的结尾,同时也是第一部分的结尾。下来的章节将更深入和细致地讨论我们在第一部分遇到的这些主题。
没有评论:
发表评论