简介
J语言更像是一个大型符号系统,用以解决复杂的数学运算。所以在下载J语言之后,进入jconsole
,就可以凭借感觉进行数学运算了。
其中加减乘除分别是+, -, *, %
。但是玩过这么多编程语言,对这些常规的运算其实已经有些倦怠了,所以直接来个鬼酷一点的,事先说明,J
语言中NB.
后为注释。
1 2 3 4 5 * 2 NB. 数乘列表
2 4 6 8 10
8 <: 7 8 9 NB. 数值和列表比较
0 1 1
7 8 9 < 9 8 7 NB. 列表和列表比较
1 0 0
(+/ % #) 2.5 3.1 11 2 NB. 求均值
4.65
fact=: 1: ` (* $:@<:) @. * NB. 此为阶乘
fact 5 NB. 计算5的阶乘
120
前三条很容易理解,就不解释了;第五条稍微有点难,也先不解释。这里先看看求均值的代码(+/ % #)
,其中%
是个二元运算符——除法,这个除法作用在两个符号之间,其中+/
用于数列求和,#
则返回数列长度,二者相除就得到均值。
这个就是J语言符号运算的魅力,而且J语言提供了Android等版本,可以在手机上使用,下载地址:Android版本;IOS版本,在手机上的效果为
接下来,为了理解阶乘代码,先做一点最基础的J语言符号训练,主要包括三个内容,一是基础的运算符号;二是与if相关的语句;三是复合语句的概念。
算术符号和优先级
初学J语言,可以把运算符分为两类,即一元运算符和二元运算符,有的时候,同一个符号在进行一元或者二元运算的时候,会有不同的含义,例如%
作为二元运算,表示除法,作为一元运算,表示求倒数
% 5
0.2
3 % 5
0.6
下表中列出了常见的算术符号
+ | - | * | % | ^ | %: | |
---|---|---|---|---|---|---|
一元 | 负号 | 正号 | 取符号 | 倒数 | e指数 | 根号 |
二元 | 加 | 减 | 乘 | 除 | 乘方 | 猜猜是什么 |
其中-
作为一元运算表示取负,而J语言中的复数用下划线表示,即_1
表示
−
1
-1
−1。
和其他语言不同,J语言中乘法并不比加法优先,如果想更改二元运算的顺序,可以使用括号。而一般来说,一元运算比二元运算有着更高的优先级。
2 * 3 + 4 NB. J语言中乘法并不比加法更优先
14
(2 * 3) + 4 NB. 但括号还是优先的
10
2 * * -3 NB. * -3取-3的符号,为-1
_2 NB. 然后2乘以负一得到-2
赋值和比较
在J
语言中,通过=:
进行变量赋值,而且作为函数式语言,=:
不仅可以赋值变量,也可以赋值函数
a =: 5 NB. 把5赋值给a
a
5
b =: - NB. 把运算-赋值给b
b a NB. 相当于 - a
_5
a b a NB. 相当于 a - a
0
type 'b'
┌────┐
│verb│
└────┘
type 'a'
┌────┐
│noun│
└────┘
type
可以返回符号的类别,在其他语言中,我们一般把-
称作运算符,但在J
语言中,则称作动词;而变量则为名词。在J语言中,还有副词、连词等。
=:
用于赋值,而其他语言中常用的=
在J语言中则表示相等
符号 | 含义 | 符号 | 含义 |
---|---|---|---|
= | 判断相等 | =: | 赋值 |
< | 小于 | <: | 小于等于 |
> | 大于 | >: | 大于等于 |
复合动词
通过=:
可以进行赋值,而且可以赋值为名词,还可以赋值为动词,例如<:
和>:
分别表示减1和加1,但容易记混,所以下面新建两个函数表示
addOne =: >:
minusOne =: <:
addOne 10
11
minusOne 10
9
这个本来没什么,不过在多个动词连接时却出现了问题,比如想实现类似
1
x
+
1
\frac{1}{x}+1
x1+1的功能,如果x=5
,那么就是下面这种
>: % 5
1.2
那么如果想做一个复合函数,也理应是test =: >: %
,但下面显然是错的
test =: >: %
test 5
1
错误的原因也很离谱,test 5
实际上执行的是5 >: % 5
,其中%
被翻译为单目运算符取倒数;>:
被翻译为双目运算符,用于比较,最后自然
5
>
1
5
5>\frac 1 5
5>51为真,即返回1。
换言之,:> %
在未指定输入的情况下,是有歧义的。
为了避免这个尴尬的问题,通过@
对动词进行复合是个好办法
test =: >: @ %
test 2
1.5
其中,@
就起到了函数复合的作用,相当于把函数
f
f
f和
g
g
g,变为
f
(
g
(
x
)
)
f(g(x))
f(g(x))。
动词列表
如果想用同一个动词,处理多个不同的值,那么可以先用不同的值做一个列表。
% 2 3 4
0.5 0.333333 0.25
但是,若想用不同的动词,处理同一个数值,那可能就不能这么直接列出来了,因为按照J的脾气,肯定先复合了再说,想做一个动词列表,那么需要用括号,然后中间用,
或者`分隔,类似下面这种
(%, ^) 5
0.2 148.413
需要注意,中间的逗号如果没有的话,那么最终的结果还是复合,只不过换了个复合的方法
% ^ 5
0.00673795
(% ^) 5
0.0336897
上面执行的计算是
1
e
5
\frac{1}{e^5}
e51,下面执行的运算是
5
/
e
5
5/e^5
5/e5,即()
把其左侧的运算符理解为了双目计算,并将输入的5
分成两份分别送到了表达式两端。
(,)
虽然能实现列表,但是这个列表也有一个问题,即无论何时都会一起使用。正常列表,比如[1,2,3]
,那讲道理我把其中的2提取出来专门处理肯定也是没问题的,但函数上面的函数列表好像不太行。
为了解决这个问题,J语言提供了`…@.语句。
由于`在Markdown中是负责高亮的,所以`自己就没法高亮了……具体使用方法,直接看下面的代码
test =: % ` ^
(test@.0) 3
0.333333
(test@.1) 3
20.0855
理解阶乘!!
再有了上面这些代码的积淀之后,只需在知道两件事就可以看懂阶乘的代码了,这两件事分别是J语言中的两类特殊函数。
一类是1:
函数,表示常量函数,1:
的返回值永远是1;相应地_3:
返回值永远是-3,这样的函数共有19个,值从-9
到9
。
另一类则是创建递归函数必不可少的$:
,表示调用自身。
接下来就回顾一下阶乘代码
fact=: 1: ` (* $:@<:) @. *
其中*
用于返回符号,当输入值x
大于1时,* x=1
,当输入值x=0
时,* x =0
。
如果*x=0
,那么fact就选择左边的1:
函数,此函数无论输入什么,返回值都是1。
如果*x=1
,那么就选择右侧的(* $:@<:)
,此函数最外层是(* )
,将输入的值
x
x
x变为x * ($:@<:)
;$:
表示调用自身,:<
减一,二者结合就成了f(x-1)
。
至此,也就明白了阶乘是怎么来的,同时也基本上算是上了J语言的船。