Jump to content

Variant of unfold supporting arbitrary function arguments

Recommended Posts

I like how the function unfold allows for rather concisely expressed transformations. I also like that with this function, methods can easily by commented out or added without changing the nesting structure of the overall Lisp program. However, what I do not like is that the unfold methods don't support any further arguments, and as a result the required preliminary work with def-unfold-set is rather cumbersome.  


So, I rolled by own version of unfold that addresses this shortcoming. This new function works as follows: you can use the names of arbitrary Opusmodus functions, as long as they expect an OMN sequence as first argument. The following example applies first the function gen-retrograde and then quantum to the material mat


(setf mat '((q c4 d4 e4) (h f4 q b3)))

(fn-unfold '((gen-retrograde) (quantum :fraction -0.2)) mat)

=> ((7W B3 7H._QT F4 -E..) (E E4 D4 S. C4 -ET))


If you would like to have functions with more concise names (as unfold does), just define functions with shorter names. Here are some examples.


(defun tr (sequence transpose &key (section NIL) (exclude NIL) (ambitus 'piano) (omn NIL))
  "Like pitch-transpose, but sequence as first param."
  (pitch-transpose transpose sequence :section section :exclude exclude :ambitus ambitus :omn omn))


(defun ld (sequence values &key set ignore seed (section NIL) (exclude NIL) (omn NIL))
  "Like length-divide, but sequence as first param."
  (length-divide values sequence :set set :ignore ignore :section section :exclude exclude :omn omn :seed seed))

With the definitions above, we can now use fn-unfold as follows for computing an octave transposition of mat and then applying length-divide to the second bar with the given settings.


(fn-unfold '((tr 12) (ld (2 3) :section 1)) mat)

=> ((Q C5 D5 E5) (3H E5 FS5 G5 3Q A4 BB4 CS5))


BTW: The definition of fn-unfold is pretty short. By far the longest part of this definition is the doc string 🙂


(defun fn-unfold (fns sequence)
  "Much like the buildin Opusmodus `unfold`, but instead works with functions and additional arguments can be given to the functions. Apply to `sequence` all fns in order.

* Arguments:
  - fns (list of lists): Each sublist has the form (<omn-fn> &rest <args>), where <omn-fn> is a function expecting an OMN sequence as first argument and arbitrary further argments, and <args> are the further arguments beyond the OMN sequence given to the function.
  - sequence: OMN sequence

* Examples:

Some material to use
;;; (setf mat '((q c4 d4 e4) (h f4 q b3)))

Remember: all functions used must expect a OMN sequence as *first* argument.

;;; (fn-unfold '((gen-retrograde) (quantum :fraction -0.2)) mat)

Some short-hand versions of common functions are defined for conciseness.

;;; (fn-unfold '((tr 12) (ld (2 3) :section 1)) mat)
  (reduce (lambda (seq fn)
	    (apply (if (functionp (first fn))
		       (first fn)
		       (fdefinition (first fn)))
		   seq (rest fn)))
	  fns :initial-value sequence))


Link to post
Share on other sites

Looks great, Torsten ! Thank you !


I have a question about the origianal unfold function and by extension to your new unfold function:


Regarding the use of customized methods, can we  put on def-library folders in the Def-Unfold_Sets

a file with our customized methods with your function ?

This file must have an specific name ?

The sets of  methods are automatically loaded in the startup , so we can call the specific sets in

the unfold function without having to evaluate the complete procedure of each method ?




Link to post
Share on other sites

> can we ... a file with our customized methods with your function?


You can put this function fn-unfold wherever you want, as long as that file is loaded at startup, and likewise also any other function including functions like tr above  🙂


we can call the specific sets in the unfold function without having to evaluate the complete procedure of each method ?


Questions concerning the original unfold function are better answered by Janusz, I guess... 


Good night (its 23:00 over here...),


Link to post
Share on other sites

we can call the specific sets in the unfold function without having to evaluate the complete procedure of each method ?


Yes naturally,


you can create your own unfold-set, save it in unfold-set folder and call it in counterpoint or unfold with the optional keyword :set and specify here the name of your set.



Link to post
Share on other sites

More important you can apply the the unfold method to a specific bar or even to an event in a given bar number:


Method applied to entire sequence:

(unfold 'name 'method sequence)


Method1 applied to 2nd bar only:

(unfold 'name '((method1 2) method2) sequence)


Method1 applied to the 3rd event in the 1st bar:

(unfold 'name '((method1 1 3) method2) sequence)



(setf mat '((s c4 d4 e4 f4 g4) (s c4 d4 e4 f4 g4) (s c4 d4 e4 f4 g4)))

(unfold 'om 't7 mat)
=> ((s g4 a4 b4 c5 d5) (s g4 a4 b4 c5 d5) (s g4 a4 b4 c5 d5))

(unfold 'om '((t-12 1 3) (t-1 2) (r 3)) mat)
=> ((s c4 mf d4 e3 f4 g4) (s b3 cs4 eb4 e4 fs4) (s g4 f4 e4 d4 c4))

(unfold 'om '? mat)
=> ((s c4 d4 e4 f4 g4) (s g4 a4 bb4 c5 d5) (s c4 bb3 gs3 g3 f3))


Why so may users do not read/examine the function documentation.🤓

Link to post
Share on other sites

Thank you, guys !!


Yes, I try to read all the stuff, but it does not mean I understand it all by myself...🤣🤣🤣🤣🤣🤣


And this quarantine is so lonely... I need to talk with you...


Fortunately, I have this marvelous forum !! With amazing people !

It´s very cool to have some functions with short "nicknames" !


Thanks a lot. I will try this.







Link to post
Share on other sites

I provided a variant for unfold that has some advantages (support for arbitrary function arguments instead of a custom symbol for each argument combination set up with def-unfold-set), but I did not say that it would replace it 🙂  In addition to what you said, the unfold function is also baked into your counterpoint function, while this alternative is not. 


apply the the unfold method to a specific bar


Sure. That also works with this variant, as shown above by using the argument section supported by most OPMO functions.  


(fn-unfold '((tr 12) (ld (2 3) :section 1)) mat)


> even to an event


Yes, that is something currently not supported by the alternative fn-unfold (except individual functions would support it). As you can see, the definition of fn-unfold is pretty short, more stuff could be added, if we want to. 


> Why so may users do not read/examine the function documentation.


Actually, I was aware of that, which is why I included an example showing the same feature (applying a specific function only to a certain bar) in my original post 😊

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Similar Topics

    • By o_e
      Why does the first example work and the second don't, what am I missing?
      (setf eins '((q d4 e4 fs4 gs4) (q d4 e4 fs4 gs4) (q d4 e4 fs4 gs4))) (unfold 'om '((pause 2)) eins) => ((q d4 e4 fs4 gs4) (-w) (q d4 e4 fs4 gs4)) (unfold 'om '((pause 2 2)) eins) => ((q d4 e4 fs4 gs4) (q d4 mf e4 fs4 gs4) (q d4 e4 fs4 gs4))  
    • By JulioHerrlein
      Dear All, 
      One interesting thing that could be implemented as a function could be a form of generating Negative Harmony.
      In the video below, there are some explanation of what it is and the origin in the Levy book.
      It was a trendy topic due to the Jacob Collier interview. And there are a lot of fun videos making versions of pop tunes using negative harmony.
      The way I understand it, it is simply a kind of mapping notes in relation to an axis, like in the figure below.

      So we need a function that could map a note in any register to another note in the closest register to the first on.
      So, any  C note will be mapped to G, all Db to F#, all D to F, all, Eb to E, all B to Ab, all Bb to A.
      It´s also possible to generate other mappings as well.
      I think that replace map or substitute map can do the job, but I´m not sure (I will try), but I find interesting to post it here to explore the idea.
      All the best,
      It´s kind of funny to sse in this por versions how every is upside down and how you can generate an entirely new song from exactly the same material.
      POP TUNES with negative harmony:
    • By AM
      here is a sketch for an alternative "binary-(or element-)layer-FUNCTION
      (defun element-layer (lists &key (rnd nil)) (let ((lists (if (null rnd) lists (rnd-order lists :list t)))) (car (last (loop for x in (rest lists) with list = (car lists) collect (setf list (loop for i in list with cnt = 0 when (equal i 0) collect (nth cnt x) and do (incf cnt) else collect i))))))) (element-layer (list '(1 0 0 1 1 0 0 1 0 0 0 0) '(0 2 3 0 4 5 0 6 0 7 8 0) '(11 12 13 14 15 16 17)) :rnd nil) => (1 11 2 1 1 3 12 1 4 5 13 6) ;;; hierarchic: every 0's will be replaced by the values from the next/sub-list...  
  • Create New...