aborg's profile.PhotosBlogGuestbookMore Tools Help

.

Ismeretlen ismerős
Public folders
Thanks for visiting!
Please wait...
Sorry, the comment you entered is too long. Please shorten it.
You didn't enter anything. Please try again.
Sorry, we can't add your comment right now. Please try again later.
To add a comment, you need permission from your parent. Ask for permission
Your parent has turned off comments.
Sorry, we can't delete your comment right now. Please try again later.
You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
Complete the security check below to finish leaving your comment.
The characters you type in the security check must match the characters in the picture or audio.

Gábor Bakos

Occupation
Interests
Nothing interesting
March 01

Az erőd ostroma

    Előzmény: Újabb lépcsőfokok
    Egy barátom (aki azóta PhD hallgató), még elsőévesként a következő problémába ütközött amikor C-ben kezdett el tanulni:

    Rossz eredményt adott egy (ehhez hasonló) programja:

#include <stdio.h>
int main(int argc, char ** argv)
{
    int x = 4;
    if (6 < x < 8)
    {
      puts("true");
    }
}

    Sajnos ez így kiírja azt, hogy true. Gondolom nem kell mondanom, hogy mi a gond. (Az első összehasonlítás eredménye a 0 szám, ami valóban kisebb mint 8.) Pedig milyen szép lenne így kifejezni a szándékunkat!
    Természetesen készült olyan programnyelv, amelyben az ehhez hasonló kifejezések helyesen értelmeződnek. Ilyen például a Fortress. Azonban a Scala igencsak támogatja a nyelv kiterjesztését, így megpróbálom ebben a bejegyzésben imitálni ezt a tulajdonságát a Fortressnek. Megpróbálom bevenni ezt az erődöt. :)
    Hogyan fogjunk hozzá? Először is jó lenne egy objektumba gyűjteni, hogy milyen volt a legutolsó összehasonlítás eredménye, valamint az összehasonlításban szereplő érték. Ha ez megvan, akkor szépen végigmehetünk a láncon egy ilyen objektummal. Igen ám, de ez nem lesz Boolean értékű! Át kell még alakítani majd azzá. Az igazi az lenne, ha az első objektummá átalakítást is meg lehetne oldani valahogy automatikusan. Mi az amit még jó lenne tudjon? Egyszerűsített kiértékelést: ha a kiértékelés egy pontján meghatározható a végeredmény, akkor ne folytatódjon a kiértékelés. Lehetőleg ilyen esetben a további kifejezések ne is hajtódjanak végre (hatékonyság, illetve mellékhatások minimalizálása miatt).
    Nézzük mire jutottam:

/** An object to extend the functionality of ordering. */
object MathRelations
{
  /** This class represents the starting point of the relation chain. */
  final class PreRelation[T <% Ordered[T]](wrapped: => T)
  {
    def <(that: => T): ChainedRelation[T] = new ChainedRelation(that, wrapped < that)
    def >(that: => T): ChainedRelation[T] = new ChainedRelation(that, wrapped > that)
    def <=(that: => T): ChainedRelation[T] = new ChainedRelation(that, wrapped <= that)
    def >=(that: => T): ChainedRelation[T] = new ChainedRelation(that, wrapped >= that)
  }


  /** This class is for the further points of the relation chain. */
  final class ChainedRelation[T <% Ordered[T]](wrapped: =>T, val isTrue: Boolean)
  {
    def <(that: => T): ChainedRelation[T] = new ChainedRelation(that, if (isTrue) (wrapped < that) else false)
    def >(that: => T): ChainedRelation[T] = new ChainedRelation(that, if (isTrue) (wrapped > that) else false)
    def <=(that: => T): ChainedRelation[T] = new ChainedRelation(that, if (isTrue) (wrapped <= that) else false)
    def >=(that: => T): ChainedRelation[T] = new ChainedRelation(that, if (isTrue) (wrapped >= that) else false)
    override def toString(): String = String.valueOf(isTrue);
  }


