» Witam! » Blog
Blog
Tak na próbę…
Z pewnością wiadomości będą dotyczyły zagadnień, którymi zajmuję się zawodowo, czyli języków programowania (w szczególności Rubiego), przetwarzania języka naturalnego i sztucznej inteligencji.
Zestawienie poleceń: Git i SVN
Yehuda Katz opublikował artykuł, w którym pokazuje, jak wygląd jego typowa sesja edycji kodu w kontekście Git-a. Cały artykuł warty jest przeczytania, natomiast tabelka, która pojawia się na końcu zdecydowanie może służyć za ściągę z Gita, dla wcześniejszych użytkowników SVN-a. Pozwalam ją sobie przerysować:
| Operacja | git | svn |
|---|---|---|
| Klonowanie repozytorium | git clone git://github.com/rails/rails.git |
svn checkout http://dev.rubyonrails.org/svn/rails/trunk |
| Przygotowywanie zmian | git add, git commit |
brak lub ręczne przygotowanie diff-a |
| Ściąganie zmian z repozytorium | git pull --rebase |
svn up |
| Rozstrzyganie konfliktów | git add, git rebase --continue |
svn resolve |
| Rozstrzyganie konfliktów (bez—rebase) | git add, git commit |
brak |
| Wycofywanie zmian (przygotowanych do wysłania) | git reset --hard |
svn up -rOLD potem zaaplikowanie diff-a (jeśli pamiętałeś żeby go zrobić) |
| Publikowanie do repozytorium | git push |
svn commit |
Musicbrainz w Rubim
SemanticWeb i Ruby
Niestety nie mam zbyt wiele czasu, aby tłumaczyć ideę SemanticWeb. W dwóch słowach można powiedzieć, że chodzi możliwość pobierania danych z Internetu w sposób zbliżony do podbierania danych z relacyjnej bazy danych. Oczywiście biorąc pod uwagę immanentny internetowy pluralizm, trudno wyobrażać sobie, że taka “baza danych” mogłaby posiadać wspólny schemat, dlatego też jednym z kluczowych elementów SemanticWeb jest koncepcja ontologii – w tym kontekście: specyfikacji pojęć, które służą do opisu danych.
Na dzień dzisiejszy jedną z najbardziej doniosłych manifestacji tej idei jest Linked Open Data i słynna chmura ontologii, czyli obrazek, który ma nas przekonać, że inwestując w ideę SemanticWeb, można już pobrać bardzo dużo danych w ten sposób i, co więcej, dane te nie będą odizolowane, ale przynajmniej w pewnym stopniu powiązane z innymi danymi, publikowanymi przez rozmaite podmioty.
Jednym z kluczowych elementów SemanticWeb staje się DBpedia, która jest ontologią powstałą na bazie informacji wyekstrahowanych z angielskiej (ale nie tylko) Wikipedii. Coraz więcej innych zasobów jest “linkowanych” do DBpedii, dzięki czemu staje się ona w dużej mierze ontologią (schematem) odniesienia.
Czy faktycznie można już wykorzystywać powiązania pomiędzy poszczególnymi źródłami wiedzy pozostaje kwestią dyskusyjną (np. przedstawione na obrazku powiązanie DBpedii z Musicbrainzem wydaje się mocno wątpliwe, gdyż najistotniejsze elementy – czyli klasy, nie są powiązane), niemniej jednak warto śledzić rozwój tej koncepcji, gdyż istotnie staję się ona coraz poważniej traktowana również przez instytucje komercyjne (np. Reuters, BBC).
Jako, że moim ulubionym językiem jest Rubi, jakiś czas temu testowałem możliwość wykorzystania biblioteki ActiveRDF do pobierania danych z DBpedii. W tamtym poście jest w zasadzie tylko kod i jeśli nie miało się styczności z ActiveRDF trudno cokolwiek z niego wywnioskować. W niniejszym poście chciałbym przedstawić inny przykład wykorzystania ActiveRDF, tym razem do pobierania danych z Musicbrainza, czyli wielkiej bazy zawierającej dane dotyczące tysięcy muzyków, płyt, utworów muzycznych i tym podobnych.
ActiveRDF
ActiveRDF, jak można wnioskować na podstawie nazwy, ma uczynić nasze życie z SemanticWeb znacznie prostszym (podobnie jak ActiveRecord czyni prostszym (?) obsługę relacyjnych baz danych). Niestety po tym jak twórca tej biblioteki, czyli Eyal Oren przestał się nią opiekować, straciła ona swoja początkową stabilność, a, co gorsza, wiele przykładów, które zostały udokumentowane, przestało działać w kolejnych wersjach. Jest to pewna przypadłość języków dynamicznych, w szczególności Rubiego, że twórcy rozwiązań mniejszą wagę przywiązują do zachowania stabilnego API – w myśl zasady: “lepsze wrogiem dobrego”, zapominając jednak, że często “działające lepsze od żadnego”. Pomijając jednak te filozoficzne dywagacje, warto wspomnieć, że obecnie ActiveRDF ma swoje konto na Githubie i jest rozwijane. Niemniej jednak uruchomienie najnowszej (1.7.0) wersji, w szczególności jeśli używamy Rubiego 1.9 nie jest wcale banalne.
Niestety aktualny opiekun biblioteki nie otagował tej wersji, nie wysłał jej także na gemcuttera, przez co skazani jesteśmy na budowanie gema ze źródeł, co wcale nie jest takie banalne (specyfikacja gemów zdefiniowana jest z użyciem jewelera, który jest niekompatybilny z Rubim 1.9). Dlatego najprościej jest ściągnąć mojego brancha 1_7_0
$ git clone git://github.com/apohllo/ActiveRDF.git ... $ cd ActiveRDF
Niestety nie obejdzie się bez użycia Rubiego 1.8 do zbudowania gema. Ja korzystam z Gentoo, dlatego mogę zmienić wersję wykonując polecenie:
$ sudo eselect ruby set 1 Successfully switched to profile: ruby18 $ /usr/bin/ruby18 /usr/bin/rake build (in /home/fox/src/ruby/ActiveRDF) Gokdok is not available. Install with: gem install gokdok Rcov or dependency is not available Generated: activerdf_net7.gemspec activerdf_net7.gemspec is valid. WARNING: no rubyforge_project specified WARNING: deprecated autorequire specified Successfully built RubyGem Name: activerdf_net7 Version: 1.7.0 File: activerdf_net7-1.7.0.gem $ sudo eselect ruby set 2 Successfully switched to profile: ruby19 $ sudo gem install pkg/activerdf_net7-1.7.0.gem
Można również skorzystać z rvm, ale nie będę tego opisywał w szczegółach.
Kiedy mamy już zbudowany i zainstalowany gem activerdf w wersji 1.7.0 musimy jeszcze doinstalować odpowiedni adapter RDF. Ponieważ Musicbrainz eksponuje sparql endpoint, musimy doinstalować właśnie ten adapter:
$ cd activerdf-sparql/ $ gem build activerdf-sparql.gemspec WARNING: no email specified WARNING: no homepage specified WARNING: no rubyforge_project specified WARNING: description and summary are identical Successfully built RubyGem Name: activerdf-sparql Version: 1.3.6 File: activerdf-sparql-1.3.6.gem $ sudo gem install activerdf-sparql-1.3.6.gem ...
W tej chwili jesteśmy już w pełni wyposażeni aby korzystać z ActiveRDF w najnowszej wersji w Rubim 1.9
Musicbrainz
Sparql endpoint Musicbrainz jest obsługiwany przez serwer d2r, który oficjalnie nie jest wspierany przez aktualny adapter ActiveRDF. Najwyraźniej jednak standard SPARQL staje się na tyle dojrzały, że nie ma zbyt wielu różnic pomiędzy poszczególnymi implementacjami, dzięki czemu można wykorzystać np. implementację dla serwera Virtuoso.
require 'active_rdf' include ActiveRDF ConnectionPool.add(:type => :sparql, :url => 'http://dbtune.org/musicbrainz/sparql', :engine => :virtuoso)
Powyższy kod tworzy połączenie z ontologią Musicbrainz. Następnie rejestrujemy najistotniejsze przestrzenie nazw (FOAF:http://www.foaf-project.org/, oraz MusicOntology:http://musicontology.com/):
ActiveRDF::Namespace.register :foaf, 'http://xmlns.com/foaf/0.1/' ActiveRDF::Namespace.register :mo, 'http://purl.org/ontology/mo/'
W zasadzie w tym momencie moglibyśmy już przeglądać zawartość Musicbrainza, ale jest jeszcze jeden
haczyk – otóż najwyraźniej serwer D2R nie radzi sobie z zapytaniami, w których explicite podane są
typy atrybutów (np. dla łańcucha znaków). Dlatego też musimy ustawić zmienną globalną (sic!)
$activerdf_without_datatype na true:
$activerdf_without_datatype = true
Mam nadzieję, że w kolejnych wersjach ActiveRDF można będzie zrobić to w bardziej cywilizowany sposób. Tak, czy owak, w tej chwili możemy już przeglądać szczegółowe informacje na temat muzyków, zespołów i innych, np. W teorii wygląd prosto, ale w praktyce (ze względu na rozproszony charakter wiedzy), sprawy się komplikują:
sting = ActiveRDF::Query.new.select(:x).where(:x, FOAF::name, "Sting").execute[0] sting.foaf::name.first #=> "Sting" groups = ActiveRDF::Query.new.select(:x).where(:x, FOAF::member, sting).execute groups.each{|g| puts g.foaf::name.first} Strontium 90 The Police
W powyższym przykładzie z bazy pobieram zasób, który reprezentuje Stinga. Następnie odpytujemy się o jego “nazwę” – i tutaj właśnie pierwsza niespodzianka, bo musimy dla tej nazwy (które też jest zasobem) wywołać metodę “first”. Powód tego (w moim najlepszym mniemaniu) jest taki, że moglibyśmy mieć w kilku źródłach wiedzy różne wartości tego predykatu i musimy mieć dostęp do wszystkich.
Następnie pobieramy informacje o zespołach, w których grał Sting. Wykorzystujemy do tego predykat FOAF:member, który wiąże grupę z jej członkami (grupa jest podmiotem w tej relacji). Dostajemy dwa rezultaty, do których dobieramy się tak jak wcześniej.
Na koniec pobierzemy informacje o zarejestrowanych koncertach, w których Sting był wiodącym wokalistą:
performances = ActiveRDF::Query.new.select(:x).where(:x, MO::lead_singer, sting).execute performances.each{|g| puts g.label} #=> Sting performing (recorded on album Why Don't You Answer?) #=> Sting performing (recorded on album Zenyattà Mondatta)
Jak widać powyżej, w bazie widnieją tylko informacje o dwóch zarejestrowanych koncertach.
Podsumowanie
Pomimo problemów jakie trzeba rozwiązać, korzystając z wiedzy zawartej w SemanticWeb, można przyjąć na chwilę obecną, że technologie semantyczne są już “używalne”, a w Rubim, czego mogliśmy się spodziewać, po przezwyciężeniu problemów konfiguracyjnych, można całkiem przyjemnie je przetwarzać.
poliqarpr 0.0.5
Poliqarpr jest nakładką dla Rubiego, pozwalającą w prosty sposób korzystać z serwera Poliqarp.
Poliqarp – serwer korpusów
Sam Poliqarp został pomyślany jako narzędzie ułatwiające pracę z korpusami tekstów. Jego zasadnicze przeznaczenie to wyszukiwanie fragmentów tekstów na potrzeby różnych zadań z dziedziny przetwarzania języka naturalnego. W obecnej chwili jest wykorzystywany w projekcie Narodowego Korpusu Języka Polskiego, ale został zaprojektowany w sposób, który pozwala stosować go również dla innych języków.
Co więcej – język zapytań Poliqarpa pozwala wyszukiwać słowa w korpusie nie tylko w oparciu o odmienione formy (wtedy nie różniłby się on specjalnie od funkcjonalności baz danych z wyszukiwaniem pełnotekstowym), ale pozwala np. określić, formę podstawową (tzw. lemat) słowa, a znalezione zostaną wszystkie wystąpienia, również form derywowanych (np. możemy zadać pytanie o “bój”, a w wynikach otrzymamy formy odmienione: boju, boje, bojom).
Również ta funkcjonalność nie jest związana wyłącznie z językiem polskim (choć ma zastosowanie głównie dla języków fleksyjnych), ani nie jest przywiązana do jednego zbioru tagów. Dzięki temu, w zależności od zastosowanego narzędzia, możemy korzystać z alternatywnych zbiorów tagów, nawet w ramach jednego języka.
Poliqarpr – klient napisany w Rubim, dla serwera Poliqarp
Poliqarpr, jak było wspomniane, jest napisanym w Ruby klientem, dla serwera Poliqarp. Jest on dostępny w publicznym repozytorium na GitHubie. Możemy go stamtąd pobrać za pomocą poniższego polecenia:
$ git clone git://github.com/apohllo/poliqarpr.git
Oczywiście jest on również dostępny jako gem na gemcutterze:
# sprawdzamy wersję rubygems $ gem -v 1.3.5 #=> OK! # dodajemy gemcutter do listy repozytoriów, jeśli wcześniej na niej nie był $ gem sources -a http://gemcutter.org # instalujemy poliqapr $ gem install poliqarpr -a http://gemcutter.org Successfully installed poliqarpr-0.0.5 1 gem installed Installing ri documentation for poliqarpr-0.0.5... Installing RDoc documentation for poliqarpr-0.0.5...
Jeśli wszystko przebiegnie bez zakłóceń możemy zacząć testować poliqarpa. Ponieważ jednak bez korpusu nie ma to większego sensu,
dostępny jest również gem poliqarpr-corpus, który zawiera przykładowy korpus dla języka polskiego (zrównoważony słownik frekwencyjny).
Instalacja jest równie prosta:
$ gem install poliqarpr-corpus
W tym momencie musimy uzbroić się w odrobinę cierpliwości, bo korpus ma rozmiar kilku megabajtów.
Poliqarpr nie jest jedynym klientem dla serwera Poliqarp – dostępna jest również wersja w Javie, napisana przez autorów serwera. W stosunku do klienta javowego otrzymujemy jednak kilka udogodnień:- specyfikacja w formie testów
- segmenty, fragmenty oraz rezultaty zapytań są pełnoprawnymi klasami
- jest napisany w Rubim ;)
W szczególności to, że rezultaty zapytań są klasami, pozwala w łatwy sposób tworzyć paginację. Po szczegóły najlepiej zajrzeć do dostępnych w repozytorium speców.
Przykład użycia
Przypuśćmy, że mamy na naszym komputerze korpus języka polskiego o wielkości 250 mln segmentów, pobrany ze
strony korpus.pl.
Załóżmy, że znajduje się w katalogu /home/user/korpus. Co należy zrobić, aby można było go wygodnie przeglądać lub wykorzystać
w programie napisanym w Rubim?
Po pierwsze musimy upewnić się, że serwer poliqarp jest zainstalowany i działa. Najprościej wykonać w linii poleceń następującą komendę:
$ poliqarpd &
Jeśli nie pojawią się żadne błędy, oznaczać to będzie, że serwer poliqarp został uruchomiony.
Następnie możemy skorzystać z serwera w programie napisanym w Rubim. Na wstępie musimy załadować zainstalowany wcześniej gem, stworzyć klienta oraz załadować korpus:
require 'poliqarpr' client = Poliqarp::Client.new("TEST") client.open_corpus("/home/user/korpus/all") client.right_context = 10 client.left_context = 10 client.lemmata = :all client.buffer_size = 1000
Kiedy tworzymy klienta, możemy przekazać opcjonalny parametr będący nazwą sesji z serwerem (pozwala odróżniać od siebie
wiele jednocześnie połączonych klientów). Potem możemy otworzyć korpus (polecenie open_corpus), podając ścieżkę do jego katalogu, wraz z nazwą
(tutaj używamy korpusu 250 mln segmentów, którego nazwa to “all”).
- rozmiar prawo- i lewostronnego kontekst
- szczegóły opisu segmentów
- rozmiar bufora wyników, itp.
Kiedy dokonamy tych wstępnych ustaleń, możemy pobrać wyniki, dla wybranego zapytania (szczegóły składni języka zapytań omówione są w artykule na stronie dr. hab. Adama Przepiórkowskiego):
result = client.find("[base=kot]") result[0..5].each do |excerpt| putes excerpt end # – kukułka – piła – kot Zaznacz te obrazki, które # burza osy – ptaki – kot ptak – zegar – samochód # – orkiestra – telefon – kot Posłuchaj uważnie nagranych dźwięków i # ptaszek – burza lew – kot lecąca z kranu woda – # strumyk telefon – sowa – kot baran – lew – ptaszek
W powyższym przykładzie szukamy w korpusie wszystkich fragmentów, w których występuje wyraz “kot” w dowolnej formie. Następnie wyświetlany 6 pierwszych rezultatów. Każdy z rezultatów jest jednak pełnoprawnym obiektem, więc możemy przyjrzeć mu się szczegółowo:
result[0].author #=> Małgorzata Pamuła result[0].title #=> Wczesne nauczanie języków obcych... result[0].short_context.join("") #=> – kukułka – piła – kot Zaznacz te obrazki, które
API w chwili obecnej jest nieźle rozbudowane i udokumentowane, więc nie będę omawiał go szczegółowo.
Na koniec nie możemy zapomnieć o zamknięciu połączenia z serwerem:
client.close
cyc-console 0.0.5
Ponieważ opisane w poprzednim poście testy nowego serwisu do składowania gemów wypadły pomyślnie, postanowiłem umieścić tam nową wersję mojego gemu cyc-console.
Udało mi się zaimplementować następujące funkcjonalności:- autouzupełnianie poleceń Cyc (póki co na podstawie pliku użytkownika ~/.cyc_functions)
- autouzupełnianie symboli Cyc (na podstawie odpowiedzi zwróconych przez serwer)
- obcinanie zbyt długich odpowiedzi (to samo jednak dzieje się w zwykłej konsoli Cyca)
- problemy z poleceniami wielowierszowymi (problem bierze się z kolorowanego znaku zachęty – readline niepoprawnie oblicza długość linii i jakoś dziwnie się zawija, póki co nie znalazłem rozwiązania, które działałoby w Rubim)
- błędy nie są manifestowane inaczej niż przez
nil - to co wysyłane jest na STDOUT nie jest widoczne
Dzięki skorzystaniu z gemcuttera mogłem spokojnie dodać zależność od colors, bez dodatkowych
problemów w trakcie testowania. Zatem instalacja (o ile mamy gemcuttera w repozytoriach)
nie powinna teraz stanowić większego problemu. Dzięki rezygnacji z githuba, gem nazywa się teraz
po prostu cyc-console, zatem polecenie instalacyjne wygląda teraz następująco:
$ sudo gem install cyc-console
Jeśli nie mamy zainstalowanego gema colors, zostanie od doinstalowany automagicznie.
gemcutter
Kiedy opublikowałem ostatniego newsa i chciałem upewnić się, czy faktycznie cyc-console jest dostępne jako gem, ku mojemu zdziwieniu okazało się, że nie – charakterystyczna rubinowa ikona w Github, nie była aktywna. Zatem szybko przeszedłem do panelu konfiguracji projektu i już chciałem włączyć obsługę gemów (a w głowie dyndała mi myśl – czy rzeczywiście nie zrobiłem tego wcześniej), moje zdziwienie było jeszcze większe – w opcjach nie znalazłem możliwości budowania gemów!
Github hostował co najmniej kilka spośród gemów, które wyprodukowałem (są to zazwyczaj nieduże projekciki, które wykorzystuję w projektach z dziedziny NLP), choć w gruncie rzeczy niedawno doszedłem do wniosku, że schemat nazewnictwa, który wykorzystują (nazwa_uzytkownika-nazwa_gemu) jest dosyć niewygodny. W szczególności, jeśli chcemy w trakcie testów instalować gem lokalnie, a później go upublicznić przez gituhub, to oba gemy mają odmienną nazwę. Kiedy mamy jakieś zależności pomiędzy takimi gemami, to sprawa jeszcze bardziej się komplikuje, bo przy testowaniu gemów zależnych, trzeba by albo zmieniać nazwę zależności (aby wybierany był gem lokalny), albo wrzucać na github gem o nowym numerze, co jest tym bardziej niewygodne, bo trwa długo, a co gorsza, jest niezgodne z praktyką inżynierii oprogramowania, gdyż nieprzetestowany moduł nie powinien być publikowany.
Ostatnio rozważałem nawet przejście na rubyforge, tylko po to, by przechowywać tam gotowe gemy, ale widać, że ten serwis jest dosyć kiepsko konserwowany, dlatego odszedłem od tego pomysłu.
Kiedy teraz okazało się, że github nie hostuje gemów (przynajmniej nowych gemów), to informację tę przyjąłem z pewną ulgą. Szukając jednak przyczyny stanu rzeczy, natknąłem się na post, w którym znalazło się zalecane rozwiązanie tej problematycznej sytuacji. Otóż twórcy githuba polecają serwis gemcutter.
Wygląda na to, że serwisy tworzone do realizacji jednego, specyficznego zadania stają się coraz bardziej popularne (niedawno np. github nie miał zakładki “issues”, która pozwala śledzić problemy związane z hostowanym projektem, zalecany był np. Lighthouseapp) – innymi słowy: idea SOA faktycznie zaczyna być realizowana, nie tylko w świecie javowym. Ale to temat na inny post.
Postanowiłem więc przetestować gemcuttera. Pierwsza rzecz, który musimy zrobić, aby zacząć go używać, to oczywiście zarejestrować się. I już na tym etapie byłem nieco zdziwiony, bo formularz rejestracyjny jest OLBRZYMI, choć zawiera tylko trzy pola :)
Po rejestracji powinniśmy zainstalować gem gemcutter, na lokalnej maszynie. Trzeba jednak wpierw upewnić się, czy mamy rubygems w wersji co najmniej 1.3.3:
$ gem -v 1.3.1 # myślałem, że mam najnowsze rubygems... $ gem update --system Updating RubyGems Updating rubygems-update Successfully installed rubygems-update-1.3.5 ... $ gem -v 1.3.5 # uff $ gem install gemcutter ...Razem z gemem dostajemy nowe polecenia dla rubygems:
- gem tumble – ustawia Gemcutter jako podstawowe źródło dla gemów (zamiast RubyForge)
- gem push – wysyła gem na serwer Gemcutter
- gem migrate – migruje gem wysłany wcześniej na RubyForge na Gemcutter
- gem owner – włącza/wyłącza możliwość publikowanie przez innych twoich gemów
Z dyskusji, która pojawiła się na Rubyinside, wynika, że oficjalny hosting gemów może zostać przeniesiony z RubyForge na Gemcutter.
Ja w każdym razie zamierzać przetestować Gemcuttera – dzięki temu zarządzanie zależnościami między gemami będzie takie, jak być powinno :)
Na pierwszy ogień wziąłem mój gem colors.
$ gem build colors.gemspec WARNING: no rubyforge_project specified Successfully built RubyGem Name: colors Version: 0.0.4 File: colors-0.0.4.gem $ gem push colors-0.0.4.gem Enter your Gemcutter credentials. Don't have an account yet? Create one at http://gemcutter.org/sign_up Email: apohllo@o2.pl Password: Signed in. Your api key has been stored in ~/.gemrc Pushing gem to Gemcutter... Successfully registered gem: colors (0.0.4)
Mając w pamięci doświadczenia z githubem, gdzie po wrzuceniu gemu do repo, jego zbudowanie i pojawienie się w repo trwało kilkanaście minut, a często wiązało się z modyfikacją manifestu (brakujące pliki, etc.) bardzo miło zaskoczył mnie fakt, że w wynik wyszukiwania na stronie gemcutter dla terminu “colors” od razu zwrócił mój gem!
Postanowiłem więc sprawdzić, czy faktycznie jest widoczny przez rubygems:
$ sudo gem install colors ERROR: could not find gem colors locally or in a repository $ sudo gem tumble Thanks for using Gemcutter! Your gem sources are now: - http://gemcutter.org - http://gems.rubyforge.org/ - http://gems.github.com $ sudo gem install colors Successfully installed colors-0.0.4 1 gem installed Installing ri documentation for colors-0.0.4... Installing RDoc documentation for colors-0.0.4... $ irb >> require 'colors' => true >> puts "abc".hl *abc* #wyboldowane :)
Zatem działa! Biorąc pod uwagę fakt, że wszystko (łącznie z tym postem), zajęło mi mniej niż godzinę, zamierzam używać Gemcuttera!
Cyc-console
Ten post z pewnością powinien mieć dłuższy wstęp, który, mam nadzieję uda mi się napisać niebawem. Otóż z pewnością powinienem napisać więcej o ontologii Cyc, bo bez tego narzędzie, które tutaj promuję nie wydaje się zanadto przydatne.
Gdyby jednak znalazł się ktoś, kto zna tę ontologię i co więcej – korzystał z wbudowanej weń konsoli, z pewnością uzna, że jest ona rodem z epoki przedpotopowej. Nie wspiera ona wielu zasadniczych koncepcji znanych choćby z konsoli Linuksa, a nawet najprostszych klawiszy specjalnych, takich jak <Home> i <End>.
Pracując nieustannie z Rubim i jego świetną konsoląirb z dodatkiem Wirble, stwierdziłem, że
można by spróbować zrobić
podobną konsolę dla Cyc-a. Zasadniczo powinna ona posiadać następujące
własności:
- sensownie reagować na <Home>, <End>, strzałki itp.
- mieć wbudowaną historię poleceń
- wspierać kolorowane terminale
- sprawdzać ilość nawiasów, przed wysłaniem zapytania na serwer
- wspierać autouzupełnianie poleceń i symboli Cyc.
Jakiś czas temu zabrałem się za jej tworzenie i okazało się, że stworzenie takiej konsoli na bazie
irb nie jest specjalnie trudne. Oczywiście, to co powstało jest w fazie alfa – w obecnej chwili
nie ma np. autouzupełniania, ale jest już historia poleceń (zapamiętywana pomiędzy uruchomieniami),
no i wykorzystanie biblioteki readline pozwala używać klawisze specjalne w normalny sposób.
Dla wszystkich zainteresowanych – kod może być pobrany z github.com/apohllo/cyc-console lub z wykorzystaniem Rubygems:
$ sudo gem install apohllo-cyc-console
(Od wersji 0.0.5 wystarczy wpisać sudo gem install cyc-console).
W obu wypadkach wymagany jest gem apohllo-colors, o którym pisałem w poprzednim poście.
Język w SPARQL
Moja przygoda z SPARQL zacząłe się od DBpedia.org – sformalizowanej wersji Wikipedii.
Chciałem zrobić coś naprawdę prostego – wydobyć wszystkie pojęcia, która miałyby określoną wartość atrybuty rdfs:label.
Szybko jednak okazało się, że proste zapytanie, które powinno zwrócić obiekty o etykiecie ‘Berlin’:
SELECT * WHERE {
?x rdfs:label "Berlin"
}
nie daje żadnych sensownych rezultatów. Domyślałem się, że problem leży w tym, że predykat rdfs:label
może posiadać wiele wartości, dla wielu języków wykorzystywanych w Wikipedii. Niestety znalezienie
odpowiedzi na pytanie, jak określić język dla atrybuty nie było całkowicie banalne.
Chociaż informacja ta zawarta jest w opisie SPARQL,
na stronach w3.org niestety uszła mojej uwadze w trakcie pobieżnego czytania.
Dopiero dalsze googlowanie przywiodło mnie do następującego rozwiązania:
SELECT * WHERE {
?x rdfs:label "Berlin"@en
}
Określenie języka poprzez dodanie za łańcuchem znaków małpy oraz jego skrótu, dało pożądany rezultat, tzn. zwróciło wszystkie obiekty, których etykieta w języku angielskim to “Berlin”. Ku mojemu zdziwieniu, rezultatów jest wiele, o czy można przekonać się samemu.
ActiveRDF i dbpedia
Niech kod mówi sam za siebie :-)
require 'active_rdf' pool = ConnectionPool.add_data_source :type => :sparql, :url => "http://dbpedia.org/sparql", :results => :sparql_xml, :engine => :virtuoso Namespace.register(:dbpedia, 'http://dbpedia.org/') Query.new.select(:x).where(:x, RDFS::Resource.new('http://www.w3.org/2000/01/rdf-schema#label'), LocalizedString.new("Berlin","en")).execute
Pobieramy z DBpedii wszystkie zasoby, których etykieta w języku angielskim to “Berlin”
W dalszych przykładach zakładamy, że mamy już załadowany ActiveRDF oraz że dodaliśmy połączenia z DBpedią.
Query.new.distinct(:y).where(:x, RDFS::Resource.new('http://www.w3.org/2000/01/rdf-schema#label'), LocalizedString.new("Berlin","en")). where(:x, RDFS::Resource.new('http://www.w3.org/2000/01/rdf-schema#comment'), :y).lang(:y,"pl").execute
Tutaj pobieramy komentarze w języku polskim dla zasobów, których polską etykietą jest “Berlin”.
Poniższy przykład ilustruje proponowane rozszerzenie biblioteki ActiveRDF, które może zostanie uwzględnione w jej kolejnej wersji:
rs = Query.new.distinct(:x).where(:x, RDFS::Resource.new('http://www.w3.org/2000/01/rdf-schema#label'), LocalizedString.new("Berlin","en")).execute Query.new.distinct(:comment).where(rs[1], RDFS::Resource.new('http://www.w3.org/2000/01/rdf#comment', :comment).lang(:comment,"pl").execute
Zamiast ostatniego polecenia znacznie lepiej byłoby, gdybyśmy mogli napisać:
rs[1].comment{|query,object| query.lang(object,"pl")}
Pierwszy wpis
Coś małego na początek – colors – rozszerzenie klasy String o możliwość kolorowania. Działa oczywiście wyłącznie na terminalach zgodnych z ANSI, na pozostałych dostaniemy tekst wejściowy z dodatkiem “krzaków”.
Instalacja z wykorzystaniem rubygems jest banalna (pod warunkiem, że mamy wersję co najmniej 1.2.0 i github dodany do źródeł)
$ sudo gem install apohllo-colors
$ sudo gem install gemcutter $ sudo gem tumble $ sudo gem install colors
(Więcej o powodach przejścia na gemcutter. Upewnij się również, że masz Rubygems w wersji co najmniej 1.3.3)
Potem odpalamy konsolę irb i sprawdzamy czy wszystko działa jak trzeba:
require 'rubygems' require 'colors' "abc".hl #=> abc (wyboldowane, uwierzcie mi na słowo) "abc".hl(:red) #=> abc (czerwone, j.w. :) "ala ma kota ale nie ma psa".hl(:blue, "ma") #=> ala *ma* kota ale nie *ma* pas (wyróżnione słowa na niebiesko)
Sam w sobie projekt ten z pewnością nie robi dużego wrażenia, ale wykorzystuję go nagminnie. Każdy kto zetknął się z kolorowaną konsolą, wie jaka jest różnica pomiędzy jednolitym, czarnym, białym czy zielonym tekstem, a np. wyróżnionym znakiem zachęty.
Zapraszam do testowania.