Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

mgrzeg.net - Admin on Rails :)

P# = Prolog + C#. How to use it - wstęp

Wprowadzenie

 

Obok gigantycznego świata języków imperatywnych typu C, C++, C#, Java, etc. istnieje cała gałąź języków deklaratywnych. Chyba najpopularniejszym z nich jest Prolog (od ‘Programowanie w Logice’), powstały na początku lat 70. ubiegłego wieku i który do dzisiaj znalazł wiele implementacji i dialektów.

Zasadniczo programy napisane w Prologu różnią się tym od programów do których jesteśmy przyzwyczajeni, że opisują fakty i relacje między obiektami, a w mniejszym stopniu skupiają się na podawaniu kolejnych kroków algorytmu. Nie zamierzam jednak skupiać się na samym języku czy też jego zastosowaniach, a raczej na jednej z mniej znanych jego implementacji - P#.

 

Czym jest P#?

 

W dużym skrócie P# jest implementacją Prologu napisaną w C# zawierającą translator kodu Prologu do C#. Powstał w 2003 roku w ramach dysertacji doktorskiej Jonathana Cook’a i właściwie od tamtej pory (czyli wersji 1.1.3) nie rozwija się dalej. Przy tworzeniu P# Jon Cook opierał się na Prolog Cafe - narzędziu pełniącym tę samą funkcję, co P#, jednak językiem docelowym była Java i właściwie wszystkie konstrukcje dostępne w Prolog Cafe (wersja 0.6.1) przeniesione zostały do P#.

P# (Psharp) składa się z gigantycznej biblioteki Psharp.dll, zawierającej pełną implementację języka oraz wszystkich niezbędnych narzędzi do pracy w środowisku .NET, oraz dwóch programów interaktywnych do pracy z Prologiem:
1. PsharpGUI - graficzny edytor kodu wraz z interpreterem oraz kompilatorem kodu

 

 

2. PsharpIntrp - konsolowy interpreter.

 

 

Oba narzędzia są bardzo pomocne przy pracy z prologiem, jednak najważniejsze jest dostępne API, dzięki któremu możemy korzystać z dobrodziejstw Prologu w programach napisanych w C#.

OK. Tyle słowem wstępu, przejdźmy teraz do najważniejszego :)

 

Jak z tego korzystać?

 

Na ruszt weźmy przykładowy predykat ‘member’ ze świetnej książki W.F.Clocksina i C.S.Mellisha (polskie tłumaczenie!!!), weryfikujący, czy zadany obiekt jest elementem listy:

 

member(X,[X|_]).

member(X,[_|Ys]):-member(X,Ys).

 

No cóż, niby nic wielkiego, ale jaka siła drzemie w tych dwóch linijkach programu! Zauważmy, że nie określiliśmy, jakiego typu są elementy listy, więc równie dobrze możemy testować listy liczb całkowitych jak i listy napisów.

Uruchamiamy PsharpGUI.exe, wklejamy kod do dolnego okna i z menu ‘Editor’ wybieramy ‘Compile to C#’ (Ctrl+Shift+C). W katalogu aplikacji pojawił się plik Member_2.cs, zawierający kod Prologu zinterpretowany do odpowiadającemu mu w C#.

Mając przygotowany kod klasy C# możemy przejść do kolejnego kroku, czyli skorzystania z wygenerowanego kodu w naszym programie C#. Poniższy kod umieszczamy w pliku Program.cs i dołączamy do projektu plik Member_2.cs wygenerowany w poprzednim kroku.

 

using System;

using System.Collections.Generic;

using System.Text;

 

using JJC.Psharp.Lang; // 0

using JJC.Psharp.Predicates; //0

 

namespace pl.net.zine.Prolog

{

  class Runner

  {

    static void Main(string[] args)

    {

      Runner p = new Runner();

      p.test_Member();

    }

 

    public void test_Member()

