Say I have a unique list of length 9 of the values between 1 and 9 inclusive in a random order (think sudoku), and I want to extract a the sub-list of the items that occur between the values 1 and 9 (exclusive). IE: between1and9([1,3,5,4,2,9,7,8,6],[3,5,4,2])
should be true.
At the moment I'm trying to use flatten/2
, but not having much luck. Here's my current tactic (assuming I enforce List ins 1..9, maplist(all_distinct, List), length(List, 9)
elsewhere to keep it tidy here/seperation of concerns):
between1and9(List,Between) :-
flatten([_,[1],Between,[9],_], List);
flatten([_,[9],Between,[1],_], List).
This version fails though when 1 or 9 are at the first or last position in List
, or if they're adjacent within List
. between1and9([_,1,9,_,_,_,_,_,_],[])
is true, but between1and9([_,1,9,_,_,_,_,_,_],_)
is false (and fails when I try to use it as a constraint to solve a bigger problem.)
It seems to be the same problem casuing both failures, flatten
doesn't seem to like treating unknowns as empty lists unless they're made explicit somewhere.
I can see why that would potentially be, if flatten
could "invent" empty lists in the first argument it would mean an infinite set of solutions for anything in the first argument. Although my full program has other constraints to prevent this, I can understand why flatten
might not want to accomodate it.
I can account for the edge cases (pun intended) by matching every permutation with disjunctions (ie: flatten([_,1,B,9,_],L);flatten([_,9,B,1,_],L);flatten([_,1,B,9]);flatten...
, And account for the Between as an empty list with: \*above permutations on flatten*\; ( Between = [], (\*permutations for either edge and 1/9*\) )
But that seems to be making an already longwinded solution (10 permutations of flatten in total) even worse (18) so I have two (strongly related) questions:
If I could do the following:
between1and9(L,B) :-
( ( X = 1, Y = 9 ); ( X = 9, Y = 1 ) ),
( ( Z1 = _; Z1 = [] ), ( Z2 = _ ; Z2 = [] ) ),
( B = _; B = [] ),
flatten([Z1,X,B,Y,Z2],L).
I wouldn't have to manually type out each permutation of match for flatten
. Unfortunately this and a few variations on it all unilaterally fail. Am I missing somethign obvious here? (I suspect opperator precedence but I've tried a few different versions.)
Or am I doing this completely wrong? The flatten/2
documentation suggests that in most cases it's an anti-pattern, is there a more prolog-ish* way to go about solving this problem? Given all the pitfalls I'm realising as I go through this I'm almost certain there is.
(Sorry, I'm painfully aware that a lot of the terminology I'm using to describe things in this is probably very wrong, I'm only kind of familiar with predicate/formal logic and much more used-to describing control flow type programming. Even though I understand logic programming in practice reasonably well I'm struggling to find the language to talk about it robustly yet, I will amend this question with any corrections I get.)
Some background: I'm new to prolog and testing out my understanding by trying to extend one of the many sudoku solvers to solve a strange variety of sudoku I found in some puzzles I printed out years ago where you're shown the sum of all the numbers that appear between the 1 and the 9 in any given row or column as an extra hint, it's kind of like a mix of sudoku and picross. The solver as it stands now is on swish: SumSudoku(swish). Although it may be a mess when you get to it.
*Corollary quesiton: is there a prolog version of the word "pythonic?"
You could use good old append/3
for this. Is it possible that you wanted append/3
all along but somehow thought it is called flatten
?
For the "1 comes before 9" case, you'd write:
between_1_and_9(List, Sublist) :-
append(_, [1|Rest], List),
append(Sublist, [9|_], Rest).
You need to swap 1 and 9 for the "9 comes before 1" case.
This also leaves a "spurious choice point" (Thank you @PauloMoura for the comment). Make sure to get rid of it somehow.
As for "Pythonic" (and this comes from a recovering Pythonista), I can only say, rest assured:
There is always more than one obvious way to do it in Prolog.
You don't even have to be Dutch.