lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: how to repeat a scheme function that creates a Score


From: Jean Abou Samra
Subject: Re: how to repeat a scheme function that creates a Score
Date: Wed, 6 Apr 2022 08:35:12 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.7.0

Le 06/04/2022 à 07:40, Jeff Olson a écrit :
Two questions below re: (a) looping and (b) random seed.


In order to investigate some other problems I'm having in a large project (pagination, memory usage) I learned enough scheme (barely) to define a function that generates a new score with each invocation:

--

\version "2.22.0"

\include "gen-music.ily"

% each invocation of scr generates a new score of different length
pcn = 0
scr = #(define-scheme-function () ()
   (set! pcn (1+ pcn))
   #{
     \score {
       \header { piece = \markup{ "Piece " #(number->string pcn) } }
       { \gen-music \bar "|."} }
   #} )

--

(The include file, gen-music.ily, is attached, and the above ~MWE is also attached as MWE-do-scr.ly.)

Question (a):

My main question is how to write a scheme function that will invoke my scr function N times, where N could be a number like 1000.

So far the only way I know how to do multiple invocations is by explicitly hard-coding "\scr \scr ..." in blocks and copy/pasting those blocks.

Looking for a better way, I realized I don't even know how to get one line of scheme to do 2 invocations (needed for tail recursion approach).  I can make them execute, in some cases, but not to produce pdf output (never gets to "Interpreting music...").  Part of this may be #(scr) vs $(scr).  The attached MWE file has lots of my failed attempts commented out (I'm at a teachable moment if someone has a moment to teach).  Or just point me to the right manual page(s).


Here is a piece of code that works:


$@(map (lambda (i) (scr)) (iota 10))


Now let's go through your attempt to see why they are failing.


#(define scors (lambda (n) (if (= n 1) (scr) ((scr) (scors (- n 1) )))))
#(scors 2)

This one is trying to apply a score (the result of (scr)) as a
function. A score is not a function. Also, if n = 1, (src) is
generated, which is not a list, so at the end of the day you
get an "improper list" (something that looks like a list but
doesn't have the empty list at the end of the cdr chain).
Rather do

#(define scors (lambda (n) (if (= n 0) '() (cons (scr) (scors (- n 1) )))))
#(scors 2)


That doesn't error out (scors is applied successfully), but no output
either, as you noticed. The problem is that when LilyPond reads a
toplevel expression introduced by #, it refrains from interpreting the
result, since that is the main way you can write code that operates via
side effects on the toplevel (think #(define ...) and such). You need
to be a little more insistent, with $.

#(define scors (lambda (n) (if (= n 0) '() (cons (scr) (scors (- n 1) )))))
$(scors 2)

Now that gives the error "bad expression type". Indeed, LilyPond knows how
to interpret a score, but not a list of scores. So you need to splice
the list into several scores that LilyPond will interpret separately.
You can do that with $@. Thus, this works:

#(define scors (lambda (n) (if (= n 0) '() (cons (scr) (scors (- n 1) )))))
$@(scors 2)


Now to your second attempt:

#(for-each (lambda (ignore) (scr)) '(1 2 3 4 5))

This has the same problem of # vs. $@. Also, it is using for-each, which
applies a function for its side effect but does not remember the result.
In this case, you want a list of the results, which is what map is for.

$@(map (lambda (ignore) (scr)) '(1 2 3 4 5))


Now to

#(begin (scr) (scr) (scr))

and

$(begin (scr) (scr) (scr))

The first discards the result because of #. The second calls src thrice, and
discards the results of the two first invocations -- that's what begin is
for. As Valentin pointed out while I was writing this, you could use add-score to let LilyPond know about your score via a side effect rather than by returning
them.

#(begin
  (add-score (scr))
  (add-score (scr))
  (add-score (scr)))


For the examples at the end, you need $@mine, which is the only one you didn't try :-)

Best,
Jean




reply via email to

[Prev in Thread] Current Thread [Next in Thread]