Осваиваю Lisp. В качестве первого упражнения решаю следующую задачу. Имеется текстовый файл с часами разработчика в формате
<дата> <модификатор деятельности> <количество часов> <описание>
(Файл может содержать комментарии, начинающие с знака «#».) Необходимо подсчитать общее количество часов работы.
Например, для файла
20.05.2011 RD 2 Чтение глав «Функции» и «Параметры» из PCL
20.05.2011 OT 1 Установка SBCL
20.05.2011 CO 2 Кодирование и отладка функции count-hours
Результат должен быть 5.
Вот мое решение:
;;;; Program reads file with working hours of developer                                                                                        
;;;; and outputs sum of hours.
;; Counts working hours from specified filename.                                                                                               
(defun count-hours (filename)                                                                                                                
  (let ((in (open filename :if-does-not-exist nil)) (hours 0))                                                                                 
       (when in                                                                                                                                
             (loop for line = (read-line in NIL) while line do          
                   ; Doesn't process line with comments and empty line                                                                       
                   (if (not (or (= (length line) 0) (char= (elt line 0) #\#)))             
                       (setf hours (+ hours (parse-integer (get-word line 3))))))                                                            
             (close in)) hours))                                                                                                               
                                                                                                                                               
;; Returns nth word in string. Words are separated by Space and Tab                                                        
(defun get-word (str num)                                                                                                                    
  (let* ((white-spaces (list #\Space #\Tab)) (pos (get-white-space-min-pos white-spaces str)))                                                 
       (if (not pos) "0"                                                                                                                       
           (if (= (1- num) 0) (subseq str 0 pos)                                                                                               
               (get-word (string-trim white-spaces (subseq str pos)) (1- num))))))
                                                                                                                                               
                                                                                                                                               
;; Returns minimum position in str of character from list of characters (white-spaces).
;; If str doesn't have characters from list then nil is returned.                                                                              
(defun get-white-space-min-pos (white-spaces str)                                                                                              
  (let ((min-pos NIL))                                                                                                                         
       (dolist (white-space white-spaces)                                                                                                      
               (let ((pos (position white-space str)))                                                                                         
                    (if (not min-pos) (setf min-pos pos))                                                                                      
                    (if (and pos min-pos) (setf min-pos (min min-pos pos)))))                                                                  
       min-pos))                                                                                                                               
Запускать можно так:
(сount-hours «wh-dimv.txt»)
Собственно вопросы:
1. Не кажется ли вам, что здесь все написано в императивном стиле, просто с использованием скобочек? Если да, то направьте на путь истинный.
2. Есть ли в коде места, которые лучше было бы реализовать с помощью макросов? Я таких мест сейчас вижу, скорее всего, потому что слишком мало знаком с lisp. Или задача слишком маленькая, чтобы понадобились макросы?




