what function can I use to make several nested calls to this other function?

158 views Asked by At

I need to call this perms function in this nested way:

e = [1,3,7,10, 25,50]
f = perms (perms (perms (perms (perms (perms [[]] e) e) e) e) e) e
g = [[]] ++ f -- final set of permutations 

perms :: (Eq a, Ord a) => [[a]] -> [a] -> [[a]] 
-- set [a] is a set of existing permutations and set [b] are the elements to add to [a] in all possible permutations
perms xss      [] = xss  -- no unique elements to create new permutations from
perms [xs]     ys = [xs ++ [y] | y <- ys \\ xs ]                         -- last list of xs in list xss 
perms (xs:xss) ys = [xs] ++ [ xs ++ [y] | y <- ys\\xs ] ++ perms xss ys 

In short I'm wondering if there is a function like a fancy use of map with (.) or something which can do six of these nested calls for me in a cleaner syntax than:

f = perms (perms (perms (perms (perms (perms [[]] e) e) e) e) e) e

I'm confident I could add another Int argument to the perms function for counting through the P(n, r) for r <- [1..6] but that's not of interest to me here. I'm wondering how I can do 6 nests of the same function calling itself recursively without the literal nesting?

But maybe it comes down to a bad choice of recursion design in my function and whole approach needs to be scrapped for something better?


Background:

This is me trying to work out a part of the Countdown game solution finder posited in this Graham Hutton Introduction to Haskell lecture. (His solution here which is more elegant I'm sure)

Using the Data.List library permutations function doesn't work, because in addition to the (n=720) 6-element permutations, I need the (720) 5-element permutations, the (360) 4-element permutations, the (120) 3-element permutations …, the (6) 1 element permutations and a null set solution. Because in this game you can use as many or few of the 6 numbers randomly selected as you wish.

I looked at the source code for permutations on hackage.org and it's a bit beyond my comprehension. So I thought I'd better try rolling this one for myself.

This is Hutton's solution for finding the choices of number permutations which I've only just chosen to look at.

-- Combinatorial functions

subs :: [a] -> [[a]]
subs []     = [[]]
subs (x:xs) = yss ++ map (x:) yss
              where yss = subs xs

interleave :: a -> [a] -> [[a]]
interleave x []     = [[x]]
interleave x (y:ys) = (x:y:ys) : map (y:) (interleave x ys)

perms :: [a] -> [[a]]
perms []     = [[]]
perms (x:xs) = concat (map (interleave x) (perms xs))

choices :: [a] -> [[a]]
choices = concat . map perms . subs
4

There are 4 answers

5
Daniel Wagner On

I would use iterate, as in

iterate (flip perms e) [[]] !! 6
6
amalloy On

Using the Data.List library permutations function doesn't work, because in addition to the (n=720) 6-element permutations, I need the (720) 5-element permutations, the (360) 4-element permutations, the (120) 3-element permutations …, the (6) 1 element permutations and a null set solution. Because in this game you can use as many or few of the 6 numbers randomly selected as you wish.

I'd much rather use permutations and then improve its result than reimplement the whole thing myself, not least because permutations will be implemented more efficiently than I would have thought to do it. In this case it's very easy to do for a general list, without needing to use its length explicitly. Of course this does not solve your direct question ("how to compose a function with itself N times"), but I think it's a much better solution to your real goal ("find permutations of subsets of a set").

import Control.Monad (filterM, (<=<))
import Data.List (permutations, tails)

subsets :: [a] -> [[a]]
subsets = filterM (const [False, True])

permutationsOfSubsets :: [a] -> [[a]]
permutationsOfSubsets = permutations <=< subsets

First we find all the subsets of the input list (setting in advance which numbers we will actually use), and then for each subset we try all its orderings.

*Main> permutationsOfSubsets [1,2,3]
[[],[3],[2],[2,3],[3,2],[1],[1,3],[3,1],[1,2],[2,1],[1,2,3],[2,1,3],[3,2,1],[2,3,1],[3,1,2],[1,3,2]]
*Main> length . permutationsOfSubsets $ [1..6]
1957

Observe that it exactly meets your specifications for a small list. For the full set of 6 numbers, it comes up with 1957 possibilities. Is that correct?

*Main> factorial n = product [1..n]
*Main> n `nPr` r = factorial n `div` factorial (n - r)
*Main> sum . map (6 `nPr`) $ [0..6]
1957

Yes, there are exactly 1957 ways to choose an ordered set of 0 or more elements from a population of 6.

3
radrow On

I think you are looking for foldl, aren't you?

foldl perms [[]] [e,e,e,e,e,e] == perms (perms (perms (perms (perms (perms [[]] e)e)e)e)e)e

Or if e is fixed, you may use replicate as well

foldl perms [[]] (replicate 6 e) == perms (perms (perms (perms (perms (perms [[]] e)e)e)e)e)e

So in your case it would work like that:

Prelude> take 10 $ foldl perms [[]] (replicate 6 e)
[[1],[1,3],[1,7],[1,10],[1,25],[1,50],[1,3],[1,3,7],[1,3,10],[1,3,25]]
2
Will Ness On

Here's a way:

> foldr ($) [[]] (replicate 1 $ flip perms e)
[[1],[3],[7],[10],[25],[50]]

> foldr ($) [[]] (replicate 2 $ flip perms e)
[[1],[1,3],[1,7],[1,10],[1,25],[1,50],[3],[3,1],[3,7],[3,10],[3,25],[3,50],
 [7],[7,1],[7,3],[7,10],[7,25],[7,50],[10],[10,1],[10,3],[10,7],[10,25],
 [10,50],[25],[25,1],[25,3],[25,7],[25,10],[25,50],[50,1],[50,3],[50,7],
 [50,10],[50,25]]

> foldr ($) [[]] (replicate 6 $ flip perms e)
...............

How does it come about? You have e.g.

f3 =                 perms   ( perms   ( perms  [[]]  e )  e )  e
   =                 perms' e ( perms' e ( perms' e  [[]] ))
   =                 perms' e $ perms' e $ perms' e $ [[]]
   = foldr ($) [[]] (perms' e : perms' e : perms' e : [])
   = foldr ($) [[]] (replicate 3 (perms' e))
   = foldr ($) [[]] (replicate 3 $ flip perms e)

perms' e xs = perms xs e
            = flip perms e xs
perms' e    = flip perms e
perms'      = flip perms

and then we abstract the 3 away.

So it's more or less just syntax. (well, not really, but close enough).

Of course we could also treat the perm' itself as the operator,

f3 =                    perms   ( perms   ( perms  [[]]  e )  e )  e
   =                    perms' e ( perms' e ( perms' e  [[]] ))
   =                    e `perms'` e `perms'` e `perms'` [[]]
   = foldr perms' [[]] (e    :     e    :     e    :     [])
   = foldr (flip perms) [[]] (replicate 3 e)
   = foldl perms [[]] (replicate 3 e)

which is even shorter.

Incidentally the very last, foldl snippet, is one rare example of the situation where foldl, and not foldl', is fine to be used. It is used to build the nested computational structure first (of the nested perms calls), which is then run through the lazy evaluation of Haskell.

I was focusing on the structure of the code when I used the ($) itself, which isn't present in the last snippet, only implied.