我正在努力研究如何舒适地检查Postscript中的字符串是否(123456)
仅包含数字。例如 0、1、2、3、4、5、6、7、8 或 9。
所以例如:
(1235) isnum
应该在堆栈上实现
(1234a) isnum
应该在堆栈上放置 false
(1.234) isnum
应该在堆栈上放置 false
我真的必须使用循环来检查每个字符是否在 (0-9) 内部,如下所示:
/integers (0123456789) def
% checks if a char is in (0123456789)
/isnum
{
integers exch search
{ pop pop pop true }
{ pop false }ifelse
}
def
/isnumber {
% [(number),(number)]
length 1 sub % [(number), length-1]
0 exch % [(number), 0, length-1]
1 exch % [(number), 0, 1, length-1]
% [(number), 0, 1, length-1]
dup /numlen exch 1 add def
/counter 0 def
% for(j=0; j<length-1; j++)
{
/j exch def
% [(number)]
dup j 1 getinterval % [(number), number[j]]
/char exch def % [(number)] && char == number[j]
char isnum {
/counter counter 1 add def
}if
}for
counter numlen eq
{
true
}
{
false
}ifelse
}def
(12345) (12345) 是编号 ==> true 在堆栈上
还是有更快的程序?
还有另一种编写这种代码的风格,它利用了postscript的关系运算符也适用于字符串的事实。
因此,如果您可以使用getinterval
逐个剥离字符以制作长度= 1字符串,则可以直接将它们与(0)
或(9)
进行比较。
/isdigit { dup (0) ge exch (9) le and } def
这使得我另一个答案中的"地图"步骤更加复杂。
/foreach { % array/string proc
1 dict begin { /proc /str }{ exch def }forall % define args
0 1 /str load length 1 sub % 3 numbers for `for` later
( { //str exch 1 getinterval
//proc exec } ) % template loop body
cvx exec % generate loop body from template
end for % discard dict then call `for`
} def
/isnumber { true exch { isdigit and } foreach } def
这就引出了另一个想法。如果您正在使用的字符串有合理的最大长度,则可以与零或九进行比较,即。 长度 = 4 的字符串(0000)
和(9999)
。
/zeros (000000000) def
/nines (999999999) def
/isnumber { dup zeros 0 2 index length getinterval ge exch
nines 0 2 index length getinterval le and } def
通过这种方式,ge
和le
隐式地对字符串内容执行循环以完成其工作。
这是一个准备自定义的程序片段。这将检查字符串中的每个字符是否为数字。字符串中的数字是 48 到 57 之间的整数:
(345fd2) { dup 48 ge exch 57 le and = } forall
这只是一个起点。可以根据需要累积和检查字符串中每个元素的结果。玩得愉快。
编辑:如果字符串未通过测试,这会在堆栈上留下 false,或者如果通过,则什么都没有:
/isnumb {{dup 48 ge exch 57 le and not{false exit}if} forall} def
编辑2:这符合您的要求。如果我知道,可能会有更简单的方法。
/isnumber? { { dup 48 ge exch 57 le and
{/flag true def}{/flag false def exit} ifelse
} forall
flag
} def
编辑3:感谢luser droog提供我学习如何消除标志的代码。任何空字符串都通过测试,所以要小心。仍然看着停止和停止。
/isnumber { true exch
{ dup 48 ge exch 57 le and % element is digit
not{pop false exit}if % element is not digit
} forall
} def
EDIT4:这会在零长度字符串的堆栈上留下 false:
/isnumeric { dup length 0 eq
{ pop false % false for zero length string
}{
true exch
{dup 48 ge exch 57 le and
not{pop false exit}if % early exit for non-digit
} forall % check each element of string
} ifelse
} def
编辑5:再次感谢luser droog提供的线索,让我真正思考。这仍然在零长度字符串的堆栈上保留 true:
/isnum {true exch {dup 48 ge exch 57 le and and}forall } def
编辑6:再次感谢有关停止和其余的信息。这是非常有帮助的。我需要更多的时间来学习和实验。
我添加了一个测试,以防运算符用于字符串以外的内容,因此对数组之类的内容返回 false 而不是抱怨:
/isnums {
{dup type /stringtype ne {stop} if
dup length 0 ne exch
{dup 48 ge exch 57 le and and}forall
}stopped {pop false}if
} def
编辑7:最后。这也行得通。
/isnumbr {
{dup type /stringtype ne {pop false stop} if
dup length 0 ne exch
{dup 48 ge exch 57 le and and}forall
}stopped not and
} def
这听起来像是地图缩减策略的工作。对于基本情况,检查一个字符:
/isdigit { dup 48 ge exch 57 le and } def % int . bool
然后,我们可以在字符串上使用此过程进行映射。
/map { [ 3 1 roll forall ] } def % string proc . array
这将生成一个布尔数组,字符串的每个字符一个布尔值。要将此数组折叠为单个布尔值,我们可以使用初始元素和二进制操作来减少数组。
/reduce { exch 3 1 roll forall } def % array initial proc . result
一起:
/isnumber { //isdigit map true {and} reduce } def % string . bool
(1234) isnumber
铌。此代码将报告空字符串的true
,因为它不包含任何非数字字符。 上面的//isdigit
也可以写{isdigit}
但使用立即加载的名称可以避免在此处创建新数组。这不能用and
来完成,因为它很可能是一个运算符,因此不是forall
的兼容类型。
通过组合两个forall
循环并消除布尔数组,可以提高效率。但是这段代码没有 beginner6789 的答案中展示的早期退出行为。
/isnumber { true exch { isdigit and } forall } def
编辑:如何使用stopped
为此
为了添加早期退出行为,我们可以利用{ ... stop ... } stopped
结构,它让我们从循环(或任何代码)的中间解脱出来,并自动提供一个布尔值。
/isnumber { { { isdigit not {stop} if } forall } stopped not } def
或者,使用更清晰的缩进,
/isnumber {
{
{
isdigit not {stop} if % if any of the individual tests fail, stop
} forall
} stopped % yields true if stop was called
not % reverse the boolean,
% so now false==stop was called
} def
类似的操作也可以通过exit
和堆栈杂耍来完成。
/isnumber {
true exch
{
isdigit not { pop false exit } if
} forall
} def