Простой восьмеричной читатель макрос в общий Лисп


В качестве упражнения, я реализовал простой макрос читателя в общий Лисп (с помощью SBCL). Он преобразует восьмеричное (только целые числа без знака) в цифры.

Использование:

* #z1234

668

Код:

(defun oct-string-to-number 
  (string)
  "Converts an octal string to a number.  Only digits from 0 - 7 are accepted; sign or decimal point symbols will cause oct-to-number to fail"
  (setq place 1)
  (setq result 0)
  (setq digits '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7))
  (loop for char across (reverse string)
    do 
    (setq pos (position char digits))
    (setq result (+ result (* pos place)))
    (setq place (* 8 place)))
  result)

(defun slurp-octal-digits 
  (stream)
  (setq string (make-array 0 :element-type 'character :fill-pointer 0 :adjustable t))
  "Slurps all digits from 0 - 7 from a stream into a string, stopping at EOF, no data, or a non-digit character." 
  (setq digits '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7))
  (with-output-to-string (out)
             (loop do
                   (setq char (read-char stream))
                   (setq isnum nil)
                   (if char
                   (progn
                     (setq isnum (find char digits))
                     (if isnum
                     (vector-push-extend char string)
                       (unread-char char stream))))
                   while (not (eq nil isnum))))
  string)

(defun octal-string-transformer 
  (stream subchar args)
  "Slurps an octal number from stream, and converts it to a number.  Number must be an unsigned integer."
  (setq oct-string (slurp-octal-digits stream))
  (oct-string-to-number oct-string))

;; Sets #z to call octal-string-transformer, so e.g. #z1234 will evaluate to 668.  Use #z as SBCL has #o already :-)
(set-dispatch-macro-character
 #\# #\z
 #'octal-string-transformer)

Я совсем новичок в общий Лисп, так что я бы высоко ценим обратную связь на все: оформление, стиль, идиома, правильности :-)

Отредактированный, чтобы добавить: на самом деле, форматирование вставленного фрагмента кода отображается как-то странно; углубление, которое присутствует, когда я редактировать исчезает, когда я представляю. Так что может быть немного мягким с отзывами о форматирование ;-)



620
2
задан 27 апреля 2011 в 10:04 Источник Поделиться
Комментарии
1 ответ

Прежде всего, обратите внимание о переменных: вы используете setq на ранее неопределенные и необъявленные переменные. Это на самом деле неопределенное поведение, и в большинстве реализаций будет создать глобальный динамический/специальные переменные. Для создания локальных переменных (которые предположительно предназначены) использовать давайте.

(defun oct-string-to-number 
(string)
"Converts an octal string to a number. Only digits from 0 - 7 are accepted; sign or decimal point symbols will cause oct-to-number to fail"

Вся эта функция может быть реализована как (разбирать-целое число строка :системы счисления 8). Однако ради обучения пошли все равно через функцию:

(setq digits '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7))
...
(setq pos (position char digits))

Чтобы выполнить итерации через список цифр для преобразования цифр в целое число, не является ни самым эффективным, ни самым емким, чтобы сделать это. Вы можете использовать цифры-чар-Р - функция, которая возвращает цифр int значение, если данный чар является цифрой, и nil в противном случае. (То же самое относится и в хлебало-восьмеричной-цифры , где вы делаете то же самое).

(loop for char across (reverse string)
do
(setq pos (position char digits))
(setq result (+ result (* pos place)))
(setq place (* 8 place)))

Вместо обращения строки и отслеживать место переменной, вы можете просто выполнить итерации по строке от начала и результат умножить на 8 каждый шаг:

(loop for char across string
do
(setq result (+ (* 8 result) (digit-char-p char))))

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

(defun oct-string-to-number (string)
"Converts an octal string to a number. Only digits from 0 - 7 are accepted; sign, non-octal digit or decimal point will signal error."
(reduce
(lambda (sum digit)
(+ (* 8 sum) (digit-char-p digit 8)))
string :initial-value 0))

4
ответ дан 28 апреля 2011 в 12:04 Источник Поделиться