Postfix függvényhívás

Fórumok

Volt már szó róla kb. egy éve, hogy talán érdemes volna megcsinálni a postfix függvényhívást. Hát itt van. Két új szabályt vettem fel a Lemon nyelvtanba:

  
    expr ::= expr : : func ( parlist )
    expr ::= expr : : func         

A postfix függvényhívásból ugyanaz a kód generálódik, mint a nyíl jobb oldalán álló hagyományos függvényhívásból.

  
    x::func(a,b,c)  ->  func(x,a,b,c)
    x::func         ->  func(x)

A postfix függvényhívás operátora a '::'. Egy-karakteres operátor nem lehetséges, mert már minden szóbajöhető karakter foglalt ('.' a névtér határoló, ':' a metódushívás). De nem olyan rossz ez a '::', hasonlít a metódushíváshoz, de mégis meg lehet különböztetni.

A ::-tól balra levő akármilyen kifejezés (precedencia!) lesz a ::-tól jobbra álló akármilyen (pl. névtérből vett) függvény első paramétere, miközben az esetleges többi paraméter eggyel hátrébb csúszik. Mintha minden függvény az első argumentumának a metódusa volna.

Példa:


#define OGGNAME1(x) strtran(atail(split(x,dirsep())),".ogg","")
#define OGGNAME2(x) (x)::split(dirsep())::atail::strtran(".ogg","")

Ezek a makrók egy ogg fájl teljes specifikációjából kiveszik a path és kiterjesztés nélküli fájlnevet. (Persze más eszközök is volnának erre, ez csak egy példa.) A fájlspecifikációt felvágja a dirsep-eknél, a keletkező tömbből veszi az utolsó elemet, abból kihagyja az .ogg kiterjesztést. Az első makró hagyományos, a második postfix függvényhívással van megírva.

Ellene szól: új szintaktika, kicsivel hosszabb kód, ügyelni kell a precedenciára.

Mellette szól: balról jobbra olvasható kód, kevesebb távoli zárójelpár.

Megjegyzés: Természetesen a függvényeknek csak egy implementációjuk van, ami azonban kétféle szintaktikával is meghívható. A fordító kiegyenlíti a különbséget, ezért a hatékonyság is egyforma.

Hozzászólások

Van még egy érdekes lehetőség/következmény. Az alábbi kifejezések mintájára


    x:=x+y  <=> x+=y     
    x:=x*y  <=> x*=y     
    ...      
    x:=x::y <=> x::=y  <=>  x:=y(x)

Például


    x::str::alltrim::padl(10,"0")   //x nem vátozik
    x::=str::alltrim::padl(10,"0")  //x egyúttal megkapja az eredményt

Külön dolgozni kellett a kiértékelés és értékadás sorrendjéért


    x::=f::g::h  <=>  x:=h(g(f(x)))

Még nincs fenn a repóban.
--
CCC3

Hát, hogy csúnya-e, az azon múlik, hogy kinek mi tetszik.

Érdekelne, hogy ismer-e valaki hasonlót más nyelvekben.

Azóta még egyszer átdolgoztam az egészet, hogy a lehető legáltalánosabb legyen. Egy példa:


  x*=y+1  <=> x*=(y+1) 
          <=> x:=x*(y+1) //C-ben is, Clipperben is

Ennek mintájára


  x::=y+1 <=> x::=(y()+1) 
          <=> x:=x::(y()+1) 
          <=> x:=x::y+1
          <=> x:=y(x)+1

Itt az a lényeg, hogy a '::'-nek ugyanaz a precedenciája, mint a ':'-nek (magas), a '::='-nek pedig ugyanaz a precedenciája, mint a '*='-nek (alacsony).

Amikor a '::' nem közvetlenül a függvény baloldalán van, mert, pl. közben van a zárójel, akkor a függvényhívást jelölő () nem hagyható el.

A szabály legáltalánosabb formája a Lemon nyelvtanban:

expr ::= expr '::' expr

ahol a '::' jobboldalán álló kifejezést alkotó fa (a nyelvtani elemzőben ez egy fa) baloldali ágain haladva (tehát a fa bal szélén) valahol függvényhívásnak kell lennie.

Persze meg lehet lenni a '::=' operátor nélkül, de egy matematikus nem bírja megállni, hogy egy ilyen általánosítási lehetőséget ki ne használjon. Ha már van '::', akkor lennie kell '::='-nek is. Tehát továbbra is az a kérdés, hogy érdemes-e bevenni a postfix függvényhívást.

Beraktam a repóba, lehet próbálgatni.

Szerk:

Ez vajon mit ír ki?


  function main()
  local x:="1"
      ? x::(val()+20)::str::ltrim::padl(4,"0")

Megoldás:
0021
--
CCC3

A "C# 2008" könyv 14. fejezetében (486. oldal) látom a CCC postfix függvényhívás rokonát. C#-ben bővítő metódusoknak hívják a tüneményt.

Különbségek:

C#-ben a bővítő metódusokat spéci szintaktikával kell definiálni, viszont a közönséges metódushívással azonos szintaktikával lehet használni. A fordító a típusok vizsgálata alapján kitalálja, hogy mikor melyik eset áll fenn.

A CCC fordító nem vizsgálja a típusokat, ezért a használatkor van szükség spéci szintaktikára (a normális metódushívás : operátora helyett ::), viszont akármilyen függvényre működik.

A C#-es dolog akkor volna több egyszerű (forrás-)szöveg transzformációnál (ahogy a CCC-ben is van), ha használni lehetne interfész implementálására. 493. oldal, 1. bekezdés, 1. sor:

Ha az olvasó először találkozik a bővítő metódusokkal, akkor tekinthet azokra úgy, mint egy lehetőségre a típus ... publikus szerződésének kibővítéséhez.

Öt sorral lejjebb.

... a bővítő metódusok valójában egyáltalán nem bővítik a típus publikus szerződését.

(Megj. Elég sok ilyen van a könyvben. Amúgy a próbák mutatják, hogy a bővítő metódusokat a fordító nem veszi számításba, mikor az a kérdés, hogy a típus milyen interfészeket implementál.)

Emellett a CCC postfix függvényhívásnak van az a formája, ami értéket ad az lvalue-knak:


    x:="hopp"
    x::upper   //nagybetűre, x nem változik
    x::=upper  //nagybetűre, x magkapja az eredményt (hasonlóan, mint '+='-nél)

--
CCC3