无点程序组合

point free procedure composition

假设我在堆栈上有两个过程 p1p2,我想使用它们并在堆栈上留下一个结合了它们的效果的新过程。我想要一个程序来做到这一点。如果我总是愿意做一点字典簿记,这就很容易了。但是我可以不介绍任何名字吗? (请注意,我想要结果过程,而不仅仅是对当前堆栈执行组合效果。)

举个例子

/compose {<< /f1 4 2 roll /f2 exch >>begin {f1 f2} end}bind def

这当然行不通,因为 end 之后 f1 和 f2 将是未知的。但是这个损坏的代码应该可以说明我所追求的。

这是完全可能的,而且不是特别困难。您使用每个过程对象创建一个新数组,后跟可执行文件名称 exec。然后使该数组可执行。

/combine { % {a} {b}  .  {{a} exec {b} exec}
    /exec cvx exch     % {a} exec {b}
    /exec cvx          % {a} exec {b} exec
    4 array astore     % [{a} exec {b} exec]
    cvx                % {{a} exec {b} exec}
} def

为了更接近你原来的风格,带有命名参数,我会这样写:

% fun1 fun2  compose  proc
/compose { 2 dict begin       % f1 f2
    {f2 f1}{exch def} forall  %
    ({ //f1 exec //f2 exec }) % ({ //f1 exec //f2 exec })
    cvx exec                  % { <f1> exec <f2> exec }
end } def

//immediate-name 语法非常强大。这里我使用了一个字符串中的代码模板。当执行字符串 cvx exec 时,它会根据内容调用扫描器,然后它会自动 load 扫描所有以双斜线 // 为前缀的标记。注释 <f1> 表示命名变量的内容。就像程序流中的 {executable array} 不执行但在堆栈上产生过程一样,execing 包含 1 的字符串也会在堆栈上产生过程。

对于命名参数样式,我利用了后记的一些特殊规则:不执行可执行数组,因此变量名数组可以写成可执行数组,然后作为数据使用,没有任何额外的麻烦.但是通过使用可执行语法,可以在不使用 / 的情况下编写内容(名称)。所以,我们可以写更短的 { f2 f1 }.

而不是 [ /f2 /f1 ]

参数部分也可以分解为它自己的函数。

/argsbegin { % a r g s _ {n a m e s}  .  -
    dup length dict begin
    {exch def} forall  % currentdict:<</n _  /a s  /m g  /e r  /s a>>
} def

/compose { {f2 f1} argsbegin
    ({//f1 exec //f2 exec}) token pop exch pop %another way to invoke the scanner
end } def

或者,为了正确地表达论点,它可能像下面这样。用 for 循环模拟向后 forall 只是有点尴尬。

/argsbegin { % a r g s _ {n a m e s}  .  -
    dup length dup dict begin  % a r g s _ {} n
    1 sub -1 0 {  % a r g s _ {} i
        3 2 roll  % a r g s {} i _
        3 copy    % a r g s {} i _ {} i _
        pop       % a r g s {} i _ {} i
        get       % a r g s {} i _ /s
        exch def  % a r g s {} i
        pop       % a r g s {}
    } for         % {}
    pop
} def

/compose { {f1 f2} argsbegin
    ({//f1 exec //f2 exec}) cvx exec
end } def