  /** Converts an Ordered object to a PreRelation. */
  def toPreRelation[T <% Ordered[T]](o: T) = new PreRelation(o)


  /** Converts a ChainedRelation to a Boolean value */
  implicit def toBoolean[T](r : ChainedRelation[T]) : Boolean = r.isTrue;
}

    Egyelőre ennyi. Mit is tud ez így valójában, mi ez a sok krix-krax? Nos, az ötlet adott volt, így a megvalósítás is viszonylag könnyen adódik. A PreRelation osztály típusparamétere egy olyan típus, mely ,,implicit módon'' átalakítható Ordereddé. Az egyetlen konstruktor paraméterét wrappednek neveztem el, ennek a típusa olyan, hogy mindig kiértékelődik (=>) az azt meghatározó kifejezés, valahányszor hivatkozunk a mezőre, az így kapott kifejezés típusa pedig megegyezik a PreRelation típusparaméterével (T).

    Definiáltam néhány metódust, melyek az összehasonlításhoz használt ChainedRelation objektumokat hozzák létre. Természetesen létre lehet hozni más relációs jelekhez is (például ==, subset, …). Egyelőre azonban ennyi elég lesz.

    Nézzük hogy fest a másik osztály! Ennek a neve ChainedRelation, ugyanolyan típusparamétere van, mint az elsőnek. Azonban ennek a korábbihoz hasonló wrapped paraméter mellett egy Boolean típusú, el is mentett, nem változtatható (val) mezője is van.

    A metódusok itt is hasonlóak. A megvalósítás kissé túlbonyolítottnak tűnhet, mivel elég lenne a második paraméternél isTrue && wrapped < that, jellegű megoldást alkalmazni. Azonban a Scala sajátosságai miatt, ekkor lefutna a thatet meghatározó kód, elvesztenénk egy fontos tulajdonságot.

    Definiáltam egy toString() metódust is, azonban erre nincs igazán szükség. A hibakereséshez azonban jól jöhet (vagy épp rosszul) a hiba jellegétől függően.

    Mi maradt még? Az előrelációt készítő metódus, illetve a Boolean értékké alakító. Előbbire még nem érdemes szavakat vesztegetni, utóbbi viszont kissé érdekesebb. Itt szerepel egy újabb kulcsszó, az implicit. Ezzel azt jelezhetjük, hogy ha ez a metódus látható, és van olyan objektumunk, ami a paraméterének megfelel, akkor fusson le, ha az adott környezetben az eredmény típusának megfelelő értékre van szükség (legalábbis nagyvonalakban). Ez még hasznos lehet majd. :)

    Ideje visszatérni a toPreRelation metódusra. Ez miért nem lehet implicit? Lenne értelme, hiszen így anélkül lehetne használni a most elkészített eljárásunkat, hogy bármit is írnunk kellene. Az elején be kellene importálni a MathRelations összes metódusát és ilyen szép dolgokat művelhetnénk. Nos, ennek az oka nem csak az, hogy bizonyos esetekben igen jelentős többletmunkát adnánk a processzornak (létre kell hoznia új objektumokat,… ) akkor is, ha erre nincs szükség (mert mondjuk nincs is igazi lánc), de az is, hogy ezt nem teszi lehetővé a Scala fordító (legalábbis jelenlegi változata):

  • illegal cyclic reference involving method toPrerelation
  • implicit method toPreRelation is not contractive, because the implicit parameter type (T) => Ordered[T] is not strictly contained in the signature (T) => <error>

    Nagyjából ennyi lenne a teendőnk. Azonban nem ártana kipróbálni is. Nézzük:

