SWI-Prolog Bundle

I’ve cloned and updated the included Prolog bundle to use my Run script.

This can be found at GitHub: SWI-Prolog.tmbundle.

Run Prolog Program

In the continuing effort to do ‘productive’ tasks, but not actually the project I am supposed to be working on, I present a TextMate command to run the current prolog file, with nice HTML output, and input via a dialog box.

 1     #! /usr/bin/env ruby
 2     
 3     require ENV["TM_SUPPORT_PATH"] + "/lib/tm/executor"
 4     
 5     command = [ENV["TM_PROLOG"] || "swipl", "-s", ENV["TM_FILEPATH"]]
 6     two_line = false
 7     
 8     welcome = /^(Welcome to SWI-Prolog)|(Copyright )|(SWI-Prolog comes with)|(and you are welcome)|(Please visit)|(For help, use)/
 9     
10     TextMate::Executor.run(command) do |str, type|
11       if type == :err
12         if two_line
13           two_line = false
14           # this line is part of the previous message
15           "#{str}</div>"
16         # Is this a warning line?
17         elsif str =~ /(Warning):\s(.*):(\d+):/
18           warn, file, line = $1, $2, $3
19           filename = file.split('/')[-1]
20           two_line = true
21           file_link = "<a class=\"near\" href=\"txmt://open?line=#{line}&url=file://#{file}\">#{filename}</a>"
22           "<div class=\"#{warn}\">#{warn}: #{file_link}, line #{line}:"
23         elsif str =~ /(ERROR):\s(.*):(\d+):(\d+):\s(.*)/
24           file, line, char, message = $2, $3, $4, $5
25           filename = file.split('/')[-1]
26           file_link = "<a class=\"near\" href=\"txmt://open?line=#{line}&column=#{char}&url=file://#{file}\">#{filename}</a>"
27           "<div class=\"err\">ERROR: #{file_link}, line #{line}, col #{char}: #{message}</div>"
28         elsif str =~ /ERROR:\s(.*)/
29           message = $1
30           "<div class=\"test fail\">ERROR: #{message}</div>"
31         elsif str =~ /%\s(.*)\scompiled\s(.*)\ssec,\s(.*)\sbytes/
32           file, time, length = $1, $2, $3
33           filename = file.split('/')[-1]
34           file_link = "<a class=\"near\" href=\"txmt://open?url=file://#{file}\">#{filename}</a>"
35           "<div class=\"test ok\"> #{file_link} (#{length} bytes) compiled in #{time} sec.</div>"
36         elsif str =~ welcome
37           "<span class=\"copyright\" style=\"font-size:xx-small;\">#{str}</span> "
38         else
39           "<div class=\"output\">#{str}</div>"
40         end
41       else
42         "<div class=\"output\">#{str}</div>"
43       end
44     end

Prolog v Java

One program I tend to get around to writing in any new language I learn is a Sudoku solver. I originally wrote one in python, that solved every puzzle I could throw at it (although it had to guess and backtrack on failure in some cases).

Recently, I wrote one in Java. It was three classes plus a Driver/Main class. It took hours to write (at least the whole length of Phar Lap), and eventually it worked. I spent quite a while fixing up all of the syntactic errors I make in Java after programming in better languages again.

I then wrote one in Prolog. It was amazingly simple - it just defines a sudoku puzzle as a list of lists (each of which is 9 elements long), states that each element in each of the lists must be unique, and that each element in the first position of each list (columns) must be unique. Next, it states that each 3x3 grid must be unique, and that each element must be an integer of range 1-9.

That’s it. Solved.

My unique/1 predicate takes a list of items, and succeeds only if each item in the list is unique. This is simple:

  1. An empty list is unique.
  2. A list of one item is unique.
  3. A list of the form [A,B|X] is unique if A and B are different, and [A|X] and [B|X] are also unique-lists.

I also use a predicate that indicates a variable should be of the value 1-9, it’s possible to use advanced techniques and something called “Domains”, but I couldn’t get this figured out.

The only trick then is in the definitions. The simplest to understand, but messiest is to define the elements in the sudoku puzzle as each having unique names ([[A1,A2,...,A9],[B1,...]] and so on), and then passing some lists to unique, ie all ‘Ax’ elements, all ‘X1’ elements, and so on.

The coolest thing about Prolog is that you don’t need to say how things will happen, but just what the desired outcomes are. This can be dangerous - it’s rather easy to write something that will be executed in a ridiculous amount of time, so you often need to think about how to narrow the search space as quickly as possible. In Java, I needed to have methods that would remove elements from a possibles list, and then check to see if there was only one element, in which case it’s the actual value that belongs there, and so on. This is all error-prone code, wheras Prolog’s backtracking does all of this for you.

The biggest benefit is that in a puzzle that is fiendishly hard, Prolog will still come up with a solution, wheras some other languages, using the algorithm I defined above may not. And, in Prolog, you will automatically get multiple solutions to a problem if they exist.

Can you tell I love Prolog?