in ,

Learn X in Y minutes Where X = Prolog, Hacker News

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 
 
 % 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:   ? -  
 magicNumber 

() % True ? -

 magicNumber   ( (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  ? -  
 
  =  3  
  X 

= (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  
 
  2. 

% X=3 2 - unification can't do arithmetic ? -

 
   is   3  
 
  2.  % X=5 - "is" does arithmetic.  ? -  
 5   = X  
 
  2.  % This is why=can't do arithmetic -                                       % because Prolog can't solve equations  ? -  
 5    is   X  
 
  2. 

% Error. Unlike=, the right hand side of IS                                      % must always be bound, thus guaranteeing                                      % no attempt to solve an equation. ? -

 
  =
(Y)
  X  
= (2) 
   Z  
 is   Y 
 
   

 3. 
 % 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) 
  print 

 (  X   % 2 true.  ? -  
 
  =
 (2) 
  print 

 (  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   ( 
  print  
 ( (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 

(

  print  
 ( (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   (  X 
 
,  

Y )

: 
  X  
=
   Y    nearby   (  X 
 
,  

Y )

: 
  (Y  
 is 
   X       1.  nearby   (  X 
 
,  

Y )

: 
  (Y  
 is 
   X   (  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  ? -  
 nearby 

(

  4  
  % 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  
 (
   X 
 
,  

Y )

: 
  (Y  
 is 
   X       1  ,  
! 
  nearbychk  
 (
   X 
 
,  

Y )

: 
  (Y  
 is 
   X   (  1. % This solves the first problem:  ? -  
 nearbychk   ( (2) 
  3  
  % True. 
  % But unfortunately it has consequences:  ? -  
 nearbychk 

( (2)

  X   
 % 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  
 (
   X 
 
,  

Y )

: 
  (nonvar)  
   X  ,   Y  (is  
 
      1.  )  nearby2  
 (
   X 
 
,  

Y )

: 
  (nonvar)  
   X  ,   Y  (is  
 
   -  1. )  nearby2  
 (
   X 
 
,  

Y )

: 
  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   (  X 
 
,  

Y )

: 
  (nonvar)  
   X  ,   nonvar   (  Y 
 
 ),  
 nearby2   (  X)  ,  
 !.   nearby3   (  X 
 
,  

Y )

: 
  nearby2   (
   X 
 
, 

Y

 
 

% 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 robin 

character ( 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  
 ) 

: 
  (Value 

= Limit

,   writeln   ( 
 
  ),       NextValue   is 
  
 Value 

(1) ,      countUpTo (

  

NextValue , 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  
 ) 

: 
  (writeln 

( Value ),      Value = Limit

 ->  
 true 
  ;  
 (          NextValue   is 
  
 Value 

(1) ,          countUpTo2 (

  

NextValue , 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  
 (
   1     
X , 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   ( 
 member  
 
  X  
, [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!      Brave Browser Read More

  

What do you think?

Leave a Reply

Your email address will not be published. Required fields are marked *

GIPHY App Key not set. Please check settings

What’s It Like to Work on League of Legends ?, Hacker News

What’s It Like to Work on League of Legends ?, Hacker News

Someone used neural networks to upscale a famous 1896 video to 4k quality, Ars Technica

Someone used neural networks to upscale a famous 1896 video to 4k quality, Ars Technica