import MathRelations.{toBoolean, toPreRelation => |}
object testMathRelations extends Application
{
  def unPureMethod() : Float = {println("Called"); 2}
  def p(x: Boolean) {println(x)}
  p(|(4.0) <= 3 >= unPureMethod() >= 1)
}

    Mit mutat így? Először is elérhetővé tettük a metódusainkat, sőt a hosszú nevű toPreRelation metódust át is neveztük | jellé. Eztán készítettünk egy olyan metódust, aminek van mellékhatása, kiírja azt, hogy Called. Emellett visszaadja a 2 értéket is. A p metódus egy Boolean értéket ír ki. Ezt csak azért írtam, hogy véletlenül se a toString érték alapján írjon ki valamit a println. (Érdemes lehet kipróbálni, hogy mit ír ki a program, ha nincs felüldefiniálva a ChainedRelation.toString metódus és csak simán a println-t használjuk a kiíráshoz.)

    Nyugodtan ki lehet próbálni különféle értékekkel, hátha valahol hibázik a program.
    Bizonyára feltűnt, hogy figyelmeztetéseket adott a programunk amikor lefordítottuk a tesztet. Nos ez hiba. (Viszont ha az osztályainkat nem finalként deklaráljuk, akkor nem jelentkezik.)

    Összegzés: Nagyjából sikerült szimulálni a Fortress hasonló nyelvi elemét. Nem ugyanaz a kényelem, de ha valakinek tetszik, akkor használhatja és nem kell attól tartania, hogy olyan hibákba fut mint a C-s program esetében.

    Megint felmerül a kérdés, hogy merre tovább. Van egy programunk, amit nem ártana mondjuk tesztelni. Vagy valamerre másfelé kellene fordulni? Majd elválik. :)
    Addig is nem sokára megjelenik a Scala új változata is, mely néhány újabb nyelvi elemet hoz majd.


January 26

Újabb lépcsőfokok


    Az előző bejegyzés is egy kedvcsináló írás volt, ez is az lesz. Akkor egy nem teljesen elmagyarázott példát is bemutattam. Lássuk mi mit csinál?

object XmlHello extends Application {
  
val xml = (
     <!-- An xml with comment -->
     <hello who={java.util.Locale.getDefault().getDisplayCountry()} from="scala"></hello>
     <tag attribute="value">
       <hello who="dear reader" from="author"/>
     </tag>)

  for (nodes <- xml; //Selecting the nodes of xml (scala.xml.Elem).
        node <- nodes \\ "hello"; //Finding the nodes with label == "hello".
        who <- node attribute "who" //Selecting those which has "who" attribute.
  )

    println(node.label + " " + who) //Printing the label and the who attribute value.
}

    Azt, hogy ez egy futtatható program lesz, gondolom nem kellene megemlítenem sem. Létrehozunk egy XML dokumentumot, melyre mutató referenciát rögtön át is adjuk egy nem változtatható referencia tárolónak, ez lenne az amit xmlként azonosítottunk. A Java nyelvvel ellentétben itt nem lehet később értéket kapnia. Természetesen itt is lehetnek változó referencia tárolók, azokat a var kulcsszóval vezethetjük be. Az XML dokumentumot ()-ek közé zártuk (a típusa pedig scala.xml.NodeBuffer). Ez a fejlesztőkörnyezet hiányossága miatt volt szükséges, enélkül nem tudta volna lefordítani. Azonban egyébként is praktikus lehet kitenni a zárójelet, nehogy véletlenül csak az XML-es megjegyzés legyen az a dokumentum amit később használhatunk.
    Az első hello tag who attribútumában megfigyelhetjük, hogy egy Scala kifejezésből veszi fel az értékét. Ez éppenséggel lehetne Java kód is, így lehet az aktuális Locale országának szépen kinéző változatát megkapni. Természetesen itt sem kötelező kiírni a csomagnevet, lehet használni az import kulcsszót a későbbi gépelés megspórolásához, az olvashatóság javításához. (Azonban kissé bonyolultabb mint Java-ban, így erről igény esetén csak később. (Például lehetővé teszi, hogy átnevezéssel hivatkozzunk valamire kódunkban.))
    Azt hiszem az XML létrehozásáról ennyi elegendő volt. Nézzük a többit!
    Ott egy ismerős for
