This is one of these situations where, in order to avoid typing errors, you need to be able to refer to both the whole parameter and to its subcomponents thru proper names.
Fortunately, Haskell provides just that. This is known as the “as patterns”. More details here: SO-q30326249.
In your case, you could note your graph parameter as: g@(Graph(pairs))
. Then, g
is your graph object, and pairs
is the corresponding list of type [(Vertex,OutNeighbors)]
.
You do not tell us about your contains
function, but it is possible to infer that its type is:
contains :: Vertex -> Graph -> Bool
With that in mind, a version of your graph function taking an arbitrary iteration count can be written this way:
type Vertex = Int
type OutNeighbors = [Vertex]
data Graph = Graph [(Vertex,OutNeighbors)] deriving (Eq, Show, Read)
funcN :: Int -> Graph -> Graph
funcN iterCount g@(Graph(pairs)) =
if (iterCount <= 0) then g -- nothing to do
else let
gm1 = funcN (iterCount - 1) g -- recursion
fn = \(v,ngs) -> contains v gm1 -- filtration
in
Graph (filter fn pairs)
Using the same techniques, a tentative version of the contains
function could be like this:
contains :: Vertex -> Graph -> Bool
contains v g@( Graph [] ) = False
contains v g@( Graph ((v0,ngs0):pairs) ) = (v == v0) || contains v (Graph(pairs))
This second function is a bit more complicated, because lists can be described thru 2 patterns, empty and non-empty.
Finally, a version of the function that does exactly 10 iterations can be written like this:
func10 :: Graph -> Graph
func10 g = funcN 10 g
or also in a more concise fashion using partial application (known in Haskell circles as currying):
func10 :: Graph -> Graph
func10 = funcN 10
Addendum: library style, using nest:
If for some reason “manual recursion” is frowned upon, it is possible to use instead the nest :: Int -> (a -> a) -> a -> a
library function. It computes the Nth compositional power of a function, using recursion internally.
Then one just has to write the single iteration version of the graph function. The code looks like this:
import Data.Function.HT (nest)
funcNl :: Int -> Graph -> Graph
funcNl iterCount g0 = let
-- 2 local function definitions:
ftfn g1 (v, ngs) = contains v g1
func1 g2@(Graph(pairs)) = Graph (filter (ftfn g2) pairs)
in
nest iterCount func1 g0