#string #numbers #compare #postscript
#строка #числа #Сравнить #postscript
Вопрос:
Я пытаюсь понять, как удобно проверить, содержит ли строка в Postscript, например (123456)
, только числа. например, 0,1,2,3,4,5,6,7,8 или 9.
так, например:
(1235) isnum
следует поместить true в стек
(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)] amp;amp; char == number[j]
char isnum {
/counter counter 1 add def
}if
}for
counter numlen eq
{
true
}
{
false
}ifelse
}def
(12345) (12345) isnumber ==> true находится в стеке
Или есть какая-нибудь более быстрая процедура?
Ответ №1:
Существует другой стиль написания такого кода, который использует тот факт, что реляционные операторы postscript также работают со строками.
Итак, если вы можете удалять символы один за другим, используя getinterval
для создания строк длиной = 1, вы можете сравнить их напрямую с (0)
или (9)
.
/isdigit { dup (0) ge exch (9) le and } def
Это делает шаг «map» из моего другого ответа немного более сложным.
/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
И это приводит к другой идее. Если для строк, с которыми вы работаете, существует разумная максимальная длина, вы можете сравнить с нулями или девятками, т.Е.. (0000)
и (9999)
для строки длиной = 4.
/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
неявно выполняют циклы над содержимым строки, чтобы выполнить свою работу.
Ответ №2:
Вот фрагмент программы, готовый для настройки. При этом проверяется, является ли каждый символ в строке цифрой. Цифры в строке являются целыми числами от 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
EDIT2: это делает то, что вы просили. Мог бы быть более простой способ, если бы я только знал.
/isnumber? { { dup 48 ge exch 57 le and
{/flag true def}{/flag false def exit} ifelse
} forall
flag
} def
EDIT3: Спасибо 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
EDIT5: Еще раз спасибо luser droog за подсказки, которые заставили меня задуматься. Это по-прежнему оставляет true в стеке для строки нулевой длины:
/isnum {true exch {dup 48 ge exch 57 le and and}forall } def
EDIT6: Еще раз спасибо за информацию о остановленных и остальных тоже. Это очень полезно. Мне нужно больше времени для изучения и экспериментов.
Я добавил тест на случай, если оператор используется для вещей, отличных от строк, поэтому возвращает 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
Комментарии:
1. Хороший материал. Возможно, вы захотите провести расследование
stopped
. Что-то вроде{ { ... } loop } stopped
позволит вам вызвать,stop
чтобы выйти из цикла, иstopped
выдаст логическое значение, сообщающее, был ли вызван `stop` или нет. Кроме того, смотрите Мой ответ для другого подхода.2. Добавлен пример
stopped
к моему ответу.3. Некоторые примеры и пояснения
stopped
см. В Этом моем посте , в котором есть материал, написанный для проекта документации SO. Это ключ к пониманию и использованию обработки ошибок в PS.4. Для кода «EDIT5» вы можете легко добавить проверку на нулевую длину. Просто используйте результат для начального логического значения
true
. ie.{ dup length 0 ne exch { dup...} forall }
5. Я добавил EDIT6 с добавлением проверки остановки и длины. Просто сегодня вечером не хватило времени.
Ответ №3:
Это звучит как задание для стратегии сокращения карты. Для базового варианта проверка одного символа:
/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
помощью, поскольку это, вероятно, будет operator и, следовательно, не совместимый тип для 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
Аналогично можно было бы сделать с помощью just exit
и stack juggling .
/isnumber {
true exch
{
isdigit not { pop false exit } if
} forall
} def