I'm writing a program in Haskell that generates random triads of ints in order to play them as chords using Euterpea. Here's what my list of lists looks like (obviously, it's infinite, but I'll take 10 $ chop 3 $ randInts, where chop is just a function that splits a list up into (ordered) groupings of three and randPitch simply generates ints over the interval 24 - 84):
[[27,33,48],[53,59,82],[31,49,62],[50,60,63],[51,56,79],[57,59,60],[52,63,69],[52,77,79],[32,32,37],[39,77,84]]
To learn a bit about music theory, I'm applying various filters to these triads. The one that's giving me trouble (at least to implement in Haskell, which is a new language for me) is what Dmitri Tymoczko would call "conjunct melodic notation"--i.e., melodies (in this case the top 'note' or pitch of the 3-tuple) should only move short distances.
What I want to do is write a function that recursively filters the endless list of triads such that what results is a list of triads whose top note only moves by <= n between each chord: essentially, a kind of random walk where you are only allowed to move n semitones for any one step. Here are the functions I have so far:
jumpSize :: (Num a, Ord a) => [[a]] -> [[a]]
jumpSize (_:[]) = []
jumpSize (x:y:[]) = (abs (maximum x - maximum y) : x) : []
jumpSize (x:y:zs) = (abs (maximum x - maximum y) : x) : jumpSize (y:zs)
This will prepend the distance from triad x to y at the beginning of list x. So take 5 $ jumpSize $ chop 3 $ randPitch gives me:
[[34,27,33,48],[20,53,59,82],[1,31,49,62],[16,50,60,63],[19,51,56,79]]
I tried to write a recursive function that filters out all the chords with jumps of great than n, recalculates the new jumpSizes and then applies itself again. However, I'm having trouble because this function is either crashing GHCi or giving me all sorts of problems. Structurally, I would like to still produce the chords by filtering over the random array, but I think I'm missing something about how to best do this in a functional language. Maybe I need to supply an initial generator chord? Thanks!
jumpRecur :: (Num a, Ord a) => a -> [[a]] -> [[a]]
jumpRecur n (xs)
| [x | x <- xs, head x > n] == [] = xs
| otherwise = jumpRecur n $ jumpSize $ filter (\x -> head x <= n) xs
Here's a sample output:
*Main> jumpRecur 5 $ jumpSize $ chop 3 $ take 1000 $ randPitch
[[2,2,2,2,2,2,2,2,70,70,76],[1,1,1,1,1,1,1,1,55,74,74],[1,1,1,1,1,1,1,5,26,28,73], [3,3,3,3,3,3,0,1,26,69,74],[5,5,5,5,5,5,5,5,33,43,77],[0,0,0,0,0,0,0,0,47,67,82],[2,2,2,2,2,2,4,4,37,66,82],[3,3,3,3,3,3,3,3,59,69,84],[4,4,4,4,4,4,4,4,59,79,81],[4,4,5,5,5,5,5,5,28,69,77],[5,5,5,5,5,5,5,5,54,68,73],[0,0,0,0,0,0,0,0,32,73,78],[5,5,5,5,5,5,1,4,52,62,78],[0,0,0,0,0,0,0,0,58,71,73],[3,3,3,3,3,3,4,4,25,64,73],[1,1,1,1,1,3,3,3,35,42,76],[4,4,4,4,4,4,4,4,35,39,77]]