GIMP Script-Fu First Dan. Work, Print, Debug
Work process.
I work with the script-fu console indirectly, that is, I write all the code in a text editor, in my case it is Emacs, and then I copy the resulting code via the clipboard to the console, and the results output to the console, if they are remarkable in some way, I copy via clipboard back to Emacs.
Usually, if I complete any functionally separate group of functions, I place it in a separate file, a library. But to load libraries I use the download commands:
;;задаём путь для загрузки библиотек
;;(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"))
Data output.
As soon as you start working, that is, writing more or less complex functions, in the script-fu interpreter you realize the need to have a simple tool for outputting various data, without it it is simply not possible to do anything, because The main debugging method here is the output of intermediate results.
Script-fu has several functions for converting data into a text representation and outputting it to the console. Usually they write like this:
(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"
but outputting mixed data turns into a complete nightmare of chains of calling functions for converting data into a string, concatenating strings and printing, and correctly outputting a list is a completely impossible task.
We need a function that encapsulates this entire transformation process, behind a simple and clear interface, we would like to have a function like printf or format that can immediately print anything in one call. Here we don’t need macros, but only a couple of auxiliary functions that convert any (well, almost any) set of tinischeme data into a string:
(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), всё!
This is a slightly simplified version of the function I use, which allows me to print almost any script-fu data.
Don’t get me wrong that the tinischeme is completely “naked”, no! For example, in the initialization file, the foldr function is defined, an analogue of the fold function I gave. But I prefer to use an iterative definition of list processing, and foldr has a recursive definition. Notice the do loop in the fold function; it does not have a loop body. What is a loop body? These are either operations with a “side effect” or various assignments. Building a cycle in this style is considered truly functional. And taking into account the fact that the tinischem does not promise anywhere that it has tail recursion optimization, constantly using recursive solutions is a reckless step.
I put these functions in the util.scm file. By the way, I posted the library of functions on gitflic.ru
Debugging.
Debugging in Script-fu comes down to printing intermediate messages that allow you to localize the problem. And thanks to the function written above, this process is greatly facilitated.
(define-m (reverse-str str)
(prn "run reverse-str with: " str "\n")
(list->string (reverse (string->list str))))
(reverse-str "смешарики")
;;run reverse-str with: смешарики
;;"икирашемс"#
Thus, today we looked at a small but very important universal printing function, which will greatly facilitate our further work with script-fu.