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

от автора

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

С консолью 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.


ссылка на оригинал статьи https://habr.com/ru/articles/855810/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *