Showing posts with label Prolog. Show all posts
Showing posts with label Prolog. Show all posts

Tuesday, January 4, 2011

Seven Languages in Seven Weeks

Today I eventually received my own copy of "Seven Languages in Seven Weeks". To be sincere, I started reading the electronic version some time ago (Pragmatic Programmers had a huge discount some time ago, just after the "Programming Ruby" at 10$).

Seven Languages in Seven Weeks


I'm not going to criticize the language choice. In fact, I think that the languages are a wise selection (Ruby, Io, Scala, Clojure, Prolog, Haskell, Erlang). They are all interesting languages and have something other languages do not have. Although I'm quite into Python, I'm not going to complain for its exclusion: the author is far more confident with Ruby (as far as he tells) and that means that his explanations are surely more accurate.

I love the idea of the seven languages in seven weeks: beware, this is not a "Teach yourself C++ in 21 bloody hours". The author is well aware that you could not possibly learn seven languages in seven weeks, but you can understand what those languages are about in seven weeks. I'm thinking about the potential this book has when read by people grown up in closed Microsoft or Java contexts (or perhaps even C and C++). If after reading this book he is still convinced that all the languages are the same he is either a compiler or a very distract reader (and probably hasn't understood a thing about the book).

Just seeing the languages is a wonderful enlightening experience. Realizing how much useless boilerplate compiler-friendly code some languages force developers to read is the first step towards better ways of programming.

And what about me? Well... I already knew 5 of the 7 languages quite well. I have known Ruby and Haskell for 5 years or so, a couple of years later I learned Erlang as well. Besides, I also wrote some thousand of lines in Prolog for my BSc and my MSc dissertation was on logic programming (specifically on building an efficient "interpreter" for a given logic programming language not too dissimilar from Prolog itself). I started studying Clojure basically when the word of mouth started spreading, which makes it about 2 good years. So what's it for me? For example, I am sorry to say that I found the part on Prolog rather unsatisfactory, but hey... I don't think that the goal of the book is to teach me Prolog. However, it gave me a pleasant introduction to Io (which I would have probably never learned) and convinced my that my first impressions on Scala were wrong and perhaps I have to give it a second chance.

So definitively worth reading. And here a free chapter!



Tuesday, August 10, 2010

Mastermind/Bulls and Cows Breaker

Introduction

Mastermind is a classical code breaking game. And I bet almost everyone has played it at least one time (at least, any geek[0]). I won't explain the rules here. If you really don't know the rules, here there is an explanation. Besides, we are playing Bulls and Cows, which is very similar, but has no copyright.

I sincerely find the game rather boring, though I find writing programs solving boring games quite entertaining. This is a very old prolog program I wrote to break the code. The algorithm is extremely trivial, and is more an example in how easy to solve are this class of problems in Prolog with native backtracking than anything else.

Wikipedia page also list some scientific papers on Mastermind/Bulls and Cows code breaking algorithms. I find them really interesting, indeed. But this is about Prolog and backtracking, indeed.

The code


:- use_module(library(lists)).
:- dynamic query/3.

mastermind(Code) :-
 cleanup, guess(Code), check(Code), announce.

guess(Code) :-
 Code = [_X1, _X2, _X3, _X4],
 selects(Code, [1,2,3,4,5,6,7,8,9]).

check(Guess) :-
 \+ inconsistent(Guess),
 ask(Guess).

inconsistent(Guess) :-
 query(OldGuess, Bulls, Cows),
 \+ bulls_and_cows_match(OldGuess, Guess, Bulls, Cows).

bulls_and_cows_match(OldGuess, Guess, Bulls, Cows):-
 exact_matches(OldGuess, Guess, N1),
 N1 =:= Bulls,
 common_members(OldGuess, Guess, N2),
 Cows =:= N2-Bulls.

ask(Guess) :-
 repeat,
 format('How many bulls and cows in ~p?~n', [Guess]),
 read((Bulls, Cows)),
 sensible(Bulls, Cows), !,
 assert(query(Guess, Bulls, Cows)),
 Bulls =:= 4.

sensible(Bulls, Cows) :-
 integer(Bulls),
 integer(Cows),
 Bulls + Cows =< 4.


%%  Helpers
exact_matches(Xs, Ys, N) :-
 exact_matches(Xs, Ys, 0, N).
exact_matches([X|Xs], [Y|Ys], K, N) :-
 (   X = Y ->
 K1 is K + 1
 ;   
 K1 is K),
 exact_matches(Xs, Ys, K1, N).
exact_matches([], [], N, N).

common_members(Xs, Ys, N) :-
 common_members(Xs, Ys, 0, N).
common_members([X|Xs], Ys, K, N) :-
 (   member(X, Ys) ->
 K1 is K+1
 ;   
 K1 is K),
 common_members(Xs, Ys, K1, N).
common_members([], _Ys, N, N).

cleanup :-
 retractall(query(_,_,_)), !.
cleanup.

announce :-
 size_of(X, query(X, _A, _B), N),
 format('Found the answer after ~d queries.~n', [N]).
%%  Utilities
selects([X|Xs], Ys) :-
 select(X, Ys, Ys1),
 selects(Xs, Ys1).
selects([], _Ys).

size_of(X, G, N) :-
 findall(X, G, Xs),
 length(Xs, N).

Notes

[0] Most of the type when you say "any" or "everyone" you are wrong. This applies to me as well.

Sunday, June 13, 2010

Prolog Homoiconicity

Although when talking about homoiconicity the first language that springs into mind is Lisp (or a Lisp dialect), Prolog is a completely homoiconic language. In Prolog everything is a term. Variables, atoms, numbers are terms. And everything else is a compound term: e.g., foo(a, b) is a term. Lists are the main data structure of Prolog, and a list is a term as well. In fact a Prolog list is i) the empty list []; ii) a term '.'(a, b), where a is a term and b is a list or a variable. We say that a compound term foo(x, y) has functor foo and arity 2. Clauses (the prolog equivalent of "functions") are represented as terms whose main functor is :-, their head is a prolog term and their body is a prolog term as well. Moreover, the read predicate reads from file (or standard input) some prolog predicates. E.g., this simple Prolog program reads a Prolog program into memory (and the program is represented as a list of prolog terms):
read_all([Predicate|T]) :-
	read(Predicate),
	Predicate \== end_of_file, !,
	read_all(T).
read_all([]).

read_file(File, L) :-
	see(File), read_all(L), seen.
If we call read_file to read "read_file.pl" (spaces added by me):
?- read_file('read_all.pl', L).
L = [(read_all([_A|_B]):-read(_A),_A\==end_of_file,!,read_all(_B)),
     read_all([]),
     (read_file(_C,_D):-see(_C),read_all(_D),seen)] ? y
The display predicate some prolog interpreters provide gives us more insight on internal representation of terms. We call it on just one rule in order not to clutter the output with list representations:
?- read_file('read_all.pl', [H|_]), display(H).
:-(read_all(.(_932,_952)),,(read(_932),,(\==(_932,end_of_file),,(!,read_all(_952)))))
And here on the full program (notice... lists are "consed" with the '.' functor):
?- read_file('read_all.pl', L), display(L).
.(:-(read_all(.(_900,_920)),,(read(_900),,(\==(_900,end_of_file),,(!,read_all(_920))))),.(read_all([]),.(:-(read_file(_1410,_1430),,(see(_1410),,(read_all(_1430),seen))),[])))
Indeed, infix operators have been placed in prefix form like every other predicate (indeed, they are not special, just a parsing trick). Another "useful" predicate (not standard) is portray_clause/1, in order to do some "pretty-printing":
?- read_file('read_all.pl', [H|_]), portray_clause(H).
read_all([A|B]) :-
        read(A),
        A\==end_of_file, !,
        read_all(B).

The assert family of predicates can “store” into the Prolog database facts (this is very common, in fact, memoization is often implemented this way) and rules (this is done less frequently – and there may be a slight efficiency penality [CHECK]). Thus we can manipulate terms (indeed that is what Prolog is about) and load them as programs. Notice that I showed the read predicate only to show that “standard” Prolog programs are represented that way, but it is not necessary at all to use programs saved in files.

4 ?- assert((foo(X) :- bar(X), \+ baz(X))).
true.

5 ?- assert(bar(1)), assert(bar(2)), assert(baz(2)).
true.

6 ?- foo(X).
X = 1 .

Monday, June 7, 2010

A matter of homoiconicity

Somewhere around the late sixties the term “homoiconicity” has been introduced in the computer science community. In fact, the term was older and dates to the 1960, at least according to Wikipedia. Strangely enough, I did not find traces of the term homoiconic or homoiconicity in McIllroy [1]. I was not unable to recover the full text of the paper on TRAC [2]. However, widespread use of the term is due to McKay (along for the very idea of object oriented programming and a good deal of criticism on Lisp – perhaps I will look for some opinions of famous lispers on Smalltalk to make things even) in his Ph.D. dissertation.
The basic idea is simple: program is data. That is to say, the program is represented in the very same format as data. Thus, a program can manipulate programs (and even itself) in the same ways it uses for data. This is great and is the foundation for great and funny meta-programming.
Indeed, references on TRAC should make clear that a structured representation of programs is not needed. TRAC manipulated strings and programs where represented as strings. On the other hand, Lisp manipulates lists and a program is a list. It is quite rare in a discussion on programming languages with a lisper that he does not use the term “homoiconic” somewhere in the discussion, usually before becoming condescending (a proof of this is being developed actively – no trolls have been harmed during the experimentations).
What I find extremely interesting is that this great property was simply obvious in theoretical models. Universal Turing Machines and primitive functions need an enumeration of programs (functions, respectively) in order to manipulate such objects. If such enumeration was not possible, most computer science would not have been created.

The von Neumann architecture also relies heavily on instructions being represented as numbers and stored along with data. It’s a bit of a pity that this properties have not been naturally ported to programming languages (except from Lisp and some others).
Indeed, we all agree that homoiconicity is a great property in programming languages. In fact, I often find frustrating complete lack of it (e.g., Java is completely not homoiconic, since its main data structure is the Object but Java programs are not even near to being objects – ok, you can always call the compiler get an AST… but this is not what the whole thing is about). Other languages have limited support for it.
In Python functions and classes (and objects) are available when compiling a module. The whole decorator concept, for example, relies on the fact that functions are objects and can be passed as such at compile time. Very powerful meta-programming techniques in Python rely on “program” manipulation, where programs are represented as classes and functions (and not syntactically, like in Lisp). This makes some kind of manipulation very hard (without resorting to byte-code manipulation – and then the same objection with the java compiler/AST thing applies – or to basic string manipulation, which I would not consider as a symptom of homoiconicity, since strings are not “the main data” in Python). However, I would not rule Python out of the bunch of homoiconic languages, either. Perhaps “partially homoiconic” applies? Or perhaps do we need better built in tools to manipulate classes and functions in Python to qualify for the full homoiconic(tm) certification from old lisp hackers[3]?

References

  1. McIlroy, M. D. 1960. Macro instruction extensions of compiler languages. Commun. ACM 3, 4 (Apr. 1960), 214-220. DOI
  2. MOOERS, C.N. TRAC, a text handling language. Proc. ACM 20th Nat. Conf. Cleveland, Aug. 1965, pp. 229-246.
  3. Not meant to be offensive… perhaps old could be replaced with ancient?

Sunday, December 28, 2008

Missionaries, Cannibals and Prolog

Apparently, many generations of computer scientists have been concerned with the problem of letting cannibals and missionaries crossing a river with the very same boat.

Before them, logicians were looking for a way to have married couples doing the very same thing with slightly different inherent problems.

Centuries ago, the problem was that no woman should be with another man without her husband being present (this is now called a threesome). However, since women now are emancipated and nobody cares if a woman is with another man (with the possible exception of her own husband, especially when there are no football matches on TV), the cannibal problem seems more interesting.

It is widely accepted that if missionaries outnumber cannibals, they convert the cannibals. Since we believe in freedom of cult, no cannibal shall be converted. Someone may be familiar with another version of this problem, since once having people converted was considering a good thing, while having missionaries eaten was bad.

However, recently cannibals became vegans and until Churches will develop soy-missionaries the problem is how to let cannibals retain their own religion.

Basically, this is the job for a Prolog interpreter. Though humans solved this problem long ago, a human can only solve the problem for small numbers of cannibals and missionary (and though we hope both populations remain small in number, cannibals can always mate and missionaries can convert new people).

This is a short Prolog program that given an initial state (all cannibals and all missionaries on the same side of the river) and the desired final state (all cannibals and missionaries on the other side of the river), computes the strategy our poor bootman has to pursue in order to carry over the whole lot of people.

initial_state(mc, mc(left, bank(3,3), bank(0,0))).
final_state(mc(right, bank(0,0), bank(3,3))).

Above we say that the initial state has the boat on the left, three cannibals and three missionaries are on the left bank and no one is on the other one. The final state encodes what we have already said.

Now we have to describe possible moves:

move(mc(left, L, _R), Boat) :-
    choose_passengers(L, Boat).

move(mc(right, _L, R), Boat) :-
    choose_passengers(R, Boat).

choose_passengers(bank(C, _M), boat(2, 0)) :- 
    C > 1.
choose_passengers(bank(_C, M), boat(0, 2)) :- 
    M > 1.
choose_passengers(bank(C, M), boat(1, 1))  :- 
    C > 0, M  > 0.
choose_passengers(bank(C, _M), boat(1, 0)) :- 
    C > 0.
choose_passengers(bank(_C, M), boat(0, 1)) :- 
    M > 0.

Basically, when the boat is on the left bank, we choose passengers from the left bank. When the boat is on the right bank, we choose passengers from the right bank. We can only take 2 people on the boat. That means that, provided there is enough people on the bank, we can only carry 2 cannibals, 2 missionaries, 1 cannibal, 1 missionary or 1 cannibal and 1 missionary. Since the boatman is usually agnostic, the cannibal aboard with one missionary is not going to be converted.

The Prolog system choses one of the possible moves and proceeds. If the move is wrong, it back-tracks and tries another solution. Until the number of choices is small, it is rather efficient.

The predicates that describe how the system changes when one move is performed are:

update(mc(B, L, R), Boat, mc(B1, L1, R1)) :-
    update_boat(B, B1),
    update_banks(Boat, B, L, R, L1, R1).

update_boat(left, right).
update_boat(right, left).

update_banks(alone, _B, L, R, L, R).
update_banks(boat(C, M), left, 
             bank(LC, LM), bank(RC, RM), 
             bank(LC1, LM1), bank(RC1, RM1)) :-
    LM1 is LM - M,
    RM1 is RM + M,
    LC1 is LC - C,
    RC1 is RC + C.

update_banks(boat(C, M), right,
             bank(LC, LM), bank(RC, RM), 
             bank(LC1, LM1), bank(RC1, RM1)) :-
    LM1 is LM + M,
    RM1 is RM - M,
    LC1 is LC + C,
    RC1 is RC - C.

Eventually, we have to tell legal and illegal configurations apart. Basically, if in the bank we leave there are more missionaries than cannibals, we are breaking the law. If not, everything is fine (since the boatman works for free, so he does not have to pay taxes).

legal(mc(left, _L, R)) :-
    \+ illegal(R).

legal(mc(right, L, _R)) :-
    \+ illegal(L).

illegal(bank(C, M)) :-
    M > C.

The solution algorithm is trivial:

solve_dfs(State, _History, []) :-
    final_state(State).

solve_dfs(State, History, [Move|Moves]) :-
    move(State, Move),
    update(State, Move, State1),
    legal(State1),
    \+ member(State1, History),

solve_dfs(State1, [State1|History], Moves).

print_moves([]).
print_moves([M|Ms]) :-
    writeln(M),
    print_moves(Ms).

go :-
    initial_state(mc, State),
    solve_dfs(State, [State], Moves),
    print_moves(Moves).

We generate new moves and check if the updated state would be legal and if we did not get in the same state before (if it happens we are looping, the boatman becomes hungry and eats whoever is on his boat -- that’s the main reason why he does not travel with the empty boat).

The full source is here:

Sunday, November 9, 2008

From foldl to difference lists

Most declarative languages have lists. Usually this lists are either a nil atom (empty list) or a cons (a couple whose second element is a list). It is easy to see how we can append things to the head of the list.

In my former article we explored lists and foldl. Indeed, it is clear that we need an accumulator because appending at the end of the list is basically not possible (with decent performance). Appending at the end of the list means copying the whole list. Why? Consider how lists are done.

You have the head (say X1) and a pointer to another list. And this other list is built in the very same way. The last element has a pointer to an empty list.



lists-2008-11-9-02-34.png




If we want to add something at the end (even though we had a magical last::[E] --> E
function which got to the last element of a list in O(1) time) we need to "modify" the last element in the process. Since modifying is not allowed, we need to create another last element whose car (first element) is 3 (following our example) and whose second element (cdr) is a cons(4, []). Of course, at this point we should modify another element in the list. Basically we need to create a copy of the list. That's O(N).

However, if we bound the cdr of the last element not to an empty list but to an "unbound variable" we could simply add at the end cons(4, Unbound2), binding the Unbound1 to cons(3, Unbound1). Amazing... there are two major drawbacks.

First, we need logical variables. Which is simply true if we are using Prolog or Oz (sorry, no Erlang this time). In Erlang/Haskell you just can't do almost anything with unbound variables. In Prolog you can.

The second problem is our magical last function. We can build such a function, though it runs O(N). We can't do better. That means that appending at the end of a list, even though we left the tail of the list unbound, costs a lot. Well, usually O(N) is good, the point is that this operation is very low level and if we got a lot of O(N) stuff in inner "loops" the algorithm will suck.

Now, let us get back to the reverse with accumulator.

standard_reverse(Xs, Ys) :- standard_reverse(Xs, [], Ys).
standard_reverse([X|Xs], Acc, Ys) :-
    standard_reverse(Xs, [X| Acc], Ys).
standard_reverse([], Ys, Ys).

Let us change the argument order:

standard_reverse2([X|Xs], Ys, Acc) :-
    standard_reverse2(Xs, Ys, [X| Acc]).
standard_reverse2([], Ys, Ys).

Ok, this change seems unimportant to say the least. Let us do a further modification: instead of using two arguments for the reverted list and for the accumulator, let us use one single argument which includes both. For example Reverted-Acc.

diff_reverse([X|Xs], Ys-Acc) :-
    diff_reverse(Xs, Ys-[X|Acc]).
diff_reverse([], Ys-Ys).

Extremely nice! Here we have difference lists. Difference lists are a subject difficult to master. Their declarative meaning is quite obvious (and I will explain it in the next paragraphs), however their procedural/operational meaning is quite messy.

Basically a difference list is a way to represent an ordinary list. For example the list [1, 2, 3] can be represented by the difference lists [1, 2, 3, a]-[a] or by the difference list [1, 2, 3, a, 4]-[a, 4] or by [1, 2, 3]-[] or in another thousand of different ways, the most interesting of which is [1, 2, 3|X]-X. Notice that the latter unifies which any of the others. The careful reader may have noticed that now we may have a way to get to the last element of the list in order to add stuff in O(1). But let's not jump to conclusions.

Any ordinary list L can be represented as L-[], so it's easy to build a difference list representing an ordinary list. Unfortunately this difference lists is fully specified (if L was).

A classical problem in Prolog is appending two lists. This should be an O(M) operation, where M is the length of the shortest list. In fact in Prolog it is O(N) where N is the length of the first list:

myappend([], Xs, Xs).
myappend([H|T], Xs, [H|Ys]) :-
    myappend(T, Xs, Ys).

Now, let's benchmark the thing:

?- time(myappend([1, 2, 3], [2, 5], L)).
% 4 inferences, 0.00 CPU in 0.00 seconds (0% CPU, Infinite Lips)
L = [1, 2, 3, 2, 5].

?- time(myappend([1, 2, 3, a, b, c], [2, 5], L)).
% 7 inferences, 0.00 CPU in 0.00 seconds (0% CPU, Infinite Lips)
L = [1, 2, 3, a, b, c, 2, 5].

However, we can do that in O(1) using difference lists:

append_dl(Xs-Ys, Ys-Zs, Xs-Zs).
?- time(append_dl([1, 2, 3, a, b, c|Xs]-Xs, [2, 5]-[], Z)).
% 1 inferences, 0.00 CPU in 0.00 seconds (0% CPU, Infinite Lips)
Xs = [2, 5],
Z = [1, 2, 3, a, b, c, 2, 5]-[].

When applicable, this is very nice. There is not much more on difference lists, still having the ability to add elements at the end of a structure is great. How? Think about it:

push_back(X, L-[X|Xs]).
?- X = [1, 2|Xs]-Xs, time(push_back(3, X)).
% 1 inferences, 0.00 CPU in 0.00 seconds (0% CPU, Infinite Lips)
X = [1, 2, 3|_G305]-[3|_G305],
Xs = [3|_G305].

Now we know how to put new elements to the front or to the back of a difference lists. That means we can build a deque.

Saturday, November 8, 2008

Accumulators and Foldl in declarative languages

Using accumulators in declarative programming is common. Basically it is possible to convert not tail recursive functions into tail recursive ones using an accumulator.
Consider a very simple procedure: sum numbers in a list. The naive implementation is:

naive_sum([]) -> 0;
naive_sum([H|T]) ->
    H + naive_sum(T).

This one is not tail recursive. Every time the second clause matches, naive_sum is called and the return value of the recursive call is needed in order to compute the result. The standard way to write this one is:

standard_sum(L) ->
    standard_sum(L, 0).
standard_sum([], Acc) -> Acc;
standard_sum([H|T], Acc) ->
    standard_sum(T, Acc+H).

Notice that this way the last operation of each recursive calling clause is the recursive clause. This means that the new call can take the place of the old one. Simple. It’s so simple (and the pattern is so common), that it has a name foldl.

.
sum(L) ->
    lists:foldl(fun(X, Y) -> X + Y end, 0, L).

Basically, it's one of the primitives of functional languages. For example, in Haskell, it would be:

mysum lst = foldl (+) 0 lst

However, an Haskell programmer would write. This trasformation is known as eta-reduction.

mysum = foldl (+) 0

In Prolog, the naive and the "standard"sum becomes:

naive_sum([], 0).
naive_sum([H|T], Res) :-
    naive_sum(T, Res1),
    Res is Res1 + H.
standard_sum(L, Res) :-
    standard_sum(L, 0, Res).
standard_sum([], Acc, Acc).
standard_sum([H|T], Acc, Res) :-
    Acc1 is H * Acc,
standard_sum(T, Acc1, Res).

Since Prolog clauses can't return a value (because they are not functions, of course!), we need an extra argument which unifies with the result. Besides, in the Prolog version it is quite explicit that naive_sum can't be tail recursive. The difference between the naive and the tail recursive version is that the first one uses linear (O(N)) space and the latter uses constant (O(1)) space. Both are O(n) in terms of running time.
Another typical example on accumulators use is the "revert list" in Prolog. This is the naive version:

naive_revert([], []).
naive_revert([H|T], L1) :-
    naive_revert(T, T1),
append(T1, [H], L1). 

This is both not tail recursive and has a worse than expected running time: indeed it is O(N^2) while it should be O(N).
The first solution is using the built-in reverse predicate. It is even the right thing to do in production, but it is not useful to learn. :)
Let’s examine the following code:

standard_reverse(Xs, Ys) :- standard_reverse(Xs, [], Ys).
standard_reverse([X|Xs], Acc, Ys) :-
    standard_reverse(Xs, [X| Acc], Ys).
standard_reverse([], Ys, Ys).

Once again, we got tail recursion (using an accumulator). Basically we read the first element in the first list and we push it in the accumulator. When we take another element this one is pushed again (and the former is after this one). Then, when we finish the list, we unify the accumulator with the last value. This version is O(n) in time and space.
The Erlang version is extremely similar (and slightly more readable):

standard_revert(L) ->
    standard_revert(L, []).
standard_revert([X|Xs], Acc) ->
    standard_revert(Xs, [X|Acc]);
standard_revert([], Acc) -> Acc.

We can explore this definition further:

rev(_Op, [], Acc) -> Acc;
rev(Op, [H|T], Acc) ->
    rev(Op, T, Op(Acc, H)).
strange_revert(L) ->
    rev(fun(A, B) -> [B|A] end, L, []).

Here, instead of using the [X|Xs] constructor explicitly, we moved it in anonymous function. Notice that we also reverted the parameters. That is to say we don’t have a cons function:

Cons = fun(H, T) -> [H|T] end

but a :

Flipped_Cons = fun(T, H) -> [H|T] end

This is not unexpected. Let’s consider the definition of the foldl function itself:

foldl(_F, Acc, []) ->
    Acc;
foldl(F, Acc, [H|T]) ->
foldl(F, F(Acc, H), T).

They look very similar. Indeed they have almost the very same shape. We just have to flip a couple of parameters (foldl has the accumulator as the second parameter, not as the third one). We must notice that the foldl above is not akin Erlang foldl. In Haskell the type of foldl is:

Hugs> :type foldl
foldl :: (a -> b -> a) -> a -> [b] -> a

The binary operation has the accumulator as the first parameter (here it has type a), which is the same convention we adopted for the handmade foldl written above (from now on our:foldl). Erlang standard foldl follows another convention, the binary function has the accumulator as the second parameter.

foldl(Fun, Acc0, List) -> Acc1
Types  Fun = fun(Elem, AccIn) -> AccOut
Elem = term()
Acc0 = Acc1 = AccIn = AccOut = term()
List = [term()]

If we want to use foldl to build the reverse function we must chose which foldl to use. our:foldl needs the Flip (Flip_Cons) operation, while the standard Erlang foldl is fine with a Cons operation.

foldl_revert(L) ->
    lists:foldl(fun(H,T) -> [H|T] end, [], L).
foldl_revert2(L) ->
    our:foldl(fun(T,H) -> [H|T] end, [], L).

And here we are foldling in Prolog:

foldl(Functor, Acc, [H|T], Foldled) :-
call(Functor, Acc, H, Acc1),
foldl(Functor, Acc1, T, Foldled).
foldl(_Functor, Acc, [], Acc).
cons(T, H, [H|T]) :- !.

Thursday, November 6, 2008

Extol of Declarativeness

Far too many programmers underestimate the beauty of declarative programming. This is something which cames from the industry: functional languages are traditionally under-employed and perceived as academic choices. In fact a lot of research has been done with Lisp and Prolog (mostly in U.S.A. and Europe respectively) in AI and formal languages fields.

However, declarative languages are very suited for the industry and to solve practical problems. Indeed, one of the most used domain specific languages (SQL) is a declarative language (even though not a turing complete one). Nobody thinks that SQL is academic. It’s just a good language for the task (and the underlying mathematical model).

Indeed, Prolog would be suited for the task as well (and Datalog is a proof), was it not turing complete (indeed, termination is a very good property to have in a query and data manipulation language.

Programming without explicit state is a nice property. A typical example is transactional semantics: in imperative languages implementing transactional behaviour is quite a hard task. Every operation must be logged (or check-pointed or both). Another option is some kind of shadow copy behaviour. Basically you access data through a pointer, which points to some kind of structure. If you want to edit the structure, you make a copy of it, edit the copy and set the pointer to point the copy if and only if every operation in the transaction has success.

 

transac_semantics-226x300-2008-11-6-13-10.png

 

 

All these operations are costly. Basically this kind of behaviour is free in functional languages. You return from a function and if you had no explicit side effects (which is not possible in haskell and easily avoidable inpr other functional languages) you automatically roll back to the state before the function call. This behaviour has some runtime cost, and this is the reason why functional languages are usually quite fast and not extremely fast (especially in certain tasks). However, its both easy to code (in fact you do not have to do anything explicitly) and rather optimized, since it’s done by the system (which should be rather efficient).

On the other hand, some other operations are quite more costly in functional environments. This happens when you often modify structures: that is to say when the algorithm uses on destructive write operations. Sometimes, declarative languages have limited support for “classical” variables: think about Oz cells, Lisp or OCaml. Erlang has ets, dets and mnesia tables. However, these algorithms are usually rather tricky to parallelize. In the most lucky environment some data decomposition comes natural, unfortunately this is not always the case. On the other hand, in declarative environments parallelism is almost free.

In Erlang parallelism is build into the very language core. In Haskell writing concurrent software is extremely easy (or at least not any more difficult than writing non-parallel software). Oz, with its dataflow variables, is also an interesting choice (and presents a concurrency model extremely educative and different from the ugly thread/synchronized Java vision). Basically in Oz variables themselves can be used to let threads communicate.

In many declarative languages variables can be bound or unbound. Unbound variables shall be bound before being used. That is to say that reading an unbound variable is a programming error (however, this does not go unnoticed and is quite different from reading an uninitialized variable/pointer). In Oz, if you have many threads and a thread tries to read an unbound variable, it hangs until someone binds that variable.

This is a nice way to do concurrency: variables are used. They can be used to build barriers, for example. They are a good IPC primitive as well. Since in Oz variables are logical variables (read-only, once bound), there is no need to worry about multiple writes. Nice.

Friday, October 17, 2008

Python considered harmful?

Yesterday morning I started writing a simple tool which should compile all C++ files in a given directory tree. Ok, simple enough, it’s just a matter of a simple os.walk and some subprocess.call.

After that, I thought that it would be nice if the tool did something more. For example, I could write down a function that was able to “make clean” the executables I built. This is simple as well. You just have to be smart since on *nix executables do not have extensions. And I could make the system Windows friendly. It was a matter of a couple of functions more.

However, at that point I needed some kind of interface. A simple command line tool was nice. But... well, I remembered using a Prolog tool which did some heuristic to get wrong commands right (zsh does this as well)... so, why not? A levenshtein distance + some more checks could do. And it did. Nice.

But... what if I just want to compile one single file? Well, I had already most of the infrastructure. And why not letting me specify a single file to clean? As easy as breathing. Done. By the way, the system at that point supported building and cleaning object files as well.

And I was already wondering that I left out C files. After all I needed to process C++ files, but the tool would be surely more useful if I could use it with C files too. And why not Pascal? Ok, I have the right design which could support kind of configuration to map file types to compilers... and I could parametrize compiler options as well. Somewhere in my mind an automated compiler detection system was already lurking.

I refactored the tool. It became a package, with a lot of useful functions... But well... why rebuilding all the sources? I need only to rebuild executables which are older than their source file. This is easy, a couple of stat...

At that point I realized that if only I added some kind of file dependency discovery I would have basically reinvented make. That is to say I was reinventing a square wheel. One of these days I’ll get the very first script and modify so that instead of compiling files generates a CMakeLists.txt, calls cmake and then Make.

The end of the story is developing in Python is too fast. :P

BTW: the author is not suggesting Python should not be used, just the opposite!

Sunday, July 29, 2007

About autotools, YellowDog, Prolog and Xpm

A few days ago, I switched to Yellow Dog for my powerbook.

I'm very satisfied with the hardware support. It's the first distro that supported my backlit keyboard from the beginning. However, it lacks a lot of software packages. Most of them can be taken from Fedora PPC (it's advised to use those of Fedora Core 5 or). However, other must be compiled.

For example, I had to compile swi-prolog, since the software I'm working on at the moment is written in Prolog. Swi Prolog itself was easy to install. I compiled it and I had it working in a few minutes. However, I was unable to compile XPCE. While I do not use the library itself, I find it useful sometimes to use gtrace or the 'graphic' help.

The problem reported was that Xpm was not found. However, I installed both libxpm and its development files. The ./configure script reported that it was not able to link against libXpm.

I first tried manually linking and it worked, so I had a look at the log. In fact it wasn't libxpm fault. The test program tried to link both libxpm and libxt, and the latter failed. Amazingly, the configure script did not check for errors in linking libxt, so the erroro reported was totally misleading.

The problem was that there was no libXt.so, but only libXt.so.6. I had to manually create the symbolic link.

Eventually, I bumped into another error (this time during 'make'). The culprit was once again Xt: it seemed that some of its 'standard' header files were missing. I found a fedora 5 devel package that fixed that and I was able to compile xpce.

Quite unfortunately, I can't use it now. Another bug in the installation of xorg conjures against me. This bug can be seen using a Tkinter canvas too. The problem is that 'names' of colors are not found (probably because rgb.txt is in the wrong place). Xpce fails and so does many stuff with Tkinter. I don't know where rgb.txt should be. I tried to put it in standard places, but up to now the server does not find it. The problem is that I have to restart the server each time (and since sometimes this hangs the whole system, I have to reboot, and Red Hat derived distros have slow boots.)

Wednesday, March 21, 2007

Scripting languages

What is a scripting language?

From Wikipedia we read that:

Scripting languages (commonly called scripting programming languages or script languages) are computer programming languages that are typically interpreted and can be typed directly from a keyboard. Thus, scripts are often distinguished from programs, because programs are converted permanently into binary executable files (i.e., zeros and ones) before they are run. Scripts remain in their original form and are interpreted command-by-command each time they are run. Scripts were created to shorten the traditional edit-compile-link-run process.

Of course any self respecting computer scientist knows that definition is meaningless, because a language is a mathematical constructand is not tied to its implementation. You can write a C interpreter, for example (and there are some). You can also write a compiler for any 'interpreted language' (of course the term 'interpreted language is also meaningless). And there are theoretical constructs (Futamura projections) that given any interpreter are able to build a compiler from it (you could implement them even in practice, though it would be terribly inefficient).

However, any other definition would be misleading as well: if you define a scripting language as a language you can use to "script" (as in "bash script"), then you've got definition that's even more stupid. bash is a scripting language, and so it is Python. However, if someone writes an application that is scripted in C, C becomes a 'scripting language').

Both definitions have an additional problem: scripting languages are considered somewhat inferior to 'non-scripting languages'. Some even use the term 'true programming languages' (and this 'true programming languages' are usually considered to be C and C++, for example).

Lisp, Haskell, OCaml, Prolog

In this dissertation on 'scripting languages', Lisp is quite emblematic. Lisp can be compiled (there are some Lisp compilers around). But Lisp can also be interpreted. This is one of the most widespread Lisp environments (and it is an interpreter). Moreover, in Emacs you can just run some Lisp snippets. And Emacs itself is scripted in Lisp (thus Lisp would be a scripting language according to the second definition). However, no one can assert that it is not a 'true' programming language. Emacs itself is a beautiful (and complex) piece of software, and a lot of 'real' (and extremely difficult) stuff in the AI field has been written in Lisp.

Haskell can be another example. Hugs is an interpreter, ghc is both an interpreter and a compiler. And Haskell is even statically typed (so you see, being dynamic is not necessary ). The very same applies for OCaml or Prolog. And all thislanguages have been used by the most theory-oriented computer scientist toimplement real programs.

I suppose nobody would say that Lisp or Haskell are 'scripting languages' (even if according to the definitions they are -- Haskell is used in many configuration tools by Linspire programmers).

Bash, Perl, Python, Ruby.

All this languages have been labeled as 'scripting languages'. And in fact according to definitions, they are. However, they are very different languages.

Bash is a very simple language designed to do simple things. Unix system administrators used it intensively (along with awk and sed and other utilities) to automate many repetitive tasks. Although this is surely 'programming', in some sense it's different from programming an application from scratch. Some even consider it a 'lower level' kind of programming (and surely we must recognize that skills involved in writing a boot script are different from skills used to write a compiler)

Perl was born in this environment: its goal was textual manipulations and interactions between processes in a system. It is a nicer alternative to bash + awk in many situations. Later it has been added a fully fledged object model, but in the while it has been used to write very complex applications that go outside the 'lower level programming domain' that is usually associated with 'scripting'. Even though I don't like Perl anymore, it is a general purpose language, with the same dignity of C or C++.

The 'scripting language' monicker is not fitting anymore. And it is even less fitting for Python. Python has been an object oriented programming language from the beginning. Good software engineering practices have always been employed. However, many consider it just another scripting language. And of course Python makes a great scripting language.

The domains in which Python excels are the same in which Java is used, for example. However, Python can be used for scripting, too. That is to say... some (and I'd be tempted to say 'most' ) 'scripting languages' can be used both as general purpose languages and automation/scripting.

The reason why they can be used for automation/scripting and that they are easier to use and we can write programs faster. The development cycle is faster, too. And to me, they all seem good properties.

I think that the very same features that make Python (or Ruby) great for scripting, make it even greater for general purpose programming. That is to say we should consider a 'scripting language' not a poorer language, but a better one (yeah, of course this is exaggerated, since there are languages suited for scripting that are not suited for general purpose programming -- AppleScript and bash, for example).

Sunday, December 24, 2006

Citation: Concurrency in Java (Van Roy and Haridi)

Concurrency in Java is complex to use and expensive in computational resources. Because of these difficulties, Java-taught programmers conclude that concurrency is a fundamentally complex and expensive concept. Program specifications are designed around the difficulties, often in a contorted way. Biut these difficulties are not fundamental at all. There are forms of concurrency that are quite useful and yet as easy to program as sequential programs (e.g., stream programming as exemplified by Unix pipes). Furthermore, it is possible to implement threads, the basic unit of concurrency, almost as cheaply as procedure calls. If the programmer were taught about concurrency in the correct way, then he or she would be able to specify for and program in system without concurrency restrictions (including improved versions of Java). (Concepts, Techniques and Models of Computer Programming, Peter Van Roy and Seif Haridi)

First of all, I completely agree with the authors. I like learning new languages/paradigms/models and find quite restricting sticking to just One Language, One Platform, One Model. I'm fond of Python (but indeed Python is an excellent imperative/object oriented language that allows functional programming -- and logic programming is experimented among other things in pypy). I also like Ruby (again Imperative with functional capabilities). I recently discovered the power of Prolog, and I love Haskell (although I never wrote any substantial piece of software in Haskell).

I can't imagine working with Java and only with Java (and the same applies to 'C++ and only C++' or whatever). However, there is plenty of programmers out there that chose One Language, One Platform, One Model and stick with it. I'm wondering if we can generalize Dijkstra statement

The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offence.

In fact it seems that Java has the very same problem. A lot of Java programmers are only able to formulate problems (and solve them) thinking in Java. One may argue that maybe business programmers already had their mind crippled, but I don't think this is the case.

I'm afraid to say the problem is not limited to Java, of course (it's just more evident because there a lot of Java programmers). There are a lot of excellent C++ (for example) programmers that simply do not believe that large programs written in dynamically typed languages can work (and of course they are wrong, since we can exhibit such programs). On the other hand some Python/Ruby/etc advocates think that static typing is fundamentally bad. In fact it is not: Java type-system is poorly designed. It's not that static typing is wrong or clumsy. Haskell and OCaml are statically typed, but types do not get in the way: they are a concrete help for the programmer (but this does not imply that a language should be statically typed).

My opinion is that more programming languages, models, paradigms should be taught to programmers. For two main reasons: first, they know more ways to solve a problem and hopefully they will be able to choose the best one; second, they will be able to adapt concepts coming from other models/paradigms/languages to the language they are currently using, thus making the design more elegant.

Elegance is not a dispensable luxury but a quality that decides between success and failure. (Dijkstra)

Tuesday, December 19, 2006

A Prolog XML printer in less than 100 lines.

Suppose we have an XML parser that converts an XML file into a Prolog term.
We may (quite) easily build one with DCG. I will be doing one soon, for values of 'soon' that depend on the status of my thesis.
We describe the way the prolog term is built:
Text is simply a prolog term. In a less naive implementation we may want to use the string datatype. However, to show the power of a declarative approach, things would not change a lot. Moreover, if we are using swi-prolog (a widespread open source implementation) internally itis able to use UCS (that is a superset of 16 bit Unicode). We only have to make the parser smart enough to convert entities in the right Unicode character and the whole thing will behave correctly
Attributes are represented as key:value terms, where : is a binary infix functor. Again, this is quite a natural representation.
A tag is represented like an tag(Attributes, Children) term, where term is the name of the tag, andl Attributes and Children are (possibly empty) lists of attributes and 'children'. This representation is not very clever. In fact you can have more optimized programs representing tags like element(tag, Attributes, Children). That is the way things are done in SWI-Prolog official SGML/XML parser library, but this is a simple example.
A 'child' is either a tag or a text section.
This (informal) description can be translated in a straightforward manner in a prolog program. The last thing before presenting the source is an example term representing a very simple XHTML file.

html([], [head([], ['']), body([], [p([], ['ciao ciao', img([src:'ciao.jpg'], [])]) ])])

And now the (81 lines long) prolog source code. You can call it with pp_tag(Term), where Term is the term representing the XML file.

pp_tag(Tag) :-
        !,
        pp_tag(Tag, 0).
pp_tag(Tag, N) :-
        Tag =.. [Tag_Name, Attributes, []],
        !,
        s(N),
        write('<'),
        write(Tag_Name),
        pp_attributes(Attributes),
        write(' />').
pp_tag(Tag, N) :-
        Tag =.. [Tag_Name, Attributes, Children],
        !,
        s(N),
        write('<'),
        write(Tag_Name),
        pp_attributes(Attributes),
        write('>'),
        N1 is N+1,
        pp_children(Children, N1),
        nl,
        tab(N),
        write('
        write(Tag_Name),
        write('>').
pp_text(Text, N) :-
        name(Text, Chars),
        pp_lines(Chars, N).
pp_line(Line, N) :-
        name(Atom_Line, Line),
        s(N),
        write(Atom_Line).
pp_lines(Lines, N) :-
        ((append(Line, [10|Rest], Lines), !),
            pp_line(Line, N),
            pp_lines(Rest, N)
        ;
            Line = Lines,
            pp_line(Line, N)
        ).
pp_children([], _N) :-
        !.
pp_children([X|Xs], N) :-
        !,
        pp_child(X, N),
        pp_children(Xs, N).
pp_child(Child, N) :-
        pp_tag(Child, N)
        ;
        pp_text(Child, N).
pp_attribute(Name:Value) :-
        !,
        write(Name),
        write('='),
        pp_quoted(Value).
pp_attributes([]) :-
        !.
pp_attributes([X|Xs]) :-
        write(' '),
        pp_attribute(X),
        pp_attributes(Xs).
pp_quoted(Term) :-
        !,
        write('"'),
        write(Term),
        write('"').
s(0) :-
        !.
s(N) :-
        nl,
        tab(N).

Of course this is a 'toy' implementation. You find the real thing here.

Wednesday, November 15, 2006

Difference Lists for visiting trees (Prolog)

This is are some (simple) exercises in Prolog using difference lists.They should be correct, but I have to say I'm a beginner Prolog programmer... so...

inorder_dl(tree(X, L, R), Xs-Ys) :- 
        inorder_dl(L, Xs-[X|Zs]), 
        inorder_dl(R, Zs-Ys). 
inorder_dl(void, Xs-Xs). 
preorder_dl(tree(X, L, R), [X|Xs]-Ys) :- 
        preorder_dl(L, Xs-Zs), 
        preorder_dl(R, Zs-Ys). 
preorder_dl(void, Xs-Xs). 
postorder_dl(tree(X, L, R), Xs-Zs) :- 
        postorder_dl(R, Ys-[X|Zs]), 
        postorder_dl(L, Xs-Ys). 
postorder_dl(void, Xs-Xs). 

Sunday, November 12, 2006

Difference lists

Well.... I just discovered this new wonderful world. I really like them a lot.

I suspect I'm getting a little bit more into Prolog programming. However, I still find more exciting Haskell lazy programming. But I have to admit that my scarce love for Prolog it's more a problem of (my) ignorance.

A couple of days ago my Programming Language teacher built under my eyes a small compiler and and interpreter for arithmetic expressions in less than 10 minutes (while explaining Prolog to my fellow students). In fact he used extensively Prolog unification mechanism (since the arithmetic expressions he was compiling were also Prolog terms).

However, thanks to DCG grammars building a parser is also quite easy.

In fact I'm falling in love with declarative programming. I definitely start liking declarative languages like Haskell (functional) or Prolog (logic) a lot more than classical programming languages.

In fact I still love Python and Ruby. I like C (and  we *need* C). I also like compiled languages like ObjectiveC (waiting for Objective C 2.0...) and D. After all, I like C++ too. But what is the point of Java? I definitely don't understand. It's dramatically uninteresting.

Friday, April 21, 2006

Something about Prolog...

As I had to admit in my last Prolog post, I'm no Prolog guru. I'd rather deserve the term newbie. However, I followed some math logic courses that make it all less harder. If you find something incorrect or if you don't understand something, comment it.

The first thing you have to keep in mind is that you don't really tell Prolog how to do something. You rather ask "him" (or her?) to prove something following the rules you gave.

Basic datatypes

In Prolog we have "Strings". We can also use terms like string or dog. However if they have capital letter, they are
variables, not simple terms.
We have list that is to say [], [a, b, c]. If we write
[H | T] H is the first element of the list, T a list containing all the remaining elements. If the list is [a, b, c], H is a and T is [b, c]

Computation model

Relations

Suppose you are working with a graph. Now suppose that you want to query the graph. As an example I report here a graph about elven genealogy in the LOTR world.The schema comes from this site. If it is wrong, tell them, not me :)

For example there are different interpretation regarding Indis. Some say she's Ingwë's sister, some say she's the daughter of Ingwë's sister. We chose to say she's Ingwë's sister since I do not want to introduce a character with no name in our database :).

The House of Finarfin:

                + - - - - - +
                :         Ingwë
             (sister)  + - - - - - +
                |      |           |
      Finwë = Indis   Olwë       Elwë = Melian
            |           |             |
          Finarfin = Ëarwen          Luthien
                   |
  +----------------+----------------+---------+
  |                |                |         |
  |                |              Aegnor      |
Finrod = Amarië Angrod = Edhellos    Galadriel = Celeborn
         :                |                         |
  (descendants        Orodreth          Elrond = Celebrian
     in Aman?)            |                    |
                  +-------+------+      +------+---------+
                  |              |      |      |         |
               Gil-galad    Finduilas Elladan Elrohir  Arwen

How would we represent this in an imperative language? We probably would have some kind of

class Elf 
- gender   (M/F) 
- name     (String) 
- children (Elven) 
- partner  (Elf) 
end 

We could also have a kind of "double linked structure". This is probably wise since otherwise querying someone's father would be dramatically expensive.

class Elf 
- gender   (M/F) 
- name     (String) 
- children (Elven) 
- partner  (Elf) 
- mother   (Elf) 
- father   (Elf) 
end 

In prolog we do not have such structure. We just tell "facts". A fact is a predicate that is always true. You can see how we define facts above. For example we are saying that aegnor is male.

male(aegnor). 
male(angrod). 
... 

You can imagine this like

Elf Aegnor;Aegnor.name = "Aegnor";Aegnor.gender = M;... 

Note that in Prolog everything is a Term, both data and program. Above I'm saying that aegnor is male. I assert that male(aegnor) is true.
This is the complete list of facts to define the above graph. If I were smarter I chose a smaller graph. However the list is long, but the imperative code to put into memory would not have been significantly shorter (yes, we could read it from file... where we would have had a list similar to the prolog version, :) )

female(amarie). 
female(arwen). 
female(celebrian). 
female(earwen). 
female(edhellos). 
female(finduilas). 
female(galadriel). 
female(indis). 
female(luthien). 
female(melian). 
male(aegnor). 
male(angrod). 
male(celeborn). 
male(elladan). 
male(elrohir). 
male(elrond). 
male(elwe). 
male(finarfin). 
male(finrod). 
male(finwe). 
male(gilgalad). 
male(ingwe). 
male(olwe). 
male(orodreth). 
parent(ingwe, olwe). 
parent(ingwe, elwe). 
parent(indis, finarfin). 
parent(finwe, finarfin). 
parent(olwe, earwen). 
parent(elwe, luthien). 
parent(melian, luthien). 
parent(finarfin, finrod). 
parent(earwen,   finrod). 
parent(finarfin, angrod). 
parent(earwen,   angrod). 
parent(finarfin, aegnor). 
parent(earwen,   aegnor). 
parent(finarfin, galadriel) . 
parent(earwen,   galadriel) . 
parent(angrod, orodreth). 
parent(edhellos, orodreth). 
parent(galadriel, celebrian). 
parent(celeborn, celebrian). 
parent(orodreth, gilgalad). 
parent(orodreth, finduilas). 
parent(elrond,    elladan). 
parent(celebrian, elladan). 
parent(elrond,    elrohir). 
parent(celebrian, elrohir). 
parent(elrond,    arwen). 
parent(celebrian, arwen). 

Rules

A rule is in the form:

goal(X) :- subgoal(X), subgoal2(X). 

In the above goal(X) is true if and only if both subgoal(X) and subgoal2(X) are true. We say we reducegoal to the two subgoals.
We will query something like:

goal(elrond). 

and it will succeed if and only if both subgoal(elrond) and subgoal2(elrond). Notice this times we have constants and not variables.
Now it is time to define some interesting relation. A father is a male parent. A mother a female parent. In Prolog we write:
father(X, Y) :- parent(X, Y), male(X). 
That is to say... X is father of Y if and only if X is parent of Y and X is male.
If we want to find Orodreth father, we query:
father(X, orodreth). 
If is succeeds (and it does) in X there is Orodreth's father: Angrod. The imperative version of this would have been... orodreth.father. If we were wise enought (or had enough space) to use a double linked structure. If we didn't, well... we would have to traverse the graph.
We can also do the converse.
father(finarfin, X). 
would return all the children of finarfin.

mother(X, Y) :- parent(X, Y), female(X). 
son(X, Y)    :- parent(Y, X), male(X). 
daughter(X, Y)  :- parent(Y, X), male(X). 

These are other trivial relationship. Now suppose you want to find out all the ancestors of a given character. If you had the simpler structure, it is a pain in the ass. If you have the complete one, it is boring. Basically you have to tell the system to traverse the graph following father and mother links. Simple and tedious.
In fact what you are doing is the "transitive closure" of the parent relationship. In Prolog it is exactly how define in natural language. X is ancestor of Y if X is parent of Y or if X is parent of Z and z is ancestor of Y.