    {

      PrologInterface sharp = new PrologInterface(); // 1

      ListTerm intList = GetList(new int[] { 1, 6, 3, 8, 6, 7 }); //2

      Term a2 = new IntegerTerm(9); // 3

      Predicate member = new Member_2(a2, intList, new ReturnCs(sharp)); // 4

      sharp.SetPredicate(member); // 5

      bool b = sharp.Call(); // 6

 

      Term result = a2.Dereference(); // 7

    }

 

    /// <summary>

    /// Pomocnicza metoda do konwersji tablicy intów na ListTerm

    /// </summary>

    /// <param name="ints">tablica intów</param>

    /// <returns></returns>

    private ListTerm GetList(int[] ints)

    {

      Term empty = SymbolTerm.MakeSymbol("[]");

      Term[] terms = new Term[ints.Length];

      for (int i = 0; i < ints.Length; i++)

      {

        terms[i] = new IntegerTerm(ints[i]);

      }

      Term list = empty;

      for (int i = terms.Length - 1; i >= 0 ; i--)

      {

        list = new ListTerm(terms[i], list);

      }

      return (ListTerm)list;

    }

  }

}

 

Kod całego programu nie jest długi, więc dla porządku i łatwego przetestowania podaję go w całości. Oto pokrótce opis tego co się w nim dzieje:

0. Ten krok właściwie nie wymaga komentarza - po dołożeniu referencji do Psharp.dll wprowadzamy odpowiednie przestrzenie do naszego kodu.

1. Przechodzimy do metody test_Member(), w której znajduje się cały interesujący nas kod. W pierwszym kroku inicjalizujemy interfejs Prologa.

2. Do utworzenia odpowiedniej listy przekazanej jako parametr predykatu Member potrzeba wykonać wiele kroków - stąd pomocnicza metoda GetList(int [] ints).

3. Tu tworzymy Term opakowujący liczbę całkowitą.

4. Tworzymy obiekt predykatu Member_2. Jak widać, nasz predykat ‘member’ zamieniony został w ‘Member_2’, czyli pierwsza litera nazwy stała się dużą literą (podobnie byłoby z pozostałymi członami oddzielonymi ‘_’: ‘my_member’->’MyMember’), natomiast ‘2’ pojawiająca się po ‘_’ określa ‘arność’ predykatu (w naszym przypadku jest to 2).

5. Bindujemy odpowiedni predykat do interfejsu Prologu.

6. Zadajemy przygotowane pytanie. W odpowiedzi otrzymujemy wynik typu bool, który informuje nas o tym, czy ‘9’ jest elementem listy, czy też nie. Oczywiście w wyniku dostajemy false.

7. W przypadku, gdyby zależało nam na wyliczeniu czegoś, to krok 3 powinien wyglądać następująco:

      Term a2 = new VariableTerm();

a w kroku 7 dostajemy pierwszą odpowiedź - czyli w naszym przypadku będzie to ‘1’. W podanym przykładzie a2 zawiera oczywiście cyfrę ‘9’, ponieważ testowaliśmy jedynie, czy jednym z elementów listy nie jest cyfra ‘9’.

 

Drugi przykład będzie nieco bardziej rozbudowany - od strony kodu Prologu. Tym razem sięgniemy po kolejny przykład, tym razem z bodajże najlepszej książki poświęconej Prologowi - ‘The Art of Prolog’ autorstwa Leona Sterlinga i Ehuda Shapiro - realizujący algorytm quicksort.

 

select(X,[X|Xs],Xs).

select(X,[Y|Ys],[Y|Zs]):-select(X,Ys,Zs).

 

permutation(Xs,[Z|Zs]):-select(Z,Xs,Ys),permutation(Xs,Ys).

permuation([],[]).

 

ordered([]).

ordered([X]).

ordered([X,Y|Ys]):-X=<Y,ordered([Y|Ys]).

 

append([],Ys,Ys).

append([X|Xs],Ys,[X|Zs]):- append(Xs,Ys,Zs).

 

partition([X|Xs], Y, [X|Ls], Bs):- X =< Y, partition(Xs,Y,Ls,Bs).

partition([X|Xs], Y, Ls, [X|Bs]):- X > Y, partition(Xs,Y,Ls,Bs).

partition([], Y, [], []).

 

quicksort([],[]).

