Процесс работы
С консолью 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/
Добавить комментарий