ancestor(X, Y) :- parent(X, Y). 
ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y). 

All this relationships can be used to find the ancestors or the grandchildren. Or to verify if the relationship holds between two elves.
mother(galadriel, celebrian). returns yes, while mother(galadriel, arwen). returns no. You have to distinguish between defining facts and relationships and querying the system. This depends from your prolog interpreter: read a book, a tutorial, something. This is just a hint (and what I need to explain quicksort later on).
If you name this file elves.pl you can load it in swi-prolog with

[elves]. 

(remeber, no extention and end with a dot). Then you can just make queries. To define new predicates, put them in elves.pl and reload the file.

Some arithmetic...

We can consider the trivial algorithm to sum two numbers, provided we have an
INC and a DEC operation.
VAR A, B 
WHILE B > 0 
INC A 
DEC B 
END 

This is quite obvious. If you (like me) hate those pseudo code examples, this is pure C.

unsigned int sum(unsigned int a, unsigned int b){ 
    while(b) { 
        ++a; --b; 
    } 
    return a; 
} 
It is quite interesting an example in a functional programming. This resembles the basic definition of addition if you are working with primitive recursive functions (S is the successor function and we omitted to write explicitly the i-th projection function).

sum( x, 0   ) = x 
sum( x, y+1 ) = S(sum(x, y))

And this is Haskell (almost the same thing, a part from notations).

mysum :: Int -> Int -> Int 
mysum x 0 = x 
mysum x y = mysum x (y-1) + 1 

In Prolog once again you follow the formal definition of sum in Set Theory. (That by the way is quite the same as above).

add(X, 0, X).add(0, Y, Y).add(X, Y, X1) :- succ(Y1, Y), add(X, Y1, X2), succ(X2, X1). 

How this works? When I query something with add prolog will try to "match" against the arguments of the main goal. I don't want to be more precise about this "matching". It is called unification, and you are supposed to read a prolog tutorial to understand more about it.
add(5, 0, 5). will match the first rule: the second parameter is 0, and the other two are the very same term. Now let's consideradd(0, 5, X). . This could match the second rule. 0 matches 0. 5 matches Y. From this point for add to succeed, X must be 5 too. From swi-prolog:

2 ?- plus(0, 5, X). 
X = 5  
Yes 

However, we said that we expect to use Prolog predicates in many different ways. Not only to sum two numbers, but also to find if given three numbers the third is the sum of the first two. We expect also that trivial predicates can be inverted. That is to say given a number and the "sum" find the number that summed to the first one gives the sum.
That is to say we expect to have the subtraction defined by only defining the sum. In fact this ain't magic. Prolog builtin plus predicate does exaclty this.
This is a copy and paste from the swi prolog terminal:

31 ?- plus(X, 4, 7). 
X = 3  
Yes 
32 ?- plus(8, 9, Z). 
Z = 17  
Yes 
33 ?- plus(3, Y, 1). 
Y = -2  
Yes 

For those of you who are interested, we can define such a predicate this way, using builtin prolog arithmetic and some metalogical variables:

add(X, Y, Z) :- nonvar(X), nonvar(Y), Z is X+Y . 
add(X, Y, Z) :- nonvar(X), nonvar(Z), Y is Z-X . 
add(X, Y, Z) :- nonvar(Y), nonvar(Z), X is Z-Y . 

nonvar returns true if and only if its argument is not a variable. That is to say, if instead of plus we used add, add(X, 4, 7). would have called the "third" predicate (for Y and Z are not variables, since they are "unified" -- substituted -- with 4 and 7). add(8, 9, Z). would have called the first predicate and add(3, Y, 1) the second.

But we can do something even smarter. First of all we recognize that the three clauses above are almost mutually exclusive. Two of the three clauses will fail if two of the three parameters are not variables. They will all succeed in the very same way if all three are not variables. They will all fail if only one is a variable.

So we can tell prolog: after you determined which is the variable, you can only use that rule. If none is a variable, use the first rule. We use "cuts". The rules become.

add(X, Y, Z) :- nonvar(X), nonvar(Y), !, Z is X+Y . 
add(X, Y, Z) :- nonvar(X), nonvar(Z), !, Y is Z-X . 
add(X, Y, Z) :- nonvar(Y), nonvar(Z), !, X is Z-Y . 

Cuts are a complex subject. I'm not going to treat them here. Now I introduce a predicate between.


between(+Low, +High, ?Value)
Low and High are integers, High >=Low. If Value is an integer,
Low=< Value=< High. When Value is a variable it is successively
bound to all integers between Low and High. If High is inf
or infinite between/3 is true iff Value>= Low, a feature that is
particularly interesting for generating integers from a certain
value.

At this point we add this clauses:
add(X, Y, Z) :- nonvar(Z), !, between(0, Z, X), add(X, Y, Z). 
add(X, Y, Z) :- nonvar(Y), !, between(0, inf, X), add(X, Y, Z). 
add(X, Y, Z) :- nonvar(X), !, between(0, inf, Y), add(X, Y, Z). 

Now you are able given one variable to generate pairs that satisfy the relationship (in fact pairs of positive numbers only).
Now let define prod.

prod(X, Y, Z) :- nonvar(X), nonvar(Y), !, Z is X*Y . 
prod(X, Y, Z) :- nonvar(X), nonvar(Z), !, Y is Z/X . 
prod(X, Y, Z) :- nonvar(Y), nonvar(Z), !, X is Z/Y . 
prod(X, Y, Z) :- nonvar(Z), !, between(1, Z, X), prod(X, Y, Z). 
prod(X, Y, Z) :- nonvar(Y), !, between(1, inf, X), prod(X, Y, Z). 
prod(X, Y, Z) :- nonvar(X), !, between(1, inf, Y), prod(X, Y, Z). 

Now it's time to define division. Do you know the euclidean algorithm? I suppose so. However, you don't need to know it. All you need to know is that dividing x for y you are looking for two numbers q and r such that

  • x = y * q + r
  • 0 <= r < y>

And in Prolog it is:

div(X, Y, 0, X) :- abs(X) < abs(Y). 
div(X, Y, Q, R) :- prod(Y, Q, T1), T1 =< X,  add(T1, R, X) , 
                   R >= 0, abs(R) < abs(Y). 

This does not work when X and Y have not the same sign. However this depends from the way I defined prod. You should be able to make it work.

Tuesday, March 14, 2006

Developing on Mac, Win, Linux (Editors - pt. 1)

I kind have to tell something about me, since it better helps understanding my position.

I chose MacOS X as my main developing platform. Of course it happens to write software meant to run on GNU/Linux (or other *nix flavours) or even on Win. Usually when this happens I'm using a cross platoform technology. I never wrote something longer than 100 lines with the WinAPI, for example.

Before I used GNU/Linux, fBSD and even before other unices (OSF1) and MacOS "Classic". This is my background. I never quite got exposed to windows. So my point is unusual: most people come from windows to other platforms. To me is quite the contrary (a part that I don't "go" to windows, I just happen to use it from time to time).

About programming environments, I've done (and do) lots of things. I like high level languages, such as Python or Ruby. I like ObjectiveC and Cocoa (of course this won't be in our comparison...). But also do a lot of low level C coding with the POSIX api (well I did... right now I prefer to do it in Python), develope some software in C++, I'm going to graduate with a project in Prolog. I quite like XML and CSS and I also have to use some Java and some calculus (both Matlab and C -- no, I don't quite master Fortran)

So my skills are not particulary vertical (for example someone who does all his job in the XML processing fields, or numeric calculus). They range in many different fields (this does not mean I'm really "guru" in each of them, on the contrary I've got much to learn).

Editors

Basically those who work in the Linux community used to share in equal parts between Emacs and vim. There used to be some other editors (nedit), but that was the story. Recently I've seen a lot interest around IDEs (kdevelop, anjuta) and some "lesser" editors have reached the status of full programmer's editors. Among these Scite and Kate, for example. Moreover HTML/web centric editors have also increased their popularity.

On the other side the Windows community has always been more IDE oriented. There were the Borland products, Microsoft Visual Studio, Wacom. The most used web environments were Dreameaver and Homesite (now builtin in dw).

Of course there were also a bunch of editors.

Linux pt. 1

