回上级页面

新蜗牛 · 指针

2023 年 05 月 11 日


上一篇:新蜗牛 · 秘密

下一篇:新蜗牛 · 其他

基于指针(或引用)可以间接访问某个变量。例如,在 C 语言中,

int a = 3;
int *p = &a;
*p = 4;
printf("%d\n", a);  /* 结果为 4 */

MetaPost 这样的宏语言,该如何像 C 语言这样间接访问变量呢?

scantokens

scantokens 是 MetaPost 原语,可将字符串转化为 Token(记号)。由于 MetaPost 的原语、宏以及变量等对象的名字皆为 Token,故而其字符串变量便可以作为指针使用。例如,以下代码

numeric a; a := 3;
string p; p := "a";
scantokens(p) := 4;
draw textext(decimal a);

与上文的 C 代码等效。

字符串表

有了指针,便可以构造表。有了表,便可批量构造或访问一组变量。例如

string a[];  a[0] := "空"; a[1] := "甲"; a[2] := "乙"; a[3] := "丙"; 
宫(scantokens(a[0]), a[0]);
for i = 1 upto 3: 
  宫(scantokens(a[i]), a[i]) 位于 scantokens(a[i - 1]) 偏 (东 1.5cm);
endfor
呜呼 甲, 乙, 丙;
指针-1

上述代码看上去有些繁琐。宏最擅长化解繁琐:

def 表 (suffix a) (text b) =
  string a[];
  begingroup
    save i; numeric i; i := 0;
    for j = b:
      a[i] := j;
      i := i + 1;
    endfor
  endgroup
enddef;

基于 ,上述构造表的代码

string a[];  a[0] := "空"; a[1] := "甲"; a[2] := "乙"; a[3] := ""; 

可简化为

表(a, "空", "甲", "乙", "");

在遍历一个表时,为了避免使用幻数,需要定义一个宏,用于查询表中元素个数:

vardef 表长 suffix a =
  save i; numeric i; i := 0;
  forever:
    exitif unknown a[i + 1];
    i := i + 1;
  endfor
  i
enddef;

基于 表长,对表 a 的遍历代码可写为

for i = 1 upto 表长 a:
  ... ... ...
endfor

在代码中频繁使用 scantokens,也颇为繁琐,以 代之:

def 名 expr a = scantokens(a) enddef;

于是

宫(scantokens(a[i]), a[i]) 位于 scantokens(a[i - 1]) 偏 (东 1.5cm);

可简写为

宫(名 a[i], a[i]) 位于 (名 a[i - 1]) 偏 (东 1.5cm);

合并

若为字符串表定义宏 ,将表中除第一个字符串之外的其他字符串依序合并为一个字符串,例如将

表(a, "空", "甲", "乙", "", "", "");

所构造的表 a 合并为 "甲, 乙, 丙, 丁, 戊",则

呜呼 甲, 乙, 丙, 丁, 戊;

便可简写为

呜呼 名(聚 a);

可定义为

vardef 聚 suffix a =
  save s, n; string s; numeric n;
  s := "";
  n := 表长 a;
  for i = 1 upto n:
    s := (tostring s) & a[i] & ",";
  endfor;
  s := s & a[n];
  s
enddef;

& 可将两个字符串合并为一个。

横陈纵列

基于上述宏,不难定义以下三个宏,用于构造一组对象并横向或纵向放置它们:

def 之乎者也 suffix a =
  for i = 0 upto 表长 a: 宫(名 a[i], a[i]); endfor
enddef;

def 横陈 (suffix a) (expr 间距) =
  for i = 1 upto 表长 a:
    定位(名 a[i], 名(a[i]).酉门 位于 名(a[i - 1]).卯门 偏 (东 间距));
  endfor
enddef;

def 纵列 (suffix a) (expr 间距) =
  for i = 1 upto 表长 a:
    定位(名 a[i], 名(a[i]).子门 位于 名(a[i - 1]).午门 偏 (南 间距));
  endfor
enddef;
指针-2