Регистрация | Войти
Lisp — программируемый язык программирования
RSS
Два вопроса...
usrleon - 11.07.2010 19:57, Сообщений - 10
...от нубаса :)
1. Попытался написать макрос который по идее должен был бы возвращать строку без некой подстроки, путем конкатенации начала и конца исходной строки
Сам по себе код работает, а вот в макросе я попытался избавиться от двойного вызова (search ,sub ,str), но при попытке выполнить выдается ошибка "*** - SEARCH: *PATT* не является SEQUENCE".
Как сделать такое в макросе?

(defparameter *src* (string "qwerty<nya>asdfgh"))
(defparameter *patt* (string "nya"))

;want to call
;(getclearstr *src* *patt*)

;code
;(concatenate 'string
;            (substring *src* 0 (search *patt* *src*))
;            (substring *src* (+ (search *patt* *src*) (length *patt*))(length *src*)))


(defmacro getclearstr (str sub)
(let ((begin-sub (search sub str))))
   `(concatenate 'string
            (substring ,str 0 ,begin-sub)
            (substring ,str (+ ,begin-sub (length ,sub))(length ,str))
)
)

2. А вообще как по правильному сделать такую операцию, возможно я велосипед изобретаю.
[#]
http://l1sp.org/cl/let

а вообще: http://lisper.ru/pcl/
чтобы не задавать глупых вопросов
Love5an - 11.07.2010 20:08
[#] Ответ на комментарий от Love5an 11.07.2010 20:08
и, макросы тут не нужны
Love5an - 11.07.2010 20:08
[#] Ответ на комментарий от Love5an 11.07.2010 20:08
Я знаю что не нужен макрос, мне просто интересно было сделать именно макрос.
и, тебя не красит то что кроме "rtfm" тебе нечего ответить.
usrleon - 11.07.2010 23:12
[#] Ответ на комментарий от usrleon 11.07.2010 23:12
Дело в том, что это простейшие, тривиальнейшие, базовые вещи, а common-lisp - очень большой, во многих местах сложный язык, поэтому без умения курить/гуглить мануалы далеко не уедешь.
Макросы не вычисляют аргументы, в отличии от функций, поэтому твой (begin-sub (search sub str)) - некорректен, потому что получает во время экспанда не строки, а символы. Это соответствует тому, что ты бы попытался вызвать (search '*patt* '*src*) вместо (search *patt* *src*).

Можно так сделать:

(defparameter *src*  "qwerty<nya>asdfgh")
(defparameter *patt* "nya")

(defmacro string-without (string substring)
  `(let* ((string ,string)
          (substring ,substring)
          (entry (search substring string))
)

     (concatenate 'string
          (subseq string 0 entry)
          (subseq string (+ entry (length substring)))
)
)
)
Ander Skirnir - 12.07.2010 00:29
[#]
>> Как сделать такое в макросе?

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

Показательные примеры:


;; Мы хотим абстрагировать возможность запоминания только что выполненного
;; в if выражения в переменной it, и это непременно макрос -
;; он вводит форму if-it, который превращается в форму let (универсальное
;; средство создать локальное окружение и что-то в нём связать)

(defmacro if-it (condition then else)
  `(let ((it ,condition))
     (if it
         ,then
         ,else
)
)
)


;; А вот это уже обязательно функция - причём через apply, иначе бы появилось
;; бы затруднение и (ошибочное) желание сделать из этого макрос:

(defun strings (&rest args)
  (apply #'concatenate 'string args)
)


;; Опять же функция, используем две предыдущих вспомогательных формы:

(defun cut-first-in-string (part string)
  (if-it (search part string)
         (strings (subseq string 0 it)
                  (subseq string (+ it (length part)) (length string))
)

         string
)
)



treep - 12.07.2010 02:53
[#] Ответ на комментарий от usrleon 11.07.2010 23:12
>> мне просто интересно было сделать именно макрос.

да не, no way :)
treep - 12.07.2010 02:54
[#] Ответ на комментарий от usrleon 11.07.2010 23:12
Тебе не надо тут делать макрос.
Макрос это функция, вычисляющаяся при компиляции и не вычисляющая свои аргументы(т.е. аргументы у нее - "код как он есть")
Они предназначены для реализации синтаксических абстракций и для каких-либо compile-time вычислений.

если что-либо можно сделать функцией, макросом это делать не надо

(defun remove-substring (substring string)
  (let ((idx (search substring string)))
    (if idx
      (concatenate 'string
                   (subseq string 0 idx)
                   (subseq string (+ idx (length substring)))
)

      string
)
)
)

Вообще, есть CL-PPCRE

(cl-ppcre:regex-replace "nya" "qwerty<nya>asdfgh" "")

Про мануалы уже сказали.
Рекомендую почитать вот это:
http://www.segfault.kiev.ua/smart-questions-ru.html
Love5an - 12.07.2010 03:12
[#] Ответ на комментарий от Love5an 12.07.2010 03:12
>http://www.segfault.kiev.ua/smart-questions-ru.html
Спасибо, за хорошую ссылку.

На самом деле с самого начала было понятно что функция напрашивается, просто заинтересовался можно ли в макросе вычислять промежуточный параметр начал и так и эдак экспериментировать, практического смысла в этом нет. cl-ppcre я тоже видел, но опять таки задача была чисто учебная.

Вобщем всем спасибо пойду дальше читать.
usrleon - 12.07.2010 09:56
[#]
>Как сделать такое в макросе?

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

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

Например, так:
(defmacro getclearstr (str sub)
  (let ((str-var (gensym "STR"))
        (sub-var (gensym "SUB"))
        (begin-sub-var (gensym "BEGIN-SUB"))
)

    `(let ((,str-var ,str)
           (,sub-var ,sub)
           (,begin-sub-var (search ,sub ,str))
)

       (concatenate 'string
                    (subseq ,str-var 0 ,begin-sub-var)
                    (subseq ,str-var (+ ,begin-sub-var (length ,sub-var))
                               (length ,str-var)
)
)
)
)
)
И вот как он будет раскрываться. Выражение
(getclearstr (get-random-string 1) (get-random-string 2))
раскроется в
(LET ((#:STR852 (GET-RANDOM-STRING 1))
      (#:SUB853 (GET-RANDOM-STRING 2))
      (#:BEGIN-SUB854 (SEARCH #:SUB853 #:STR852))
)

  (CONCATENATE 'STRING (SUBSEQ #:STR852 0 #:BEGIN-SUB854)
               (SUBSEQ #:STR852 (+ #:BEGIN-SUB854 (LENGTH #:SUB853))
                       (LENGTH #:STR852)
)
)
)

Что здесь важно:
  1. выражения (get-random-string 1) и (get-random-string 2) вычислены лишь один раз. Как следует из названия (выдуманной) функции get-random-string, эта функция возвращает каждый раз разную строку. Если не заводить переменные для сохранения результатов вычисления этого выражения, то получится неочевидное поведение, отличающееся от поведения обычной функции (при вызове функции значения аргументов считаются один раз).
  2. выражения (get-random-string 1) и (get-random-string 2) вычисляются в том порядке, в каком они вычисляются при вызове обычной функции.
  3. для имен временных переменных используются уникальные символы, получаемые с помощью gensym. Это позволяет защититься от конфликтов имен.
  4. сам макрос не вычисляет нужное значение; эти вычисления делает код, возвращаемый макросом.
В Practical Common Lisp в главе про макросы это описано.
dmitry_vk - 12.07.2010 12:13
[#] Ответ на комментарий от usrleon 12.07.2010 09:56
>просто заинтересовался можно ли в макросе вычислять промежуточный параметр начал и так и эдак экспериментировать

Возникает вопрос: вычислять когда: при раскрытии макроса или в коде, сгенерированном макросом?

Если при раскрытии макроса, то тогда в макрос можно будет подавать только константы. Если в коде, сгенерированном макросом, то никаких ограничений надо; надо лишь выбирать не константное имя переменной, содержащей промежуточный результат, а генерировать его функцией gensym.

Вообще, макросы не следует использовать в тех случаях, когда функции будет достаточно. Макросы стоит использовать в том случае:
  1. если надо сгенерировать функции/классы/типы/и т.п. по каким-то шаблонам или по каким-то правилам
  2. если надо предоставить какой-то синтаксис, отличный от вызова функции, или скрыть детали реализации интерфейса или иным образом упрощать способ записи выражения. Например, синтаксис циклов loop или iterate
В случае, если какое-то вычисление можно задать функцией, но по каким-то причинам желательно часть вычисления или все вычисление делать во время компиляции, то следует использовать макросы компилятора (compiler macro). Пример, когда их стоит использовать:
  1. если при константных аргументах можно упростить работу функции во время работы программы. Примеры: предвычисление регулярных выражений, парсеров, строки форматирования, частные случаи вычислительных функций (например, (expt x 1) = x), функции от константных аргументов
  2. если при компиляции можно проанализировать выражения для аргументов функции и просигнализировать ошибку. Примеры: несоответствие числа и типа аргументов, неверный синтаксис строк форматирования и т.п.
dmitry_vk - 12.07.2010 12:24
@2009-2010 lisper.ru