quicksort([X|Xs],Ys):-partition(Xs,X,L,B),quicksort(L,Ls),quicksort(B,Bs),append(Ls,[X|Bs],Ys).

 

Odpowiedni kod metody wywołującej algorytm quicksort w C#:

    public void test_Sort()

    {

      PrologInterface sharp = new PrologInterface();

      ListTerm intList = GetList(new int[] { 1, 6, 3, 8, 6, 7 });

      Term a2 = new VariableTerm();

      Predicate member = new Quicksort_2(intList, a2, new ReturnCs(sharp));

      sharp.SetPredicate(member);

      bool b = sharp.Call();

 

      Term result = a2.Dereference();

    }

Jak widać, z punktu widzenia kodu C# różnice nie są duże - Term a2 jest właśnie zmienną, w której chcemy mieć ‘na wyjściu’ posortowaną listę liczb.

Po wykonaniu tej metody term result zawiera tablicę [1, 3, 6, 6, 7, 8].

 

Zakończenie

 

Na koniec tego wstępu do P# jeszcze jeden programik.

Jak już wcześniej napisałem, w ramach pakietu dostajemy dwa programy wspierające interaktywną pracę z Prologiem i pozwalające na translację kodu Prologu do C#. Brakuje jednak narzędzia, które po prostu będzie ‘tłumaczyło’ kod prologu zawartego w pliku do odpowiedniego zestawu klas C#. Nic straconego! Okazuje się bowiem (co w gruncie rzeczy nie powinno dziwić), że w ramach biblioteki Psharp jest predefiniowany predykat realizujący ‘compile(‘nazwa_pliku’)’ i nazywa się oczywiście ‘Compile_1’ [:)]. Teraz, gdy już wiemy w jaki sposób ‘wywoływać’ predykaty, poniższy kod możemy pozostawić bez komentarza.

 

using System;

using System.Collections.Generic;

using System.IO;

using System.Text;

 

using JJC.Psharp.Lang;

using JJC.Psharp.Predicates;

 

namespace Prolog2CSharp

{

  class Program

  {

    static void Main(string[] args)

    {

      if (args.Length != 1 || !File.Exists(args[0]))

      {

        Console.WriteLine("Usage: {0} file.pl{1}where file.pl is the prolog file",

          System.Diagnostics.Process.GetCurrentProcess().ProcessName, Environment.NewLine);

        return;

      }

      PrologInterface sharp = new PrologInterface();

      SymbolTerm file = SymbolTerm.MakeSymbol(Path.GetFileNameWithoutExtension(args[0]));

      Predicate member = new Compile_1(file, new ReturnCs(sharp));

      sharp.SetPredicate(member);

      bool b = sharp.Call();

    }

  }

}

 

Oczywiście możemy wykorzystać ten programik w ramach ‘Pre-Build events’, czy też w ramach wspomnianego obok ‘CustomTool dla Visual Studio’ i zautomatyzować sobie generację kodu C#. Należy oczywiście pamiętać o odświeżeniu projektu i dołączeniu wygenerowanych plików (sugeruję utworzyć podkatalog ‘Prolog’ w strukturze projektu i tam trzymać pliki .pl Prologu i wygenerowane klasy C#).

W powyższym tekście nie opisywałem mechanizmów stojących za P# - zainteresowanych odsyłam do materiałów związanych ze wspomnianą dysertacją Jonathana Cooka.Poza możliwością pracy równoległej P# udostępnia wiele różnych mechanizmów. Naprawdę warto zajrzeć!

Opublikowane 10 kwietnia 2007 01:41 przez mgrzeg
Filed under:

Powiadamianie o komentarzach

Jeżeli chciałbyś otrzymywać email gdy ta wypowiedź zostanie zaktualizowana, to zarejestruj się tutaj

Subskrybuj komentarze za pomocą RSS

Komentarze:

Brak komentarzy

Co o tym myślisz?

(wymagane) 
(opcjonalne)
(wymagane) 

  
Wprowadź kod: (wymagane)
Wyślij
W oparciu o Community Server (Personal Edition), Telligent Systems