If I have to work on Linux, I've got no problems of any sort. I'm average skilled both in vim and in Emacs and I can make it. I do use Aquamacs (an Emacs version) even on the MacOS: once I preferred vim, but right now I need some things Emacs has and vi hasn't (a decent Prolog mode, for example).

Windows

About an year and an half/two years ago I had to develop an application with Twisted. For a 0-based array of reasons I used Windows as my main developement environment. I installed entought python (and in those times there was no Python 2.4, so no problem), I put Twisted 1.3 on the top of it and chose gvim. Gvim is beautifully integrated with windows. Of course it acts quite much like vim does (i don't like the evim variant). I did it. Still I wasn't using windows. I cloned my linux environment on Windows. And it is what I made when I had to code some C++ with mingw and so on.

I chose to use gvim not because I really wanted to, but because other editors were really priced or poor. Yes I tried scintilla too. Not particulary poor, of course not priced... but in fact too simple.

Windows and MacOS

If you compare the situation with the MacOS it's tragic. MacOS has many "simple" editors that I do not really like (but some love SubEthaEdit or Smultron), but it has also BBEdit (the best web editing environment I've seen, much better than dreamweaver and priced 199$ - 129$ for TextWrangler users -- TW is a free editor everybody can download and use ). And it has TextMate: the best general programmer editor a part from Emacs.

UltraEdit (Windows)

Recently I had to do some more work: I tried UltraEdit, priced 39 $. The same as TextMate. A couple of users said it is a wonderful editor and I gave it a try. Out of the box it did not support nor Python nor Ruby. Quite annoying in fact.

I googled the solution and I find I had to copy some strings in a file. The format is awful. Where TM has a lot of small bundles, BBEdit has plugins, UltraEdit has only this big flatfile. Ok. What matters is functionality.

But functionality is missing. For example Python or Ruby indenting is really disappointing. TextMate indents code back and forth to match the syntax of the code. It takes less time to try than to read. If you haven't a Mac, Emacs does it.

TextMate also has lots of ways (and easy one) for saving me from typing lots of code. Snippets are poweful, and I also have commands (snippets are short words or commands that are expandend to full constructs with placeholders to fill in).

I find nothing like this (nothing that simple) in UltraEdit or in Scintilla. Not that Scintilla is not a bad editor. It correctly deals with a lot of languages, the syntax theme is clear and easily readable (I think I should do a "Scintilla Theme" for TextMate one day). With Python/Ruby it lets you run or check syntax directly from editor.

And UltraEdit (in the Studio Version) is a very good "tiny ide"-"enhanced editor" for Java, for example (but we are at 99$). Much better than BBEdit or TextMate in Java editing. It's fast and has some useful basical functions. Of course Eclipse or NetBeans do a lot more stuff, but UltraEdit Studio takes a couple on microseconds to load.

I've not tried it with PHP and HTML (have I already said I don't use PHP?), but it appears to be really good. I just can't stand feature bloated IDEs like DreamWeaver. They tend to make the programmer not to think... but that's another story.

The many windows editors

It could be a matter of taste. Another friend of mine who did a lot of coding in Windows suggested another text editor (I don't even remember the name of the editor -- I still remember my friend's name of course). Probably among all editors out there (more or less shareware) there is the one that suits my taste (a part from the Emacs/gvim variants). Still everybody knows a good way to fuck up a windows installation is to install and try software (I know ghost, but it looks like I've no time to waste to play with software).

GNU/Linux pt. 2

Again... GNU/Linux is quite convenient in this sense. Emacs and vim are wonderful editors (and they are both free and free). They can be extended to do almost everything (think about Emacs mode to make it a Java IDE or the Auctex package).

They are both available for Windows and Mac, but additional packages are not as easy to install (on Debian is a matter of aptgetting...)

About the IDEs... well, KDevelop is told to be really good. Probably For sure it outperforms XCode, but should be not as good as MS Vistal Studio, even if it should support more languages (so if you need one of those...). So depends on what you have to do... anyway it's a really good IDE, nothing to say... but :)

Windows pt 2

I tried some more editors. Notepad++ is nice (but not really suited for Python or Ruby: in this sense Scite is much better).

I tried Komodo, and it's wonderful (and also cross-platform). The best thing it does are easy debugging and intellisense like for dynamic languages (tried with python and ruby, it should work with PHP and Perl too). Unfortunately the full version costs almost 300 bucks. If you don't develop professionally you can buy the "personal or educational" version, that at 29$ is quite affordable.

Anyway some things in Komodo to me look quite akward, while TextMate is as easy as poweful. Of course comparing Komodo to TM in Rails editing is playing dirty. TM is the editor of choice of Rails developers. And Komode has that intellisense... well, I think it should be great (even if I didn't really use it, so I think I'm not really gonna miss it).

I've seen there are a lot of targetted small IDEs that are worth trying, but I'm not gonna spend all my time this way.

Next time I'm gonna talk about databases

Thursday, February 23, 2006

get_sublist in prolog

The hardest thing when programming prolog, is to change your mind.
In fact declarative programming (while powerful) is hard to get in touch.
I always tend to think imperatively, even if I attended formal logic lessons in the University.
Now just a couple of easy things... I'm a beginner.

get_sublist(Start, Len, List, Sub) :- 
    append(L1, L2, List), 
    length(L1, Start), 
    append(Sub, _, L2), 
    length(Sub, Len).

This way you get a sublist. The style is declarative.
The efficience is .... well we ll'see later.

An "imperative" version is:
get_sublist(_, 0, _, []).
get_sublist(Start, Len, List, [H | Acc]) :- 
    nth0(Start, List, H), 
    succ(Start, NStart), succ(NLen, Len), 
    get_sublist(NStart, NLen, List, Acc). 

It's not as clean, but should be more efficient. It uses an accumulator variable.
In fact this algorithm is not really declarative.
We could say that it does the same thing than

def get_sublist(start, len, list, acc) 
    if len > 0 
        acc.push list[start] 
        nstart = start + 1 
        nlen = len - 1 
        get_sublist(nstart, nlen, list, acc) 
    end 
end 

Incidentally this is ruby code, you can test it with

l = [] 
get_sublist(2, 3, %w{ a b c d e f g}, l) 
l.each { |e| puts e} 

And now... lets see if we optimized or not.
This is comes from SWIProlog.

5 ?- time(get_sublist(2, 3, [a, b, c, d, e, f], Acc)). 
% 22 inferences, 0.00 CPU in 0.00 seconds (0% CPU, Infinite Lips) 
Acc = [c, d, e] 
Yes 
6 ?- time(get_sublist_old(2, 3, [a, b, c, d, e, f], Acc)). 
% 37 inferences, 0.00 CPU in 0.00 seconds (0% CPU, Infinite Lips) 
Acc = [c, d, e] 
Let's try with bigger numbers... 
24 ?- make_list(100000, L), time(get_sublist(2000, 500, L, Acc)). 
% 378,001 inferences, 0.33 CPU in 0.42 seconds (78% CPU, 1145458 Lips) 
L = [100000, 99999, 99998, 99997, 99996, 99995, 99994, 99993, 99992|...] 
Acc = [98000, 97999, 97998, 97997, 97996, 97995, 97994, 97993, 97992|...] 
Yes 
25 ?- make_list(100000, L), time(get_sublist_old(2000, 500, L, Acc)). 
% 15,007 inferences, 0.08 CPU in 0.09 seconds (87% CPU, 187587 Lips) 
L = [100000, 99999, 99998, 99997, 99996, 99995, 99994, 99993, 99992|...] 
Acc = [98000, 97999, 97998, 97997, 97996, 97995, 97994, 97993, 97992|...] 
Yes 
26 ?- make_list(100000, L), time(get_sublist_old(2000, 5000, L, Acc)). 
% 42,007 inferences, 0.56 CPU in 0.66 seconds (85% CPU, 75013 Lips) 
L = [100000, 99999, 99998, 99997, 99996, 99995, 99994, 99993, 99992|...] 
Acc = [98000, 97999, 97998, 97997, 97996, 97995, 97994, 97993, 97992|...] 
Yes 
27 ?- make_list(100000, L), time(get_sublist(2000, 5000, L, Acc)). 
% 7,530,001 inferences, 6.88 CPU in 8.82 seconds (78% CPU, 1094477 Lips) 
L = [100000, 99999, 99998, 99997, 99996, 99995, 99994, 99993, 99992|...] 
Acc = [98000, 97999, 97998, 97997, 97996, 97995, 97994, 97993, 97992|...] 
Yes 

It looks like we didn't optimize very much. :(
I should study more.