跳到主要内容
Varlig 指南
Varlig 首页

列表与统计

列表把多个值集中放在一个名称下,比如一周的步数、一组考试分数或一个月的收据。把值放进列表后,你就可以求和、求平均值、衡量它们的离散程度,并挑出重要的值。

本页介绍如何书写列表和按索引取值、汇总统计、排序与切片、用 mapfilter 变换列表,以及对整个列表进行运算。每当你有一列数字需要汇总时,都可以参考本页。由行组成的列表还可以当作矩阵使用,矩阵有专门的页面介绍。

书写列表

把值放在方括号里,用逗号分隔:

calc
成绩 = [72, 85, 91, 64, 88] 结果为 [72, 85, 91, 64, 88]
成绩[0] 结果为 72
成绩[4] 结果为 88
length(成绩) 结果为 5

值在列表中的位置称为索引,写在列表后面的方括号里。索引从零开始,所以 成绩[0] 是第一个分数,成绩[4] 是第五个。最后一个元素总是位于 length(成绩) - 1。负数、小数或超出范围的索引都会报错;索引不会从末尾倒着绕回来。

元素可以是计算式、带单位的数量,也可以是其他列表:

calc
[2 * 45, 60 + 15] 结果为 [90, 75]
[1.5 kg, 750 g] 结果为 [1.5 kg, 750 g]
[[1, 2], [3, 4]][1][0] 结果为 3

对于嵌套列表,第一个索引选出内层列表,第二个索引再从中选出一个值:[1] 选中 [3, 4],然后 [0] 选中 3

千位分隔符在列表中同样有效,所以你可以按数字原本的写法直接粘贴。在值与值之间的每个逗号后面加一个空格;逗号后面紧跟三位数字时,仍然算作同一个数字的一部分:

calc
工资 = [£32,000, £41,500, £38,250] 结果为 [£32,000.00, £41,500.00, £38,250.00]
average(工资) 结果为 £37,250.00

有几点需要注意:

  • 不加空格时,每个逗号都会分隔值,所以 [8200,10400] 包含两个值。像 [100,200,300] 这样的列表既可能是一个数字,也可能是三个数字,因此 Varlig 会显示错误,并写明两种可能的含义。
  • 结果会显示千位分隔符,并在每个逗号后面加一个空格,所以 [8,200, 10,400] 是包含两个值的列表。
  • 一个列表最多包含 1,024 个值。

汇总统计

calc
成绩 = [72, 85, 91, 64, 88]
sum(成绩) 结果为 400
average(成绩) 结果为 80
median(成绩) 结果为 85
min(成绩) 结果为 64
max(成绩) 结果为 91
count(成绩) 结果为 5

meanavgaverage 的别名,totalsum 的别名,stdevstddev 的别名,countlength 的别名,所以电子表格里的习惯写法可以照搬过来。product 把所有值相乘。没有百分位数函数;median 给出的是中间值。

对于命名的列表,你也可以用文字写出统计量:

calc
成绩 = [72, 85, 91, 64, 88]
average of 成绩 结果为 80
total of 成绩 结果为 400

sum ofmean ofmedian ofmin ofmax ofcount of 的用法相同。在 of 后面写一个名称,或者直接写出用逗号分隔的值,加不加圆括号都可以。如果值写在方括号里,请使用函数形式,如 average([72, 85, 91])

大多数统计函数,包括 sumproductaveragemedianminmaxcountstddev,也接受分开书写的值,而不一定是列表:

calc
max(72, 85, 91) 结果为 91
average of 70, 85, 91 结果为 82
mean of (1,2,3) 结果为 2
average(1,200, 1,800) 结果为 1,500

合计上方的行

只写了 sumtotal 的一行,会把它上方的答案加起来,一直加到上一个 sumtotal 行或者 --- 分隔线为止。它的作用与某一行快捷菜单中的 Subtotal(小计)选项相同,所以笔记中的每个部分都能在你输入时得到各自的合计:

