GIMP Script-Fu Первый Дан. Работа, Печать, Отладка

4c8a0e33b60faf2ed6b741d3b4cf125b.png

Процесс работы.

С консолью script-fu я работаю опосредованно, т.е весь код я пишу в текстовом редакторе, в моём случае это Emacs, а далее получившийся код копирую через буфер обмена в консоль, а результаты выведенные в консоль, если они чем то примечательны копирую через буфер обмена обратно в Emacs.

Обычно если я завершаю какую либо функционально обособленную группу функций я помещаю её в отдельный файл, библиотеку. А вот уже для загрузки библиотек я использую комманды загрузки:

;;задаём путь для загрузки библиотек
;;(define path-home "D:")
(define path-home (getenv "HOME"))
(define path-lib (string-append path-home "/work/gimp/lib/"))
(define path-work (string-append path-home "/work/gimp/"))
;;загружаем библиотеки
(load (string-append path-lib "util.scm"))
(load (string-append path-lib "defun.scm"))
(load (string-append path-lib "struct2.scm"))

Вывод данных.

Как только начинаешь работать, т.е писать более менее сложные функции, в интерпретаторе script-fu осознаешь необходимость иметь простое средство для вывода различных данных, без него просто не возможно что-то сделать, т.к. основным методом отладки здесь являтся вывод промежуточных результатов.

В script-fu есть несколько функций для перевода данных в текстовое представление и вывода его на консоль. Обычно пишут так:

(print 23)
23
#t
> (prin1 23)
23#t
(prin1 (string-append "number is " (number->string 23)))
"number is 23"#t
(print (string-append "list: " (apply string-append (map atom->string '(1 2 3)))))
"list: 123"

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

Нам нужна функция которая инкапсулирует весь это процесс преобразований, за простым и понятным интерфейсом, хотелось бы иметь функцию на подобии printf или format, которая сразу может распечатать всё что угодно в одном вызове. Здесь нам не понадобятся макросы, а лишь пара вспомогательных функций которые переводят любой (ну практически любой) набор данных тинисхемы в строку:

(define (to-str elem)
   (cond
    ((string? elem) elem)
    ((and (atom? elem)
          (not (vector? elem))) (atom->string elem))
    ((list? elem)
     (string-append "("
                    (apply string-append
                           (insert-between-elements
                            (map to-str elem) " "))
                    ")"))
    ((vector? elem)
     (string-append "#("
                    (apply string-append
                           (insert-between-elements
                            (map to-str
                                 (vector->list elem)) " "))
                    ")"))
    ))

;;полезная функция обработки списков, применяет функцию двух аргументов,
;;к списку, для первого применения используется начальный элемент инит
(define (fold f init lst)
  (do ((rez init (f rez (car l)))
       (l   lst  (cdr l)))
      ((null? l) rez)))


(define (insert-between-elements lst new-elem)
   (reverse
    (fold (lambda (prev elem)
             (if (not (null? prev))
                 (cons elem (cons new-elem prev))
                 (cons elem prev)))
          '()
          lst))
   )

(define (prn . args)
   (display
    (apply string-append
     (map to-str
          args))))

(to-str `((1 2 3 "hello" 'world 23 (q 3 e ,#(1 2 3) 4))))
;;"((1 2 3 hello (quote world) 23 (q 3 e #(1 2 3) 4)))"
(prn "Hello" " " "World!" "\n")
;;Hello World!
;;#t
(prn "x: " '(12 3 12 (23 q a b d) "next" "prev" q123) ", всё!" "\n")
;;x: (12 3 12 (23 q a b d) next prev q123), всё!

Это немного упрощённый вариант функции которой я пользуюсь, позволяющей печатать практически любые данные script-fu.

Не поймите меня не правильно, что тинисхема совсем «голая», нет! Например в файле инициализации определена функция foldr, аналог приведённой мной функции fold. Но я предпочитаю использовать итерационное определение обработки списка, а foldr имеет рекурсивное определение. Обратите внимание на цикл do в функции fold, у него отсутствует «тело» цикла. А что такое тело цикла? Это либо операции с «побочным эффектом», либо различные присваивания. Построение цикла в подобном стиле считается истинно функциональным. И с учётом того, что тинисхема нигде не обещает, что она имеет оптимизацию хвостовой рекурсии, постоянно использовать рекурсивные решения, это опрометчивый шаг.

Я поместил эти функции в файл util.scm. Кстати библиотеку функций я выложил на gitflic.ru

Отладка.

Отладка в Script-fu сводиться к распечатке промежуточных сообщений, позволяющих локализовать неисправность. А благодаря написанной выше функции, этот процесс значительно облегчается.

(define-m (reverse-str str)
   (prn "run reverse-str with: " str "\n")
   (list->string (reverse (string->list str))))

(reverse-str "смешарики")
;;run reverse-str with: смешарики
;;"икирашемс"#

Таким образом сегодня мы рассмотрели небольшую, но очень важную функцию универсальной печати, которая значительно облегчит нам дальнейшую работу со script-fu.

© Habrahabr.ru