Как проверить, содержит ли строка только числа в Postscript

#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