calc
# 食品杂货
面包 = £1.20
牛奶 = £1.45
鸡蛋 = £2.30
total 结果为 £4.95
# 日用品
垃圾袋 = £3.50
洗洁精 = £1.80
total 结果为 £5.30

如果你给某个值起名为 sumtotal,这一行会显示你的值。

离散程度:方差与标准差

calc
成绩 = [72, 85, 91, 64, 88]
variance(成绩) 结果为 132.5
stddev(成绩) to 1 dp 结果为 11.5

两者都是样本统计量:它们除以值的个数减一(n - 1),当你的数据是从更大整体中抽取的样本时,通常这样计算。它们至少需要两个值。在任何统计量后面加上 to 1 dp 即可舍入。

如果你的列表就是整个总体,则应改为除以 n。可以用下文介绍的 map 来构建:

calc
# 列车延误分钟数
延误 = [2, 4, 4, 4, 5, 5, 7, 9]
stddev(延误) 结果为 2.1380899353
中心 = average(延误) 结果为 5
sqrt(sum(map((x - 中心)^2, x, 延误)) / count(延误)) 结果为 2

带单位的列表

sumaveragemedianminmaxstddev 会保留单位,并在兼容的单位之间换算:

calc
通勤 = [24 min, 31 min, 27 min, 22 min]
sum(通勤) 结果为 104 min
average(通勤) 结果为 26 min
median(通勤) 结果为 25.5 min
sum([1 kg, 500 g]) 结果为 1.5 kg

variance 只接受普通数字,因为它的结果会是单位的平方。sort 同样只适用于普通数字。对单位不兼容的值(如 sum([1 kg, 3 m]))进行统计会报错,而不会悄悄跳过某些值。

空列表

对空列表调用 结果
sum([]) 0
product([]) 1
count([]), length([]) 0
average([]), median([]), min([]), max([]) 错误
variance([]), stddev([]) 错误

空列表最常见于没有匹配到任何值的 filter 的结果,所以即使原始列表有值,对筛选后的列表求 average 也可能失败。

列表函数参考

调用 作用
range(start,end) startend 的整数,包含两端
range(start,end,step) step 为步长计数,步长可以是负数或小数,但不能为零
length(list), count(list) 值的个数
sort(list) 从小到大排序;仅限普通数字
reverse(list) 把相同的值按相反顺序排列;适用于任何值
unique(list) 每个值只保留第一次出现的那个
slice(list,start,end) 从索引 start 开始、到索引 end 之前(不含)的值
map(expression,variable,list) 对每个值计算一次表达式
filter(condition,variable,list) 保留使条件为 true 的值
calc
range(1, 5) 结果为 [1, 2, 3, 4, 5]
range(0, 100, 25) 结果为 [0, 25, 50, 75, 100]
range(10, 0, -2) 结果为 [10, 8, 6, 4, 2, 0]
成绩 = [72, 85, 91, 64, 88]
sort(成绩) 结果为 [64, 72, 85, 88, 91]
reverse(sort(成绩)) 结果为 [91, 88, 85, 72, 64]
slice(sort(成绩), 0, 3) 结果为 [64, 72, 85]
unique([4, 2, 4, 6, 2]) 结果为 [4, 2, 6]

reverse(sort(…)) 让最大的值排在最前面,slice(sort(…), 0, 3) 则挑出最低的三个。

切片边界必须满足 0 <= start <= end <= lengthstartend 相等时得到空列表。没有 成绩[0:2] 这种简写;请写 slice(成绩, 0, 2)

unique 按值的存储形式精确比较,所以 1 kg1000 g 算作两个不同的值,在小数点后第十位不同的两个结果也不会合并。

map 与 filter

map 对列表中的每个值计算一个表达式。filter 保留通过测试的值。两者都是先写表达式,再写代表每个值的名称,最后写列表:

