Тема: Как проанализировать что пользователь в DCL-окне ввел целое число?

Как поанализировать на случай ошибки, что пользователь ввел целое число в Edit Box DCL окна?
Там же все значения строковые...
atoi  преобразует строки в целое - 0

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

все зависит от конкретной задачи, но на вскидку есть два варианта:
1. сперва добавляешь к строке скобки (круглые) спереди открывающую, сзади - закрывающую; теперь запускаем полученную строку в read и анализируеш тип всех элементов полученного списка - для текста получится SYM, для чисел - REAL или INT.
2. можно попробовать анилизировать строку при помощи WCMATCH - надо только с шаблоном повозиться...

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

Может, проверить строку на наличие точки или запятой как десятичного разделителя?

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

Я делаю примерно так:
1. (atoi ...
2. Если возвращает 0 (юзер ввел буквы вместо числа),
то что-то типа (alert "А ну введи целое число!") и потов возврат снова в то же диалоговое окно.

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

Проверка строки на соответствие целому числу:

(defun checkInt (str:Int / str:Test)
   (setq str:Test "") ; инициализация строки-шаблона
   (eval    (list 'wcmatch
          str:Int
          (repeat (strlen str:Int) ;формирование строки-шаблона
        (setq str:Test (strcat str:Test "#"))
          ) ;_ repeat
    ) ;_ list
   ) ;_ eval
) ;_ defun

Вызов:  (checkInt "12345678") возвращает T
             (checkInt "abcdef") возвращает nil

Удачи.

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

> kos
(11.04.2003 в 14:32:15)
Справедливо только для чисел >= 0. Кроме того целое число лежит в диапазоне от +2,147,483,647 до ?2,147,483,648. Все что больше или меньше сводится к ближайшему пределу. Например, (atoi "2147483648") вернет 2147483647 (на еденицу меньше). А функция (checkInt) при этом вернет t.

(defun checkInt2 (str:Int / answer)
   (if (equal (type (setq answer (read str:Int))) 'INT)
     answer
   );if
) ;_ defun
Возвращет число или nil.

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

Да, про отрицательные числа упустил... Поэтому предлагаю немного измененный вариант - инициализация строки-шаблона должна выглядеть так:
(setq str:Test "[-0123456789]")

По поводу диапазона целых чисел: эта функция только проверяет строку посимвольно на наличие "чужеродных" (не цифра) символов ничего при этом не преобразовывая.

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

> "Да,
про отрицательные числа упустил... Поэтому предлагаю немного измененный вариант - инициализация строки-шаблона должна выглядеть так:
(setq str:Test "[-0123456789]")" <

В этом случае "---45---64" тоже вернет t.

> "По поводу диапазона целых чисел: эта функция только проверяет строку посимвольно на наличие "чужеродных" (не цифра) символов ничего при этом не преобразовывая." <

Так это-то и плохо, вопрос ведь был про Edit Box. И предполагается, если функция вернет t, сразу переводить ее в число. А если даипазон превышен, то получится неверный результат.

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

1.
Неправда ваша: "---45---64" вернет nil.
Инициализация строки-шаблона задает только первый символ шаблона, полностью он формируется добавлением к нему символа "#" в функции (repeat). Кстати, после первого исправления функции забыл добавить:
(repeat (1- (strlen str:Int) ) и далее по тексту...

2.
Да, согласен. Я просто предложил реализацию идеи Сергея Попадьина (п.2).

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

По поводу сообщения в пятницу kos (11.04.2003 в 16:40:00), я решил, что строка
(setq str:Test "[-0123456789]")
просто опечатка, а на самом деле имелось в виду
(setq str:Test (strcat str:Test "[-0123456789]"))
а все остальное без изменений.
Но, если я правильно понял сообщение kos (14.04.2003 в 09:34:35) п.1. Функция должна выглядеть так:

(defun checkInt3 (str:Int / str:Test)
(setq str:Test "[-0123456789]") ; инициализация строки-шаблона
(eval (list 'wcmatch
str:Int
(repeat (1- (strlen str:Int)) ;формирование строки-шаблона
(setq str:Test (strcat str:Test "#"))
) ;_ repeat
) ;_ list
) ;_ eval
) ;_ defun

Но это еще хуже - после применения
(checkInt3 "1")
получим
error: bad argument type: stringp nil

Уж если делать, через wcmatch, то примерно так

(defun CheckInt4 (string / template lng)
   (if (> (setq lng (strlen string)) 1)
     (progn
       (setq template "[-+0123456789]")
       (repeat (1- lng)
          (setq template (strcat template "#"))
       );repeat
     );progn
     (setq template "#")
   );if
   (wcmatch string template)
);defun

А если продолжить идею через (read), то можно обойтись одной функцией и для целых чисел, и для действительных

(defun is_it_number (string type_nmb / answer)
   (cond
     ((equal type_nmb 'INT)
      (if (equal (type (setq answer (vl-catch-all-apply 'read (list string)))) type_nmb)
        answer
      );if
     )
     ((equal type_nmb 'REAL)
      (if (or
       (equal (type (vl-catch-all-apply 'read (list string))) type_nmb)
       (is_it_number string 'INT)
     );or
        (atof string)
      );if
     )
   );cond
);defun

Т.е. для проверки целого числа
(is_it_number "345" 'INT)
а для действительного
(is_it_number "345" 'REAL)

Ну а можно окончательно обрадовать пользователя, и позволить ему прямо в edit_box производить какие-то элементарные вычисления.

;*********************************************************************************
;  Функция phrase_to_words                                                       
;                                                                                 
;  Переделывает фразу в список слов в соответствии со списком разделителей.       
;  Если param=t, то разделители включаются в список слов, иначе не включаются.   
;  Возвращает список слов.                                                       
;*********************************************************************************

(defun phrase_to_words (string lst_divider param / lst_answer strmus strcus)
   (setq lst_answer '() strcus "")
   (repeat (strlen string)
     (if (member (setq strmus (substr string 1 1)) lst_divider)
       (progn
         (if (/= strcus "")
           (setq lst_answer (cons strcus lst_answer))
         );if
    (if param
           (setq lst_answer (cons strmus lst_answer))
    );if
    (setq strcus "")
       );progn
       (setq strcus (strcat strcus strmus))
     );if
     (setq string (substr string 2))
   );repeat
   (if (/= strcus "")
     (setq lst_answer (cons strcus lst_answer))
   );if
   (if lst_answer
     (reverse lst_answer)
     '("")
   );if
);defun

;*********************************************************************************
;  Функция mini_calc                                                             
;                                                                                 
;  Возвращает или само число, или результат последовательных вычислений, или nil.
;*********************************************************************************

(defun mini_calc (string / str_work answer lst_action lst_word lng_word nmb
                   action value)

   (setq str_work (vl-list->string
           (subst (ascii ".") (ascii ",")
             (vl-string->list (vl-string-trim " " string)))))
   
   (if (setq answer (is_it_number str_work 'REAL))
     
     answer
     
     (progn
       
       (setq lst_action (list
             (list "+" '+)
             (list "-" '-)
             (list "*" '*)
             (list "/" '/)
             (list "^" 'expt)
       ));setq
       
       (setq lst_word (phrase_to_words str_work (mapcar 'car lst_action) t))
       (if (or
        (= (car lst_word) "-")
        (= (car lst_word) "+")
      );or
    (setq lst_word (cons "0" lst_word))
       );if

       (if (and
        (> (setq lng_word (length lst_word)) 2)
        (/= (/ lng_word 2) (/ lng_word 2.0))
        (setq answer (is_it_number (car lst_word) 'REAL))
      );and
    (progn
      (setq nmb 1)
      (while (< nmb (1- lng_word))
        (if (and
          (setq action (cadr (assoc (nth nmb lst_word) lst_action)))
          (setq value (is_it_number (nth (1+ nmb) lst_word) 'REAL))
        );and
          (if (vl-catch-all-error-p (setq answer (vl-catch-all-apply action (list answer value))))
        (setq answer nil nmb lng_word)
        (setq nmb (+ nmb 2))
          );if
          (setq answer nil nmb lng_word)
        );if
      );while

      answer
    );progn
       );if

     );progn
   );if
   
);defun

Например, (mini_calc "20-10+5/3*5^0.5") вернет 5.0. Причем при вводе строки в качестве разделителя целой и дробной части числа можно использовать как ".", так и ",".

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

Принимаю все замечания, но прошу учесть, что пример клепался на колене в течение 2-х минут и никак не тестировался и не отлаживался. Поэтому отсутствие ошибок я расценил бы как фантастику.
Спасибо за полезные функции.

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

В поставку AutoCAD входит файл ai_utils.lst очень полезный для изучения.
Собственно оттуда:

;;;
;;; Pass a number, an error message, and a range.  If the value is good, it is
;;; returned, else an error is displayed. 
;;;  Range values:
;;;                 0 - any numeric input OK
;;;                 1 - reject positive
;;;                 2 - reject negative
;;;                 4 - reject zero
;;;                 
(defun ai_num (value error_msg range / good_value)
   (cond
     ;; is it a number
     ((not (setq good_value (distof value)))
       (set_tile "error" error_msg)
       nil
     )
     ;; is it positive
     ((and (= 1 (logand 1 range))
        (= (abs good_value) good_value)
      )
       (set_tile "error" error_msg)
       nil
     )
     ;; is it zero
     ((and (= 2 (logand 2 range))
        (= 0.0 good_value)
      )
       (set_tile "error" error_msg)
       nil
     )
     ;; is it negative
     ((and (= 4 (logand 4 range))
        (/= (abs good_value) good_value)
      )
       (set_tile "error" error_msg)
       nil
     )
     (T good_value)
   )
)

Возвращает либо вещественное число, либо nil.
Дальше анализируем вешественное число (что у него после запятой) примерно так:

(if (< (- <real> (fix <real>)) 0)
      (введено не целое)
      (введено целое)
)

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

Поправка:
(if (> (- <real> (fix <real>)) 0)
(введено не целое)
(введено целое)
)

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

> Andrew
1. Если не менять саму функцию, то хотя бы изменить ее описание:
;;; Range values:
;;; 0 - any numeric input OK
;;; 1 - reject positive
;;; 4 - reject negative
;;; 2 - reject zero

2. Для целых чисел не решет проблемы с диапазоном (см. leha (11.04.2003 в 15:40:56) )

3. Уже не так важно, но все-таки: 0 отнесли к положительным числам.

Re: Как проанализировать что пользователь в DCL-окне ввел целое число?

1. Функция не моя и просто скопирована без изменений. Сам использовал (и без изменений, и с модификациями) - очень удобно.
Это к тому что во многих случаях нет смысла изобретать велосипед.

2. А это вообщето не проблема, это именно допустимый диапазон целых чисел. И если планируются бОльшие числа, то рациональнее, по моему, работать с вещественными.