Azonban mintha túl sok minden lenne utána. Nos, Scala-ban nincs for ciklus. Az egyébként is helyettesíthető while ciklussal szükség esetén. Helyette a fort sokkal érdekesebben lehet használni. Be lehet járni vele különféle szekvenciákat, szűrni lehet azokból és akár az eredmény is lehet egy újabb szekvencia. Elég hasznos, kényelmes lehet, ha jól használja az ember.
    Először az XML dokumentum részein haladunk végig, ezekre később a nodes aktuális értékeivel hivatkozhatunk. A nodes egyes értékei a scala.xml.Comment, scala.xml.Elem
, scala.xml.Elem típusúak, (legspeciálisabb) közös ősük a scala.xml.Node absztrakt osztály. (Igen, a változónév és a megjegyzés is elég megtévesztő) A következő lépésben ezek közül kiválasztjuk azokat, melyek hello címkével rendelkeznek valahol (akár a belsejükben). Ezt a scala.xml.Node közvetlen ősében (scala.xml.NodeSeq - a név talán mégsem volt annyira megtévesztő) definiált \\(that: String): NodeSeq metódussal tehettük meg. Dehát nem is írtunk .-ot! Nos, igen. A Scala támogatja a metódusok olyan hívási módját is (amennyiben nulla, vagy egy paramétere van), amely olyan mintha operátorként használnánk. Így lényegében végtelen sok operátort használhatunk. És igen, használhatunk olyanmetódusneveket is, amelyeket Java esetén nem (de az adott futtatókörnyezet esetében igen). Így a \\ is használható. Amit ez csinál az hasonlít az XPath lekérdező nyelv hasonló műveletére: megkeresi az adott gyökérből kiindulva az összes illeszkedő scala.xml.Node-ot. Ezeknek a node azonosítót adtuk. A következő sorban ezek közül csak azokat választjuk ki, melyeknek van who attribútuma. Ehhez a scala.xml.Node attribute(key: String): Option[Seq[Node]] metódusát használtuk (most is pontok nélkül). A hozzá tartozó scaladoc egy kissé idejétmúlt, mivel azt írja ha nincs hozzá key attribútum, akkor nullt ad vissza. Szerencsére nem így van, mivel akkor a példánk sem működne. Amit visszaad az egy scala.Option. Ennek pontosan két leszármazottja van, a scala.Some (egy osztály) és a scala.None (egy egyke objektum). Scala fordítóval nem is lehetne több. (Azonban Java-t, vagy más nyelvet is használva ez már nincs így…) Ezt úgy érték el, hogy a scala.Option osztályt ellátták a sealed kulcsszóval, emiatt csak vele egy fordítási egységben (állományban) lévő osztályok terjeszthetik ki, ezek pedig csak a felsoroltak.
    Ami meglepő, hogy a scala.Option esetén is alkalmazható a for (a scala.None-okat kihagyja, míg a scala.Some-okat megtartja), így az értékeket a who változóban elhelyezhetjük.
    A végén már csak ki kell írnunk a node címkéjét, illetve a who változó értékét egy szóközzel elválasztva és meg is kapjuk a korábban leírt eredményt.

    Sok mindenről lehetne még írni (akár programokról (ScalaCheck, lift,
), további nyelvi elemekről (eseti osztályok, egzisztenciális típus paraméterek, generikusok, ), vagy akár elméleti alapokról (monádok, katamorfizmus, kategória elmélet, ), technológiákról (IDE használat, különböző platformokra fejlesztés, )), azonban úgy tűnik senki sem szeretné egy olyan irányba terelgetni ezt a bemutatást ami tetszene neki. A végén kénytelen leszek magam dönteni. :) (Esetleg hasznosabbak lennének Fortress-szel, vagy Nemerlével kapcsolatos bejegyzések?)
    Végül egy fejtörő: Mit csinál a következő programrészlet?
("" /: (1 to 20)) ( (_,_)._1 + "_" )  Nem egyszerű ugye? (Nos kissé csaltam is. Ennek nagy részéről még nem írtam, de az egyik levelező listán előjött megoldásként és megtetszett. A Perl kedvelők örömére idekerült. ;-))

Folytatás itt

January 20

Az első lépcsőfokok

