Suppose I have a list comprehension that returns a list of sequences, where the elements chosen depend on each other (see example below). Is there a way to (conveniently) program the number of elements and their associated conditions based on an earlier computation? For example, return type [[a,b,c]] or [[a,b,c,d,e]] depending on another value in the program? Also, are there other/better ways than a list comprehension to formulate the same idea?
(I thought possible, although cumbersome and limited, to write out a larger list comprehension to start with and trim it by adding to s a parameter and helper functions that could make one or more of the elements a value that could easily be filtered later, and the associated conditions True by default.)
s = [[a, b, c, d] | a <- list, someCondition a,
b <- list, b /= a, not (someCondition b),
otherCondition a b,
c <- list, c /= a, c /= b, not (someCondition c),
otherCondition b c,
d <- list, d /= a, d /= b, d /= c,
someCondition d, someCondition (last d),
otherCondition c d]
The question is incredibly difficult to understand.
The problem is "program" is not really an understandable verb in this sentence, because a human programs a computer, or programs a VCR, but you can't "program a number". So I don't understand what you are trying to say here.
But I can give you code review, and maybe through code review I can understand the question you are asking.
Unsolicited code review
It sounds like you are trying to solve a maze by eliminating dead ends, maybe.
What your code actually does is:
Generate a list of cells that are not dead ends or adjacent to dead ends, called
filtered
Generate a sequence of adjacent cells from step 1,
sequences
Concatenate four such adjacent sequences into a route.
Major problem: this only works if a correct route is exactly eight tiles long! Try to solve this maze:
So, working backwards from the code review, it sounds like your question is:
Solutions
You can solve a maze with a search (DFS, BFS, A*).
The function
solve
works by depth-first search. Rather than putting everything in a single list comprehension, it works recursively.In order to find a path from
start
toend
, ifstart /= end
,Look at all cells adjacent to the end,
neighbor <- maze end
,Make sure that we're not backtracking over a cell
guard (negihbor `notElem` path)
,Try to find a path from
start
toneighbor
.Don't try to understand the whole function at once, just understand the bit about recursion.
Summary
If you want to find the route from cell 0 to cell 19, recurse: We know that cell 18 and 19 are connected (because they are directly connected), so we can instead try to solve the problem of finding a route from cell 0 to cell 18.
This is recursion.
Footnotes
The guard,
Is equivalent to,
And therefore also equivalent to,
Or,
Or,
The first one,
someCondition a
, is fine.Footnote about
do
notationThe
do
notation in the above example is equivalent to list comprehension,The equivalent code in list comprehension syntax is,
Looks like you're trying to solve some logic puzzle by unique selection from finite domain. Consult these:
The way this helps us is, we carry our domain around while we're making picks from it; and the next pick is made from the narrowed domain containing what's left after the previous pick, so a chain is naturally formed. E.g.
You can trivially check a number of elements in your answer as a part of your list comprehension:
or just create different answers based on a condition,
I suppose you want to encode the length of the list (or vector) statically in the type signature. Length of the standard lists cannot be checked on type level.
One approach to do that is to use phantom types, and introduce dummy data types which will encode different sizes:
Now you can create vectors of different length which will have distinct types:
If the length of the output depends on the length of the input, then consider using type-level arithmetics to parametrize the output. You can find more by googling for "Haskell statically sized vectors".
A simpler solution is to use tuples, which are fixed length. If your function can produce either a 3-tuple, or a 5-tuple, wrap them with an
Either
data type: `Either (a,b,c) (a,b,c,d,e).You have Data.List.subsequences
You can write your list comprehension in monadic form (see guards in Monad Comprehensions):
(Explanation: The monad must be an instance of MonadPlus which supports failure.
guard False
makes the monad fail evaluating to mzero., subsequent results are appended with mplus = (++) for the List monad.)Update again, obtaining elements recursively, saving combinations and checks
result for size 2 with non trivial result: