wtorek, 17 czerwca 2014

Słownik argumentów

W pewnej pythonowej bibliotece była pewna funkcja, która akceptowała dowolnie nazwane argumenty, a potem zapisywała je jako atrybuty znacznika HTML.

Coś w tym rodzaju:

def  tag( t, **keys ):
    print( "<" + t, end='' )
    for k in keys:
        print( ' ' + k + '="' + str(keys[k]) + '"', end = '' )
    print( "/>" )

Z rozpędu próbowałem ostatnio wywołać ją w taki sposób:

   tag( "circle", cx=100, cy=20, fill-opacity=0.6 )

To oczywiście nie działa, bo - cytuję:

>>> tag( "circle", cx=100, cy=90, fill-opacity=0.9 )
  File "", line 1
SyntaxError: keyword can't be an expression

Trzeba to zrobić tak:

tag( "circle", cx=100, cy=90, **{"fill-opacity":0.9} )
Używamy tu pythonowego zapisu służącego do przekazywania nazwanych argumentów przez słownik.

Pojedyncza gwiazka służy do przekazywania argumentów przez listę. Przekazywanie dowolnej liczby argumentów za pomocą tablicy nie jest niczym nowym. W języku C można to robić używając makrodefinicji va_start, va_list i pokrewnych, ale nie jest to elastyczny mechanizm. W JavaScript jest już lepiej - mamy apply() i call() w duchu lispowym. Cała idea pojawia się zresztą wyraźnie w sławnej SICP , ale, co ciekawe w oryginalnym interpreterze Johna McCarthy'ego - nie pojawia się wcale, ten pierwszy interpreter Lispu oparty był na eval.

To, co mamy w Pythonie i Javascripcie jest bardziej elastyczne i estetyczne - lista (słownik) argumentów może być zbudowana niezależnie, jako obiekt pierwszej klasy. W C to niemożliwe - musi ona pochodzić z wywołania naszej funkcji. Ale przynajmniej można ją przekazać dalej - robi się to tak:

#include 
#include 

void somefun( const char *fmt, ... )
{
        va_list  ap;
        va_start( ap, fmt );
        vprintf( fmt, ap );
        va_end( ap );
}

Brak komentarzy: