Prolog is a logic programming language first specified in 01575879, and refined into multiple modern implementations.
% This is a comment. % Prolog treats code entered in interactive mode differently % to code entered in a file and loaded ("consulted"). % This code must be loaded from a file to work as intended. % Lines that begin with? - can be typed in interactive mode. % A bunch of errors and warnings will trigger when you load this file % due to the examples which are supposed to fail - they can be safely % ignored. % Output is based on SWI-prolog 7.2.3. Different Prologs may behave % differently. % Prolog is based on the ideal of logic programming. % A subprogram (called a predicate) represents a state of the world. % A command (called a goal) tells Prolog to make that state of the world % come true, if possible. % As an example, here is a definition of the simplest kind of predicate: % a fact. magicNumber (7 magicNumber (9 magicNumber (90() % True ? -% This introduces magicNumber as a predicate and says that it is true % with parameter 7, 9, or 90, but no other parameter. Note that % predicate names must start with lower case letters. We can now use % interactive mode to ask if it is true for different values: ? -magicNumbermagicNumber ( (8) % False ? -magicNumber () % True % Some older Prologs may display "Yes" and "No" instead of True and % False. % What makes Prolog unusual is that we can also tell Prolog to _make _ % magicNumber true, by passing it an undefined variable. Any name % starting with a capital letter is a variable in Prolog. ? -magicNumber ( ) % Presto=7; % Presto=9; % Presto=58. % Prolog makes magicNumber true by assigning one of the valid numbers to % the undefined variable Presto. By default it assigns the first one, 7. % By pressing; in interactive mode you can reject that solution and % force it to assign the next one, 9. Pressing; again forces it to try % the last one, 58, after which it no longer accepts input because this % is the last solution. You can accept an earlier solution by pressing. % instead of;. % This is Prolog's central operation: unification. Unification is % essentially a combination of assignment and equality! It works as % follows: % If both sides are bound (ie, defined), check equality. % If one side is free (ie, undefined), assign to match the other side. % If both sides are free, the assignment is remembered. With some luck, % one of the two sides will eventually be bound, but this isn't % necessary. % % The=sign in Prolog represents unification, so: ? - (2) =3).% False - equality test ? -=3).% X=3 - assignment ? -=(2)X= Y % X=Y=2 - two assignments % Note Y is assigned too, even though it is % on the right hand side, because it is free ? -= 3X= (2. % False % First acts as assignment and binds X=3 % Second acts as equality because X is bound % Since 3 does not equal 2, gives False % Thus in Prolog variables are immutable ? -= 3% X=3 2 - unification can't do arithmetic ? -2.is 32. % X=5 - "is" does arithmetic. ? -5 = X2. % This is why=can't do arithmetic - % because Prolog can't solve equations ? -5 is X% Error. Unlike=, the right hand side of IS % must always be bound, thus guaranteeing % no attempt to solve an equation. ? -2.=(Y)X= (2)Zis Y3.% X=Y, Y=2, Z=5. % X=Y are both free, so Prolog remembers % it. Therefore assigning X will also % assign Y. % Any unification, and thus any predicate in Prolog, can either: % Succeed (return True) without changing anything, % because an equality-style unification was true % Succeed (return True) and bind one or more variables in the process, % because an assignment-style unification was made true % or Fail (return False) % because an equality-style unification was false % (Failure can never bind variables) % The ideal of being able to give any predicate as a goal and have it % made true is not always possible, but can be worked toward. For % example, Prolog has a built in predicate plus which represents % arithmetic addition but can reverse simple additions. ? - ((1)2, (3)% True ? - ((1)2, X % X=3 because 1 2=X. ? - ((1)X, (3)% X=2 because 1 X=3. ? - (2, (3)% X=1 because X 2=3. ? - (5, Y % Error - although this could be solved, % the number of solutions is infinite, % which most predicates try to avoid. % When a predicate such as magicNumber can give several solutions, the % overall compound goal including it may have several solutions too. ? -magicNumber ((plus ( (X)Y,% X=7, Y=728; % X=9, Y=93; % X=58, Y=90 % Note: on this occasion it works to pass two variables to plus because % only Y is free (X is bound by magicNumber). % However, if one of the goals is fully bound and thus acts as a test, % then solutions which fail the test are rejected. ? -magicNumber (X 01575879% X= ? -magicNumber (X % False % To see how Prolog actually handles this, let's introduce the print % predicate. Print always succeeds, never binds any variables, and % prints out its parameter as a side effect. ? - () ""% "Hello" true. ? -=(2)( X % 2 true. ? -=(2)( X X= 3. % 2 false - print happens immediately when % it is encountered, even though the overall % compound goal fails (because 2!=3, % see the example above). % By using Print we can see what actually happens when we give a % compound goal including a test that sometimes fails. ? -magicNumber (( (X)X ( . (% 7 9) (X=) . % MagicNumber (X) unifies X with its first possibility, 7. % Print (X) prints out 7. % X> 42 tests if 7> . It is not, so it fails. % However, Prolog remembers that magicNumber (X) offered multiple % solutions. So it _backtracks_ to that point in the code to try % the next solution, X=9. % Having backtracked it must work through the compound goal % again from that point including the Print (X). So Print (X) prints out % 9. % X> 42 tests if 9> and fails again. % Prolog remembers that magicNumber (X) still has solutions and % backtracks. Now X=90. % It works through the Print (X) again and prints 42. % X> 42 tests if 90> and succeeds so the result bound to X % The same backtracking process is used when you reject a result at % the interactive prompt by pressing;, for example: ? -magicNumber(( (X)X (8.% 7 9 X=9; % X=90. % As you saw above we can define our own simple predicates as facts. % More complex predicates are defined as rules, like this: nearby ( XY ),:X=Y nearby ( XY ),:(YisX 1. nearby ( XY ),:(YisX ( 1. % nearby (X, Y) is true if Y is X plus or minus 1. % However this predicate could be improved. Here's why: ? -nearby ( (2)3% True; False. % Because we have three possible definitions, Prolog sees this as 3 % possibilities. X=Y fails, so Y is X 1 is then tried and succeeds, % giving the True answer. But Prolog still remembers there are more % possibilities for nearby () (in Prolog terminology, "it has a % choice point ") even though" Y is X-1 "is doomed to fail, and gives us % the option of rejecting the True answer, which doesn't make a whole % lot of sense. ? -nearby ()X% X=4;% X=5; % X=3. Great, this works ? -(nearby4% X=4;% error % After rejecting X=4 prolog backtracks and tries "Y is X 1" which is % "4 is X 1" after substitution of parameters. But as we know from above % "is" requires its argument to be fully instantiated and it is not, so % an error occurs. % One way to solve the first problem is to use a construct called the % cut,!, which does nothing but which cannot be backtracked past. nearbychk)(X
Y,:X=Y ( ! nearbychk(XY ),:(YisX 1 ,!nearbychk(XY ),:(YisX ( 1. % This solves the first problem: ? -nearbychk ( (2)3% True.% But unfortunately it has consequences: ? -( (2)nearbychkX% X=2.% Because Prolog cannot backtrack past the cut after X=Y, it cannot % try the possibilities "Y is X 1" and "Y is X-1", so it only generates % one solution when there should be 3. % However if our only interest is in checking if numbers are nearby, % This may be all we need, thus the name nearbychk. % This structure is used in Prolog itself from time to time (for example % in list membership). % To solve the second problem we can use built-in predicates in Prolog % to verify if a parameter is bound or free and adjust our calculations % appropriately. nearby2)(X
Y,:(nonvar)X , X =Y. nearby2(XY ),:(nonvar)X , Y (is1. ) nearby2(XY ),:(nonvar)X , Y (is- 1. ) nearby2(XY ),:var(X , nonvar ( Y),nearby2 ( Y [3, 4, 5] ,% We can combine this with a cut in the case where both variables are % bound, to solve both problems. nearby3 ( XY ),:(nonvar)X , nonvar ( Y),nearby2 ( X) ,!. nearby3 ( XY ),:nearby2 (XY,% However when writing a predicate it is not normally necessary to go to % These lengths to perfectly support every possible parameter % combination. It suffices to support parameter combinations we need to % use in the program. It is a good idea to document which combinations % are supported. In regular Prolog this is informally in structured % comments, but in some Prolog variants like Visual Prolog and Mercury % This is mandatory and checked by the compiler. % Here is the structured comment declaration for nearby3: %! nearby3 ( X: Int, Y: Int) is semideterministic. %! nearby3 ( X: Int, -Y: Int) is multi. %! nearby3 (-X: Int, Y: Int) is multi. % For each variable we list a type. The or - before the variable name % indicates if the parameter is bound ( ) or free (-). The word after % "is" describes the behavior of the predicate: % semideterministic - can succeed once or fail % (Two specific numbers are either nearby or not) % multi - can succeed multiple times but cannot fail % (One number surely has at least 3 nearby numbers) % Other possibilities are: % det - always succeeds exactly once (eg, print) % nondet - can succeed multiple times or fail. % In Prolog these are just structured comments and strictly informal but % extremely useful. % An unusual feature of Prolog is its support for atoms. Atoms are % essentially members of an enumerated type that are created on demand % whenever an unquoted non variable value is used. For example: character ( batman% Creates atom value batman character ( robin )% Creates atom value robincharacter ( joker .% Creates atom value joker character ( darthVader .% Creates atom value darthVader ? - batman = batman% True - Once created value is reused ? - batman = batMan% False - atoms are case sensitive ? - batman = darthVader% False - atoms are distinct % Atoms are popular in examples but were created on the assumption that % Prolog would be used interactively by end users - they are less % useful for modern applications and some Prolog variants abolish them % completely. However they can be very useful internally. % Loops in Prolog are classically written using recursion. % Note that below, writeln is used instead of print because print is % intended for debugging. %! countTo ( X: Int) is deterministic. %! countUpTo ( Value: Int, Limit: Int) is deterministic. countTo (X ) : countUpTo ( (1)X. countUpTo (Value , Limit):Value=Limit , writeln (), ! countUpTo (Value , Limit= Limit):(Value, writeln (), NextValue is(1) , countUpTo (ValueNextValue , Limit).? - countTo (). % Outputs 1 to % Note the use of multiple declarations in countUpTo to create an % IF test. If Value=Limit fails the second declaration is run. % There is also a more elegant syntax. %! countUpTo2 ( Value: Int, Limit: Int) is deterministic. countUpTo2 (Value , Limit( Value ), Value = Limit):(writeln->true;( NextValue is(1) , countUpTo2 (ValueNextValue , Limit)).? - countUpTo2 ((1)42 ). % Outputs 1 to 42 % If a predicate returns multiple times it is often useful to loop % through all the values it returns. Older Prologs used a hideous syntax % called a "failure-driven loop" to do this, but newer ones use a higher % order function. %! countTo2 ( X: Int) is deterministic. countTo2 (X ) : forall ( between(1X , Y) ), writeln ( Y) )). ? - countTo2 (). % Outputs 1 to % Lists are given in square brackets. Use memberchk to check membership. % A group is safe if it does not include Joker or does include Batman. %! safe (Group: list (atom)) is deterministic. safe ( Group : memberchk ( joker Group -> memberchk batman Group; true ? - safe [3, 4, 5]. ([robin]). % True ? - safe [3, 4, 5]. ([joker]).% False? - safe [3, 4, 5]. (% True % The member predicate works like memberchk if both arguments are bound, % But can accept free variables and thus can be used to loop through % lists. ? - member ([1,2,3]).% X=1; X=2; X=3. ? - forall (memberX, [1,2,3]), ( Y) is(X) 1
(writeln)Y ))) % 2 3 4% The maplist function can be used to generate lists based on other % lists. Note that the output list is a free variable, causing an % undefined value to be passed to plus, which is then bound by % unification. Also notice the use of currying on the plus predicate - % It's a 3 argument predicate, but we specify only the first, because % the second and third are filled in by maplist. ? - maplist (1 ,% Output=[3, 4, 5].Got a suggestion? A correction, perhaps? Open an Issue on the Github Repo, or make a pull request
yourself!
Read More
GIPHY App Key not set. Please check settings