无点程序组合
point free procedure composition
假设我在堆栈上有两个过程 p1
和 p2
,我想使用它们并在堆栈上留下一个结合了它们的效果的新过程。我想要一个程序来做到这一点。如果我总是愿意做一点字典簿记,这就很容易了。但是我可以不介绍任何名字吗? (请注意,我想要结果过程,而不仅仅是对当前堆栈执行组合效果。)
举个例子
/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}
不执行但在堆栈上产生过程一样,exec
ing 包含 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
假设我在堆栈上有两个过程 p1
和 p2
,我想使用它们并在堆栈上留下一个结合了它们的效果的新过程。我想要一个程序来做到这一点。如果我总是愿意做一点字典簿记,这就很容易了。但是我可以不介绍任何名字吗? (请注意,我想要结果过程,而不仅仅是对当前堆栈执行组合效果。)
举个例子
/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}
不执行但在堆栈上产生过程一样,exec
ing 包含 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