Hello, my first ever post on any forum on the internet! I have a question about recursive counts which I've seen named as dimensional counts. I wondered if anyone could help me achieve this in PD using two variables, the count value and the dimension value. Examples are a 2 dimensional countdown from 4 would be 4321 a 3 dimensional countdown from 4 would be 4321 321 21 1 a 4 dimensional countdown from 4 would be 4321 321 21 1 321 21 1 21 1 1. Can any of you clever people help please? Been scratching my head about this for a little too long now! I've been trying to make one patch which deals with any two variables. Thank you in advance.
-
Recursive count up/down
-
@tildebrow To be honest, I've never heard of recursive counting before, so I can only conclude from your examples how it should work. This is my attempt:
The patch doesn't give any output for dimension 1, but I assume you won't need that anyway.
-
I gave it a shot too. I thought it would be more intuitive to do recursive stuff in a textual language so I figured it out in lua first:
function DimensionCount(dimension, number) if(dimension == 1) then print(number) else DimensionCount(dimension - 1, number) number = number - 1 if(number > 0) then DimensionCount(dimension, number) end end end
after that I tried to translate it into pd. The tricky part is storing the variables on the stack while a 'function call' happens. The only way I could figure out how to do it was lists in a
[trigger]
edit: changed to[t f]
for efficiency dimensional_count.pd -
@manuels Thank you so much! This is exactly the output I was looking for. Could you possibly give me any pointers on learning material for the thought process that would help me get to the point where I'd be able to solve something like this for myself? I'm so impressed and grateful!
-
@seb-harmonik.ar Thank you for taking the time to do this! It's amazing to see two working versions, one from @manuels and yours too. I'm now going to have fun going through to see how both versions work. The storing of variables was what was stumping me. Do you have any pointers on learning materials I could study to get better at thinking in a more logical way to help get me to the point of solving things like this? Thanks again - really appreciate your contribution and so impressed!
-
@tildebrow Don't use my poor attempt on recursive counting in practice, the solution of @seb-harmonik.ar is much better! I'm actually quite impressed by it ... That's probably as elegant and efficient as you can get with this in Pd.
-
@manuels thank you for your feedback. Still going to go through both with a fine tooth comb to try. to up my game!
-
@tildebrow well I have nothing specific, look up 'recursion'. What really helps is an exposure to functional programming.
But can try to walk through my process for this solution in depth:
first thing to do was look at the sequence and think about it.
If we increase the dimension, any particular 'level' has to expand into the result of itself if it were the argument for the current dimension.e.g. if the current dimension is 2 the sequence is 4321. When we increase the dimension the element '4' has to expand to 4321 (the result of dimension 2 count 4), the element '3' has to expand to '321' (the result of dimension 2 count 3) etc. If all these recursive expansions are done after one another then we will get the entire dimension 3.
So, if we want dimension 3 we have to call dimension 2 on each of the 'elements'. Each dimension 2 element has to call each of its dimension 1 elements (recursive). When 2 calls dimension 1 with a certain element it's over because those are our single values, therefore dimension 1 with a certain number is our 'base case' and we just print/output the number.
When implementing recursion we often start with a 'base case'. that's why dimension 1 is the 1st check in the recursive function.
My first implementation actually used a counter internally:
function DimensionCount(dimension, number) if(dimension == 1) then print(number) else for i = number, 1, -1 do DimensionCount(dimension - 1, i) end end end
but, you can also count down recursively:
function RecursiveCount(currentCount) print(currentCount) if(currentCount > 1) then RecursiveCount(currentCount - 1) end end
I thought there must be a way to combine these, so the next version was
function RecursiveCount(dimension, currentCount) DimensionCount(dimension - 1, currentCount) if(currentCount > 1) then RecursiveCount(dimension, currentCount - 1) end end function DimensionCount(dimension, number) if(dimension == 1) then print(number) else RecursiveCount(dimension, number) end end
from there the last piece was actually combining them into 1 function:
function DimensionCount(dimension, number) if(dimension == 1) then print(number) else DimensionCount(dimension - 1, number) if(number > 1) then DimensionCount(dimension, number - 1) end end end
then cleaning it to make it more
[spigot]
/pd friendly.Like I said the challenge in converting it to a pd patch is mainly keeping values on the stack. For this part you have to understand a bit about how pd calls its outlet code and look up stuff about the 'call stack'. The call stack is also a fundamental concept in recursion.
I considered[swap]
and[trigger]
to hold values on the stack, but I'm not even sure swap would work and trigger can't output multiple different atoms. Therefore I stored the stack variables in a list, and using[trigger]
passed one version into the first 'call' to DimensionCount and the other version to the 2nd (if number > 0 at the spigots)
the 2nd list is saved on the stack since it is in the middle of a C function call. (in pd's outlet code for[trigger]
)
Just now I tried making a version based on the original explicit counter-down version, but since 'i' has to be restored after every recursion it ends up being equivalent I think.Also, know that if you need to you can manage a stack explicitly. In this case, you would use one or two arrays to store the values of 'dimension' and 'number'. Everytime you want to make a function 'call' you have to store the previous values in the arrays and increment the size, then when the function call is over you decrement the arrays and get the old values back out. That's basically how you convert recursive calls into iterations/loops, and the alternative to recursion to manage variables in recursive problems.