6 ms·
The thing about the cut is that it makes debugging extremely difficult because it (nearly always) completely nullifies the usual semantics of how you expect pre
by superdisk 3y ago
The thing about the cut is that it makes debugging extremely difficult because it (nearly always) completely nullifies the usual semantics of how you expect predicates to work. Stuff will seem to work and then break in mysterious ways. Also I've personally never found a valid use for them, there always seems to be a better, more logical alternative for any potential use case I've found.
It's kind of unfortunate IMO that most oldskool Prolog code out there is peppered with cuts and (->)/2 since it propagates a ton of bad habits.
Regarding the RLE problem, I posted a logical solution without cuts: https://news.ycombinator.com/item?id=35632344 https://news.ycombinator.com/item?id=35632344
- YeGoblynQueenne 3y agoI don't remember having any problems with debugging programs with cuts. But it should be said that I restrict myself to green cuts [1]. There are usual ways to avoid those, but not without making the code more complicated than it needs to be. Debugging soft cuts is more of a problem. That's because soft cuts don't appear in the tracer and it's difficult to follow program flow when it doesn't directly correspond with the source code as-written. Or at least that's the way the tracer behaves in SWI-Prolog which I use almost exclusively these days (plus Visual Prolog, but let's not talk about that). There may be a setting to configure it. I generally don't use soft cuts, I find them a bit clutter-y anyway. There's a run-length encoding program in the "99 Prolog problems" and it also doesn't use cuts: https://www.ic.unicamp.br/~meidanis/courses/mc336/2009s2/prolog/problemas/ https://www.ic.unicamp.br/~meidanis/courses/mc336/2009s2/pro... _______________ [1] A "green cut" is one that does not change the program's Least Herbrand Model (LHM). Or, in plain English, a green cut does not change the set of queries to the program, that succeed. A "red cut" is one that does change the program's LHM. A green cut only stops unproductive backtracking, or backtracking that diverts a proof down infinite branches. A red cut is one that cuts finite success branches. You should never use red cuts, and you should never run on a barge :P
- ghusbands 3y ago> There's a run-length encoding program in the "99 Prolog problems" and it also doesn't use cuts I tested them. p10, p11 and p13 all infloop when you ask what encodes to [[3,7], [2,8], {what 9 encoded to}]. p12 claims that [7,7,7,8,8,9] and [7,7,7,8,8,[9,1]] are valid encodings for [7,7,7,8,8,9] and then gives "Arguments are not sufficiently instantiated". As I implied in my original comment, there are plenty of examples out there, none of which actually work in both directions, without metaprogramming. Using clpfd seems to work well in all directions, which is neat, but that's a solver built atop prolog, rather than prolog itself.
- YeGoblynQueenne 3y agoRight, that's interesting. Did you try tabling the looping program? That's one way to avoid (some) looping behaviour. Note that p10 - p13 are meant to be called in mode (+,?) according to their comments, so (-,+) (or (-,?)!) is asking for trouble. I can see you were looking for a program that would run in arbitrary mode that is also non- or semi-deterministic ("gives one answer". Did you mean more like functional?) but I replied to another comment just to point to existing RLE programs, not to give examples of what you were asking. The instantiation error is the kind of thing that you'd use a "green" cut to avoid. The cut in that case would avoid unnecessary backtracking and not change the results of the program. I don't know about the malformed result. I'd have to pick at it a bit and I don't want to do this now. I also don't want to try and write a "pure" version, first because I don't think it satisfies any real-world need, and second because I already have a version that seems to work OK and uses cuts. I wrote it several years ago (possibly around 2010 ish). I'm copying it here keeping the idiosyncracies of my coding and commenting style at the time. %! run_length_encoding(+List, -Run_length_encoding) is det. % % Run_length_encoding is a list of all key-value pairs where each % key is an element in List and value the number of consecutive % repetitions of that element up to the first differing element. % % For example: % == % ?- run_length_encoding([a,a,a,b,b,b,b,b,b,c,c,c,a,a,f], RLE). % RLE = [a-3, b-6, c-3, a-2, f-1]. % == % run_length_encoding([], []-0):- !. run_length_encoding([H|List], Run_length_encoding):- run_length_encoding(List, [H-1], Run_length_encoding). % Last element in input list or single-element input list. run_length_encoding([], [C-F|Fs], Run_length_encoding):- reverse([C-F|Fs], Run_length_encoding). % Run of N consecutive identical elements run_length_encoding([C|Cs],[C-F|Fs], Acc):- ! % Orange ish; backtracking will produce successive % counts of repetitions of C from different indices in the list % I think. ,F_ is F + 1 ,run_length_encoding(Cs, [C-F_| Fs], Acc). % End of run of N consecutive identical elements. run_length_encoding([C|Cs], Fs, Acc):- run_length_encoding(Cs,[C-1|Fs], Acc). I give this as an example of a simple, short program you can write in Prolog to accomplish a simple, useful task, while using the cut to make your life easier, which is what I'm arguing about here. Note the uncertainty I had at the time about the use of the cut in the second auxiliary clause. I've left that comment in, in the interest of being honest about the difficulties in learning how to use the cut correctly. As I say, that was written many years ago. I'm not arguing that it's easy to learn to use the cut, I'm just saying that it makes your life easier once you've learned how to use it. I should probably have made that more clear in my comment. Please let me know if my code above breaks. I haven't tested it in ages. But please respect the documented call modes and determinism :) Edit: if you're wondering about the use of reverse/2, the goal is to simplify debugging; if you put accumulators in the head, you don't see their instantiations until recursion unrolls, so you don't know what's going on. Edit 2: To further clarify, as documented, the program only runs in one mode, (+,-). It was an auxiliary for another program that calculates the Shannon entropy of a string (tokenised as a list of characters) so there was no need for other modes. Not every Prolog program needs to run in all possible modes. And even when one does, it's often simpler to write multiple auxiliaries for additional modes. And why not do the simpler thing, when you can? The point is to write programs that have the desired behaviour. The constant complaint about Prolog (in this discussion also) is that it makes it hard to do simple things, not that it doesn't look pretty. So we should talk about how easy it is to do simple things, not how hard it is to write pretty programs.