Prolog中实现唯一绑定的更优方法咨询
Hey there! Great job getting that crossword solver up and running—even if all those manual not(X=Y) checks feel clunky and repetitive. Let's fix that and make your code more concise, readable, and aligned with declarative programming principles.
The Problem with Manual Uniqueness Checks
Writing out every pair-wise not(X=Y) is not only tedious, but it's also error-prone (easy to miss a pair!) and doesn't scale well if you ever expand the crossword size. Instead of telling Prolog how to check uniqueness, we can declare what we want: all words in the crossword must be distinct.
A Clean Solution: A Reusable all_unique Predicate
Most Prolog implementations don't have a built-in predicate for checking uniqueness of arbitrary terms, but we can easily build one using two basic Prolog tools: sort/2 and length/2. Here's how it works:
sort/2takes a list and returns a sorted version where duplicate elements are removed.- If the original list has all unique elements, the sorted list will have the same length as the original.
Here's the helper predicate:
all_unique(List) :- sort(List, SortedList), length(List, OriginalLength), length(SortedList, OriginalLength).
Updated Crossword Code
Now we can replace all those repetitive not(X=Y) lines with a single call to all_unique/1, passing in a list of all our crossword words:
word(astante, a,s,t,a,n,t,e). word(astoria, a,s,t,o,r,i,a). word(baratto, b,a,r,a,t,t,o). word(cobalto, c,o,b,a,l,t,o). word(pistola, p,i,s,t,o,l,a). word(statale, s,t,a,t,a,l,e). % Helper to check all elements in a list are unique all_unique(List) :- sort(List, SortedList), length(List, OriginalLength), length(SortedList, OriginalLength). crossword(V1,V2,V3,H1,H2,H3):- word(V1,V1a,V1b,V1c,V1d,V1e,V1f,V1g), word(V2,V2a,V2b,V2c,V2d,V2e,V2f,V2g), word(V3,V3a,V3b,V3c,V3d,V3e,V3f,V3g), word(H1,H1a,H1b,H1c,H1d,H1e,H1f,H1g), word(H2,H2a,H2b,H2c,H2d,H2e,H2f,H2g), word(H3,H3a,H3b,H3c,H3d,H3e,H3f,H3g), % Grid intersection constraints V1b = H1b, V1d = H2b, V1f = H3b, V2b = H1d, V2d = H2d, V2f = H3d, V3b = H1f, V3d = H2f, V3f = H3f, % Clean, scalable uniqueness check all_unique([V1,V2,V3,H1,H2,H3]).
Why This Works
- Declarative Style: Instead of manually enumerating every pair of words to compare, we're telling Prolog "all these words must be unique"—which is exactly what we mean, without getting bogged down in implementation details.
- Scalability: If you ever add more words to the crossword (e.g., V4, H4), you just add them to the list passed to
all_unique/1—no need to write dozens more comparison lines. - Reliability: No risk of missing a pair-wise check, which is easy to do with 6+ variables.
Bonus: Efficiency Tip (Optional)
For a small performance boost, you can reorder your goals to apply the intersection constraints earlier. This lets Prolog prune invalid word choices sooner, instead of generating all possible word combinations first:
crossword(V1,V2,V3,H1,H2,H3):- word(V1,V1a,V1b,V1c,V1d,V1e,V1f,V1g), word(H1,H1a,V1b,H1c,H1d,H1e,H1f,H1g), % Bind V1b to H1b immediately word(H2,H2a,V1d,H2c,H2d,H2e,H2f,H2g), % Bind V1d to H2b immediately word(H3,H3a,V1f,H3c,H3d,H3e,H3f,H3g), % Bind V1f to H3b immediately word(V2,V2a,H1d,V2c,V2d,V2e,V2f,V2g), % Bind V2b to H1d immediately word(V3,V3a,H1f,V3c,V3d,V3e,V3f,V3g), % Bind V3b to H1f immediately V2d = H2d, V2f = H3d, V3d = H2f, V3f = H3f, all_unique([V1,V2,V3,H1,H2,H3]).
This isn't strictly necessary for your current problem, but it's a good habit to pick up as you dive deeper into declarative programming and optimization.
内容的提问来源于stack exchange,提问作者user1714807