Scala logo
    Az emberek folyamatosan keresik a leginkább megfelelő eszközt a problémák megoldásához. A programozás esetén a programozási nyelv kiválasztása egy örök vitatéma. Ez a bejegyzés nem fogja eldönteni ezt a vitát. Mindössze egy ,,új'' szereplőt mutat be. Ez a szereplő a Scala.
Nézzük mit nyújthat ez a nyelv?
  • Nyílt forrású, természetesen a hozzá tartozó könyvtár is.
  • Több platformon is képes futni. (A leginkább támogatott a JVM-es, de működik .NET-en is.)
  • Kompatibilitás (ezáltal rengeteg korábbi megoldást probléma nélkül használhatunk vele). De nem minden áron.
  • Objektumorientált megközelítés.
  • Egyszerűbb XML kezelés.
  • Funkcionális programozási lehetőség.
  • Generikus programozás, könyvtárak. (Bár kissé eltér a szemantikája a Java által követettől.)
  • Átgondolt tervek alapján készítették.
  • Rugalmas fejlesztési módszer (és néhány a hibára lehetőséget adó konstrukció elhagyása).
  • Jelzések (annotation) használata.
  • Tömör (például eseti osztályok (case class)).
  • Típusinformációk kikövetkeztetése.
  • Kötelező kiírni a felüldefiniálást (override) jelző kulcsszót (override).
  • Nagyszerű közösség. (Régi-új megoldásokkal. Igyekeznek mindenhonnan a legjobbakat ,,összelopkodni'')
Ennyit a marketingről... Mi az amitől mégsem annyira kerek ez az egész? (Szubjektív.)
  • A típust a változó neve után adhatjuk meg. (A Pascalt kedvelőknek gondolom épp ez tetszik. Az is igaz, hogy a típusinformációk kikövetkeztetése és a lehetőség, hogy a metódus hívások elől a .-ot, argumentumai körül a ()-et bizonyos esetekben elhagyhatjuk, így egy szeparátor jel mindenképp szükséges, az pedig, hogy így a típus a végére kerül végülis nem olyan nagy gond.)
  • A generikusok jelzésénél nem a <, > párost, hanem a [, ] párost használhatjuk.
  • A tömbindexelő operátor a ( ), nem pedig a [ ].
  • Mozgó célpont: a gyakori kiadások nem teljesen kompatibilisek a korábbiakkal. (Azért általában 1-2 kiadás idejére még bent marad elavultként (deprecated) mielőtt eltávolítanák.)
  • Viszonylag kicsi fejlesztői közösség.
  • A fejlesztő környezet(ek) még nem az igazi(ak). (Legalábbis aki már hozzászokott az Eclipse, Netbeans, ... tudású eszközökhöz ezt kevésnek találhatja.)
  • Operátorok használatának lehetősége. (Néha jól jön, de az implicit átalakításokkal (implicit conversion) együtt kissé kockázatosnak érzem.) Szerencsére van precedencia, illetve megadható, hogy jobb-asszociatív legyen.
  • Nem kötelező ellenőrzött kivételek (checked exception).
  • Az, hogy a ; nem kötelező nem igazán tudom megítélni mennyire zavaró. Néha jó, ám néha bosszantó hibák forrása is lehet.
    Összességében nem rossz nyelv. Nem egyszerű, de miután ráérez az ember a stílusára kedvelhetővé válik.
Kiknek szól ez a cikk(sorozat)? Nos, nem a többféle programozási nyelvet jól ismerőknek. Nekik valószínűleg az Ocaml, LISP, vagy a Haskell után nem sok újat mutathat. Inkább az objektumorientált fejlesztésben gyakorlottabbak a célközönség.

Alapok

    A bemutatott példákat az Eclipse-hez készült aktuális (jelenleg beta) kiterjesztés (plugin) segítségével fordítom, futtatom. (A Netbeans rajongók egy-két független kiterjesztés közül válogathatnak: ScalaBeans, Scala Editor (Erlybird)) Ehhez néhány segítség:
A telepítés a szokásos, a Help/Software Updates/Find and Install.../Search for new features to install/New Remote Site... részen az URL: http://lamp.epfl.ch/~mcdirmid/scala.update . Eztán már telepíthető, csak ki kell választanunk és el kell fogadnunk a licencet. A használatáról a  A futtatás szintén szokásos.
    Amennyiben nem eclipse-et használunk a próbálkozásainkhoz, akkor biztosan tetszeni fog a scala parancs. Ezzel nem csak parancsállományszerűen (script) futtathatjuk programunkat, de egy interaktív környezetben ki is próbálhatjuk a képességeit.

Az elmaradhatatlan világunkat üdvözlő program:

object Hello

{

  def main(args : Array[String]) : Unit = println("Hello world")

}


Vagy, ha ez jobban tetszik:

object HelloSimple extends Application

{

  println("Hello world!")

}
    Kezdőknek azt hiszem az utóbbi sokkal érthetőbb, különösen ha a bőbeszédű Java változatával hasonlítjuk össze. Kezdjük is ezzel. Az object kulcsszóval egyke (singleton) osztály egyetlen példányát és magát az osztályt is deklaráljuk. Az scala.Application egy osztálya a hozzácsomagolt könyvtárnak. (A scaladoc sajnos nem jön elő F2-re, azonban ha fölé megyünk egérrel megtudhatjuk, hogy mire is jó.) Ezzel lehet egyerűen futtatható programot készíteni. Mivel a scala nem tartalmaz statikus (static) metódusokat (method), emiatt minden ilyesmit az egyke objektumokkal kell megoldanunk.
    Ekkor nem kell a main metódust elkészítenünk, de ha nem tesszük (mint ahogy erre nincs is most szükség), akkor a parancssori paramétereket nem tudjuk felhasználni.
    A kiírást végző metódus a scala.Predef.println(x: Any), ami a scala.Console.println(x: Any), ami végül Java platformon a java.lang.System.out.println(Object x) metódust hívja meg.
    Nézzük akkor az első példát. Itt ugyanúgy egy egyke objektum létrehozásával kezdünk, mely alapértelmezésben a scala.ScalaObject kiterjesztését/implementálását jelenti. Hasonlóan a Java nyelvhez itt is egy String tömböt paraméterként kapó, visszatérési értékkel nem rendelkező main metódussal tudjuk jelezni, hogy mit szeretnénk majd futtatni. Gondolom kiderült, hogy ezek közül mi melyiknek felel meg (String tömg: Array[String], nincs visszatérési érték, de terminálhat (szándékunk szerint fog is): Unit). Eztán a metódus implementációját adjuk meg az = jel után. A println(x: Any) ugyanaz mint korábban, ; itt sem szükséges utána.

    Nézzünk egy feleslegesen túlbonyolított üdvözlő programot:

object XmlHello extends Application {

   val xml = (

     <!-- An xml with comment -->

     <hello who={java.util.Locale.getDefault().getDisplayCountry()} from="scala"></hello>

     <tag attribute="value">

       <hello who="dear reader" from="author"/>

     </tag>)

  for (nodes <- xml; //Selecting the nodes of xml (scala.xml.Elem).

        node <- nodes \\ "hello"; //Finding the nodes with label == "hello".

        who <- node attribute "who" //Selecting those which has "who" attribute.

  )

    println(node.label + " " + who) //Printing the label and the who attribute value.

}


    Amit nálam kiír az a következő:

hello Magyarország

hello dear reader

    Ennek a példának a részletesebb magyarázata azonban a következők részekre marad.
    Felmerülhet a kérdés, hogy mire is jó igazán? Nos erre -egy általános nyelv esetében természetesen- sok válasz adható. Egyes területekhez kapcsolódó speciális nyelvek kialakításától matematikai problémák formalizálásán és megoldásán át webes, adatbázisos alkalmazások készítésén keresztül szinte bármire. Kinek mihez van kedve?


Folytatás: itt.

 
This person's network is empty (or maybe they're keeping it private).