calc
成绩 = [72, 85, 91, 64, 88]
map(s + 5, s, 成绩) 结果为 [77, 90, 96, 69, 93]
filter(s >= 80, s, 成绩) 结果为 [85, 91, 88]
count(filter(s >= 80, s, 成绩)) 结果为 3
average(filter(s >= 80, s, 成绩)) 结果为 88

filter(s >= 80, s, 成绩) 可以读作“成绩 中满足 s >= 80 的值 s”。两者都适用于单位和金额:

calc
map(p * 1.2, p, [£10, £25, £40]) 结果为 [£12.00, £30.00, £48.00]
支出 = [£42.50, £18.20, £65.00, £23.30]
filter(x > £30, x, 支出) 结果为 [£42.50, £65.00]
sum(filter(x > £30, x, 支出)) 结果为 £107.50

你选的名称只在这次调用内部有效,不会改变笔记中其他地方同名的值:

calc
x = 10
map(x * 2, x, [1, 2, 3]) 结果为 [2, 4, 6]
x 结果为 10

filter 中的条件必须是比较式或其他真/假值。普通数字不会被当作真,所以 filter(x, x, [0, 1, 2]) 会报错。对空列表执行 map 或 filter,得到的仍是空列表。

列表运算

你可以把每个值都乘以或除以同一个数,也可以把两个长度相同的列表逐值相加或相减:

calc
[30, 36, 22] / 40 * 100 结果为 [75, 90, 55]
2 * [3, 1, 4] 结果为 [6, 2, 8]
-[5, -3] 结果为 [-5, 3]
模考 = [62, 70, 60]
期末 = [72, 78, 69]
期末 - 模考 结果为 [10, 8, 9]
average(期末 - 模考) 结果为 9
[1 kg, 2 kg] + [500 g, 500 g] 结果为 [1.5 kg, 2.5 kg]
[72, 85] == [72, 85] 结果为 true

第一行把满分 40 的分数换算成百分比。期末 - 模考 这一行把每位学生的模考分数与期末分数一一对应。

有些运算不支持列表:

  • 给列表加上单个数字,如 成绩 + 5。请改用 map(s + 5, s, 成绩)
  • 把长度不同的列表相加。值永远不会为了凑齐长度而重复。
  • * 把两个平面列表相乘。要计算逐对乘积之和,请使用 dotdot([2, 3, 1], [4, 5, 10])33,比如两杯单价 4 的咖啡、三杯单价 5 的茶和一块单价 10 的蛋糕。对于由行组成的列表,* 表示矩阵乘法,请参阅向量与矩阵

由行组成的列表

由行组成的列表就像一张小表格。averagesum 按列计算,map 则可以逐行计算:

calc
# 每行是一名学生:[模考, 期末]
成绩单 = [[62, 72], [70, 78], [60, 69]]
average(成绩单) 结果为 [64, 73]
sum(成绩单) 结果为 [192, 219]
map(average(r), r, 成绩单) 结果为 [67, 74, 64.5]

medianminmaxstddev 不接受由行组成的列表。请像最后一行那样用 map 逐行处理,或者把某一列单独提取成一个列表。

所有这些运算的确切规则,请参阅高等数学:功能与限制参考页。

综合运用

下面是手表记录的一周步数,以及每日目标:

calc
# 步数,周一至周日
步数 = [8200, 10400, 6300, 12000, 9100, 7400, 9600]
目标 = 9000
sum(步数) 结果为 63,000
average(步数) 结果为 9,000
median(步数) 结果为 9,100
达标天数: count(filter(s >= 目标, s, 步数)) 结果为 4
极差: max(步数) - min(步数) 结果为 5,700
# 每步约 0.75 m
步行距离 = sum(步数) * 0.75 m
步行距离 in km 结果为 47.25 km

平均值恰好等于目标,但只有四天达标,而且最好和最差的两天相差 5,700 步。修改 目标,或者粘贴下一周的步数,每一行都会随之更新。