Python & CSV, ma il tipo?

Chi usa Python sicuramente conosce il modulo csv: è molto comodo per leggere dei file in formato csv (comma-separated values). Finché i dati sono tutti di tipo stringa le cose sono abbastanza semplici:

with open(fname, newline='') as fi:
    reader = csv.reader(fi)
    for line in reader:
        # line e' una lista di stringhe

Addirittura se è presente l’header le cose si fanno ancora più semplici:

with open(fname, newline='') as fi:
    reader = csv.DictReader(fi)
    for line in reader:
        # line e' un dizionario avente come chiavi i nomi 
        # delle colonne presenti nella prima riga del file

Ma se volessimo, ad esempio, convertire la seconda colonna in int e la quarta in float? Col modulo csv questa cosa non è direttamente possibile, e saremmo costretti a scrivere una roba di questo tipo:

with open(fname, newline='') as fi:
    reader = csv.reader(fi)
    for line in reader:
        line[1] = int(line[1])
        line[3] = float(line[3])
        # line e' una lista di stringhe

Non so a voi, ma a me questo codice non piace proprio! Per questo motivo vi propongo un piccolo snippet di codice che faciliterà questo tedioso compito:

def csv_mapper(csv_reader, mapping=None):
    def id_func(x):
        return x

    if mapping is None:
        mapping = {}

    if hasattr(csv_reader, "fieldnames"):
        # csv_reader returns a dict
        for line in csv_reader:
            yield {name: mapping.setdefault(name, id_func)(value)
                   for name, value in line.items()}
    else:
        # csv_reader returns a sequence
        for line in csv_reader:
            yield [mapping.setdefault(index, id_func)(value)
                   for index, value in enumerate(line)]

Questo codice semplicemente riceve in input il reader ottenuto dal modulo csv e un dizionario contenente un mapping. Le chiavi del dizionario sono specificate come nomi se il reader restituisce dei dizionari (per cui i nomi sono noti), mentre interi indicanti le posizioni nel caso di un reader che ignora i nomi dei campi. L’esempio riportato sopra si può quindi riscrivere così:

with open(fname, newline='') as fi:
    reader = csv.reader(fi)
    for line in csv_mapper(reader, {1: int, 3: float}):
        # line e' una lista

Se supponiamo che la colonna 1 si chiami “num” e la colonna 3 “percentage”, per esempio:

with open(fname, newline='') as fi:
    reader = csv.DictReader(fi)
    for line in csv_mapper(reader, {'num': int, 
                                    'percentage': float}):
        # line e' un dizionario

HTH, alla prossima!

Annunci
Pubblicato in Informatica, Python | Contrassegnato , , | Lascia un commento

Come funziona einsum?

Oggi parleremo della funzione di Numpy einsum. La funzione, come dice il nome, segue la convenzione di somma definita da Albert Einstein. Questa convenzione elimina le sommatorie nelle formule sommando automaticamente sugli indici in comune negli elementi delle formule, ed è tipicamente usata nel calcolo vettoriale/matriciale/tensoriale. Ad esempio:

  • x = u \cdot v = \sum_{i=1}^{n} u_i \cdot v_i , detta comunemente prodotto scalare, si può esprimere semplicemente come x = u_i \cdot v_i
  • $latex la traccia di una matrice M (ovvero la somma degli elementi sulla diagonale principale) si può calcolare con tr(M) = M_{i,i}
  • il prodotto tra due matrici A e B (di dimensione rispettivamente m \times n e n \times p, si può calcolare come A_{i,j} B_{j,k}
  • il prodotto esterno di due vettori u, v si può calcolare come u_i v_j

Per altri utilizzi più complessi, si rimanda alla fonte Wikipedia.

In Numpy, la funzione einsum riceve i seguenti parametri:

einsum(subscripts, *operands, out=None, dtype=None, order=’K’, casting=’safe’)

  • subscripts, è una stringa contenente gli indici della somma;
  • operands, sono tutti gli array sui quali applicare le operazioni di somma;
  • out, indica, se presente, dove memorizzare i risultati del calcolo;
  • dtype indica il tipo dei risultati;
  • order, specifica se usare il row major order (‘C’), il column major order (‘F’), usare il column order se sono tutti in column order, row altrimenti (‘A’), o mantenere quello degli operandi (‘K’);
  • casting regola se e in che modo si possa fare il casting in caso di tipi diversi.

In questo piccolo post vedremo solo i primi due parametri, gli altri sono coperti ampiamente da molte guide su Numpy.

Iniziamo con un esempio pratico di come utilizzare einsum, ovvero il calcolo del prodotto scalare tra due vettori (facilmente realizzabile con numpy.dot). Supponiamo di aver importato numpy con l’alias np.

>>>a = np.arange(1,6)
>>>b = np.arange(6,11)
>>>np.dot(a,b)
130
>>>np.einsum('i,i', a, b)
130

Semplice! Come abbiamo menzionato sopra, un prodotto scalare non è altro che un prodotto elemento per elemento dei due vettori, infine sommato. Di conseguenza alla funzione diremo di moltiplicare gli elementi i-esimi (ma qualsiasi altro indice sarebbe stato corretto), e siccome l’etichetta i è ripetuta, la sommatoria sommerà su i.

Vediamo un esempio di prodotto matrice-vettore (sempre realizzabile con numpy.dot):
>>>a = np.arange(1,26).reshape((5,5))
>>>b = np.arange(6,11)
>>>np.dot(a,b)
array([130, 330, 530, 730, 930])
>>>np.einsum('ij,j', a, b)
array([130, 330, 530, 730, 930])

Il primo parametro della funzione einsum è una sequenza di etichette separate da virgole. Ogni sottosequenza si riferisce al corrispondente operando passato alla funzione, ed ogni lettera ad una dimensione dell’operando. Etichette uguali in operandi diversi sono automaticamente sommate. Nel primo esempio, abbiamo 'i,i', che si traduce con moltiplica a[i] con b[i], poi somma il risultato su i, mentre nel secondo si ha 'ij,j', ovvero moltiplica a[i,j] per b[j] e somma su j, mentre l’indice i, presente solo in uno dei due operandi, detta la dimensione del risultato, ovvero il numero di righe di a.

Ma non è finita qui! Oltre alla sequenza di etichette, si può specificare anche la dimensione dell’output, tramite una freccia stilizzata. Per chiarire come, usiamo einsum per sommare le righe o le colonne di una matrice. Ad esempio:
>>>a = np.arange(1,26).reshape((5,5))
>>>np.einsum("ij->i", a)
array([ 15, 40, 65, 90, 115])
>>>a.sum(axis=1)
array([ 15, 40, 65, 90, 115])
>>>np.einsum("ij->j", a)
array([55, 60, 65, 70, 75])
>>>a.sum(axis=0)
array([55, 60, 65, 70, 75])

La dicitura 'ij->i' significa al variare di i e j, somma gli elementi, ma solo su j. Il risultato conterrà un elemento per ogni valore che assume l’indice i. In pratica, gli indici che compaiono a destra della freccia non saranno sommati. Se si specifica solamente la freccia, sarà sommato tutto. Infatti un modo per sommare tutti gli elementi di una matrice a usando einsum è np.einsum('ij->', a).

Passiamo ora a cose più interessanti. Se io volessi moltiplicare il contenuto di un vettore per 2? Proviamo con einsum:
>>>a = np.arange(1,6)
>>>np.einsum('i,i->i', a, 2)
ValueError: einstein sum subscripts string contains too many subscripts for operand 1

Mmm… C’è qualcosa che non va! Il motivo è che einsum di default non fa broadcasting, per cui l’intero 2 non può essere iterato con un indice. Il modo più semplice per risolvere il problema sarebbe chiaramente fare a * 2, ma vediamo come si può fare con einsum:
>>>a = np.arange(1,6)
>>>np.einsum('...,...', a, 2)
array([ 2, 4, 6, 8, 10])

Cosa sono quei '...'? Chi conosce Numpy li avrà subito riconosciuti: sono delle ellissi, ovvero un modo per dire a Numpy di considerare tutte le dimensioni possibili. In questo caso Numpy capisce che il numero 2 va convertito in un array 1-D di dimensione 5, per poter effettuare l’operazione (è la stessa cosa che avviene sotto sotto in a * 2). Un modo equivalente di scrivere l’operazione è 'i,...->i', in cui si specifica esplicitamente l’asse su cui sommare. Nel caso di una matrice, è possibile combinare la somma per righe o colonne con la moltiplicazione per scalare:
>>>a = np.arange(1,26).reshape((5,5))
>>>np.array_equal(a.sum(axis=1) * 2, np.einsum('ij,...->i', a, 2)
True
>>>np.array_equal(a.sum(axis=0) * 2, np.einsum('ij,...->j', a, 2)
True

o moltiplicare banalmente tutta la matrice per 2:
>>>np.array_equal(a * 2, np.einsum('ij,...', a, 2)
True

La bellezza di einsum è la naturale generalizzazione a più argomenti, ad esempio se volessimo moltiplicare 3 array con un prodotto matriciale ci basta scrivere np.einsum('ij,jk,kl', a, b, c), invece che np.dot(a,np.dot(b,c)).

Conclusione: einsum è una funzione generale ma elegante, che riassume molte altre funzioni di Numpy, anche se all’inizio può sembrare un po’ ostica da imparare. Alla prossima!

Pubblicato in Informatica, Python | Contrassegnato , , | Lascia un commento

Riflessioni socio/culturali

Leggendo la pagina di Wikipedia sulla Sindrome di Asperger (della quale riporto il collegamento in basso per chi volesse approfondire), ho trovato questa frase:

“La scarsa empatia è probabilmente l’aspetto più disfunzionale della sindrome. I soggetti che ne sono affetti hanno sperimentato difficoltà in aspetti basilari dell’interazione sociale, come il mancato sviluppo di amicizie, o la condivisione di interessi con altri (per esempio, mostrare agli altri oggetti di proprio interesse), o un deficit di reciprocità sociale o emotiva e un ridotto utilizzo del linguaggio non verbale come il contatto visivo, le espressioni del viso, la postura e i gesti. Questo stato o condizione innata comporta problemi coi normali scambi sociali tra persone di pari livello.
Nell’infanzia e durante l’adolescenza ciò può causare seri problemi perché un bambino o un ragazzo ha difficoltà a decodificare i segnali impliciti su cui si reggono le interazioni sociali e potrà essere scartato dai coetanei, scontrandosi con una sorta di “crudeltà sociale”; potrà essere spesso considerato corresponsabile di questa crudeltà nei suoi confronti, sebbene egli non riesca a capire né il perché dell’ostilità né che cosa stia facendo di “sbagliato”. Recenti scoperte nel campo dell’educazione speciale hanno cercato di risolvere questo problema, ottenendo però scarsi risultati.”

Quello che mi ha colpito è proprio la locuzione “crudeltà sociale”, ossia le persone affette da questa sindrome sono scartate dalle altre perchè in un certo qual modo diverse dal loro standard comportamentale e sociale. Di chi è la colpa? Di una persona che non si vede accettata solo perché non segue un certo insieme di “dettami” non scritti e che nessuno gli comunica perché ritenuti “ovvii”? Questo mi porta a pensare che non sia il singolo individuo ad essere malato, ma l’intera società, nella quale alcune persone non si sentono al loro posto solo perché non vogliono essere schiave di leggi non scritte ma dettate dal “senso comune”, di leggi ritenute “assolute” ma in realtà palesemente soggettive e frutto di ignoranza. E intanto purtroppo molte persone dalla psicologia fragile subiscono passivamente situazioni di questo tipo, arrivando ad incolparsi anche per colpe che non hanno. E’ davvero questo il mondo in cui viviamo? Se lo è, mi fa schifo.

Link: http://it.wikipedia.org/wiki/Sindrome_di_Asperger

Pubblicato in Misc | Lascia un commento

Min-Conflicts vs N-Regine

Recentemente mi sono appassionato all’intelligenza artificiale, e una parte che mi ha affascinato è quella del CSP, ovvero “Constraint satisfaction problems” (Problemi di soddisfacimento di vincoli). Un’istanza di un problema P di tipo CSP consiste in un insieme di variabili, ciascuna delle quali è definita su un dominio di valori, e da un insieme di vincoli. Una soluzione per P è un assegnamento di un valore a ciascuna variabile (ovviamente contenuto nel suo dominio), il quale rispetta tutti i vincoli.

Ad esempio, supponiamo di voler trovare due valori x e y reali tali che x+y=1. Una formulazione di questo problema in CSP è la seguente:

  • Insieme delle variabili = \{x,y\}
  • Dominio(x) = \mathbb{R}, Dominio(y) = \mathbb{R}
  • Vincoli: x+y=1

Una soluzione del problema potrebbe essere \{x=0, y=1\} ma come è noto dall’algebra lineare questo problema ammette infinite soluzioni valide.

Un problema che è spesso utilizzato per testare gli algoritmi di problem solving è quello delle n-regine, ovvero il problema di posizionare n regine su di una scacchiera n \times n in modo che nessuna regina sia sotto attacco da parte di nessun’altra. (Una regina può attaccare in orizzontale, in verticale e in diagonale secondo le regole degli scacchi). Una sua formulazione in CSP è la seguente:

  • Definiamo una variabile per ogni colonna (poiché può andare solo una regina per colonna è legittimo supporre che ci sia una variabile per colonna);
  • Ogni variabile ha dominio [1,8], e il suo valore indica la riga in cui la regina associata a quella colonna è posizionata;
  • I vincoli sono imposti dal regolamento degli scacchi: ogni regina non dev’essere sotto attacco.

Traduciamo i vincoli in una funzione Python che controlla se due posizioni si attaccano a vicenda in una scacchiera:

def attacks(pos_1, pos_2):
    return (pos_1[0] == pos_2[0] or
            pos_1[1] == pos_2[1] or
            abs(pos_1[0] - pos_2[0]) == abs(pos_1[1] - pos_2[1]))

La prima condizione dice che due regine si attaccano se stanno sulla stessa riga, la seconda che esse si attaccano se stanno sulla stessa colonna. La terza è un po’ meno intuitiva ma indica che due regine che stanno sulla stessa diagonale si attaccano. Se due regine in posizione (r1,c1) e (r2,c2) stanno sulla stessa diagonale in direzione NO-SE, allora vale la relazione r2-r1 = c2-c1, mentre per la diagonale NE-SO vale la relazione r2-r1=c1-c2 (provare per credere!).

Come algoritmo per la risoluzione, ho scelto di utilizzare il cosiddetto Min-Conflicts (http://en.wikipedia.org/wiki/Min-conflicts_algorithm). Esso consiste in un concetto molto semplice, ma altrettanto potente: minimizzare ad ogni iterazione il numero di conflitti (ovvero il numero di vincoli violati). L’algoritmo è del tipo hill-climbing, ovvero fa parte della famiglia degli algoritmi che tendono a ricercare una soluzione in modo locale, migliorando via via la situazione in cui si trovano alterandone una o più parti. Questi algoritmi sono tipicamente molto veloci, ma risentono molto della presenza di minimi locali, spesso non riuscendone ad uscire. Per fortuna il problema delle n-regine presenta uno spazio delle soluzioni abbastanza denso (ovvero a partire da ogni configurazione è possibile raggiungere in pochi passi una soluzione valida) da consentirne un’esplorazione agevole tramite questo tipo di algoritmi. Uno schema ad alto livello del suo funzionamento può essere:

  1. Genera un assegnamento a;
  2. Se a non genera conflitti, allora return a;
  3. Sia una variabile scelta a caso che genera conflitti;
  4. Sia val il valore da assegnare a v che minimizza il numero di conflitti;
  5. Imposta val in a;
  6. Vai al passo 2;

Nel caso del problema delle n-regine, avremo che un assegnamento sarà rappresentato da una lista di n interi nel range 1-n, uno per colonna, indicante la riga alla quale la regina si trova. Per esempio all’assegnamento [5,8,6,4,2,3,1,7] corrisponde la seguente configurazione:

board

Notiamo che compare esattamente una regina per ogni colonna, ma la configurazione non è un assegnamento che soddisfa tutti i vincoli, infatti la regina in posizione (2,5) è attaccata (e viceversa può attaccare) quella in posizione (3,6), e questo genera due conflitti. Se si osserva la scacchiera si possono contare 6 conflitti, generati da 3 coppie di regine che si attaccano. Per giungere ad una soluzione è necessario scambiare di posto due regine fino a quando non si ottiene una configurazione ammissibile. Nel nostro caso sono sufficienti due scambi:

  1. [5, 8, 6, 4, 2, 3, 1, 7] -> [5, 8, 4, 6, 2, 3, 1, 7] (scambio la regina alla colonna 3 con quella alla colonna 4)
  2. [5, 8, 4, 6, 2, 3, 1, 7] -> [5, 2, 4, 6, 8, 3, 1, 7] (scambio la regina alla colonna 2 con quella alla colonna 5)

La soluzione ottenuta, [5, 2, 4, 6, 8, 3, 1, 7], presenta 0 conflitti, e quindi è corretta.

Passiamo ad esaminare l’implementazione dell’algoritmo di Min Conflicts in Python 3.3:

def min_conflicts(N, max_passi=100000):
    current = start(N)
    last = None
    for it in range(max_passi):
        var = find_conflict_var(current, N, last)
        last = var
        if var is None:
            return current, it
        val = select_value(var, current, N)
        set_value(current, var, val)
    return None, max_passi

La funzione min_conflicts riceve un numero n e un numero massimo di iterazioni da eseguire. Sono presenti alcune funzioni generiche, ovvero start, find_conflict_var, select_value set_value. Esse sono specifiche del problema affrontato. Ad ogni iterazione la variabile current contiene l’assegnamento che è correntemente esaminato dall’algoritmo. All’inizio esso viene calcolato in modo greedy cercando di soddisfare più vincoli possibile in modo veloce, inserendo una regina per colonna dove essa sembra provocare meno conflitti a livello locale (in seguito è illustrata meglio la funzione start). Ad ogni iterazione viene calcolata una variabile var che causa conflitti. Se non ne esiste una, allora viene restituito None e l’algoritmo termina (se non è possibile trovare una variabile che causa conflitti allora non ci sono conflitti e l’assegnamento è valido, ergo abbiamo finito!). Se ci troviamo nel primo caso allora si procede a selezionare il valore che causa il minimo numero di conflitti tramite la funzione select_value e ad assegnare il suddetto valore alla variabile tramite set_value. E’ usata una variabile last che contiene la variabile modificata al passo precedente, serve ad evitare che venga modificata la stessa variabile in due iterazioni successive (utile per risparmiare iterazioni all’algoritmo cambiando più volte la stessa variabile).

Esaminiamo adesso la funzione find_conflict_var:

def find_conflict_var(sol, N, last):
    def is_in_conflict(v):
        r = sol[v - 1]
        return any(v != c1 and attacks((r, v), (r1, c1))
            for c1, r1 in enumerate(sol, start=1))

    pool = list(range(1, N + 1))
    if last is not None:
        pool.remove(last)
    while pool:
        v = random.choice(pool)
        if is_in_conflict(v):
            return v
        pool.remove(v)
    if last and is_in_conflict(last):
        return last

La funzione riceve la soluzione corrente (sol), la dimensione n della scacchiera e la variabile last specificata in precedenza. Innanzitutto viene dichiarata una piccola funzione locale is_in_conflict che, data una variabile v, verifica se essa effettivamente genera conflitti sfruttando la funzione attacks riportata in precedenza. Si genera un pool di variabili candidate, dal quale eventualmente si rimuove last se presente, e si estrae una variabile a caso dal pool. Se essa genera conflitti allora essa viene restituita, altrimenti essa viene rimossa dal pool e si prosegue. Se infine non è stata trovata alcuna variabile in conflitto e last risulta essere l’unica variabile che genera conflitti allora essa viene restituita (serve per garantire la correttezza dell’algoritmo). Se non vale nemmeno questa condizione la funzione termina (e implicitamente restituisce None per la semantica di Python).

La seconda funzione esaminata è select_value:

def num_of_conflicts(sol, var):
    row = sol[var - 1]
    return sum(var != c1 and attacks((row, var), (r1, c1))
               for c1, r1 in enumerate(sol, start=1))

def select_value(var, current, N):
    conflicts_vec = []
    current_o = current[:]
    for val in range(1, N + 1):
        set_value(current_o, var, val)
        conflicts_vec.append(num_of_conflicts(current_o, var))
        current_o = current[:]
    min_c = min(conflicts_vec)
    values = [i for (i, v) in enumerate(conflicts_vec, start=1) 
              if v == min_c]
    return random.choice(values)

La funzione riceve la variabile della quale calcolare il valore, l’assegnamento corrente e la dimensione n. Si prova ogni possibile valore per la variabile, calcolando di volta in volta il numero di conflitti che il nuovo valore genera. In seguito viene calcolato il minimo tra tutti questi valori di conflitti e si sceglie uno a caso tra i valori che generano il numero minimo di conflitti (in linea di principio i valori candidati potrebbero essere più di uno).

La funzione set_value non si occupa solamente di impostare la nuova riga della regina nella colonna var value, ma sposta anche la regina che prima si trovava alla riga value in quella precedentemente occupata nella colonna var (si, è complicato a dirsi ma non a farsi!). Per esempio, se ho l’assegnamento [5, 8, 6, 4, 2, 3, 1, 7] e var = 3, value = 4, spostando la regina nella colonna 3 alla riga 4, l’assegnamento [5, 8, 4, 4, 2, 3, 1, 7] presenterà un conflitto dovuto alla presenza di due regine alla riga 4. La soluzione più semplice è spostare la regina che in precedenza era alla riga 4 (quella in colonna 4) alla riga in cui la regina della colonna 3 si trovava in precedenza (nel nostro caso 6), ottenendo [5, 8, 4, 6, 2, 3, 1, 7] ed eliminando il conflitto.

Infine, osserviamo la funzione start e la tecnica greedy che utilizza per generare il primo assegnamento:

def start(N):
    def get_menaces(sol, row, col):
        return sum(attacks((r, c), (row, col))
                   for c, r in enumerate(sol, start=1))

    assignment = []
    pool = set()
    n = 1
    while n < N:
        els = [(col, get_menaces(assignment, col, n)) 
               for col in range(1, N + 1)]
        min_val = min(els, key=operator.itemgetter(1))[1]
        min_rows = {el[0] for el in els if el[1] == min_val}
        min_rows.difference_update(pool)
        if min_rows:
            element = random.sample(min_rows, 1)[0]
        else:
            els = set(range(1, N + 1)) - pool
            element = random.sample(els, 1)[0]
        assignment.append(element)
        pool.add(element)
        n += 1

    last_element = (set(range(1, N + 1)) - pool).pop()
    assignment.append(last_element)
    return assignment

La funzione mantiene nella lista assignment l’assegnamento calcolato, e in pool tutte le righe già assegnate. Ad ogni iterazione si sceglie il valore che genera meno conflitti con le regine già posizionate in precedenza e non assegnato in precedenza, scegliendo a caso se si ha una parità tra più valori. Se tutti i suddetti valori non sono validi (già presenti in pool) allora si sceglie un elemento a caso non presente in pool come riga in cui inserire la regina. Questo algoritmo non è chiaramente sempre ottimale, ma consente di generare un buon assegnamento dal quale far partire l’algoritmo (ovvero con un numero relativamente basso di conflitti).

Il codice completo è riportato in https://gist.github.com/DavideCanton/8945058.

Mettiamo alla prova l’algoritmo con il classico problema delle 8 regine:

$ python3 -m timeit -n 10 -s "from queens_csp import main" "main(8)"
...
10 loops, best of 3: 13.8 msec per loop

Niente male! Proviamo adesso con N = 50:

$ python3 -m timeit -n 10 -s "from queens_csp import main" "main(50)"
...
10 loops, best of 3: 6.66 sec per loop

Un buon risultato, non c’è che dire! Sicuramente è possibile fare di meglio con una funzione generatrice di soluzioni iniziali più accurata, per diminuire il numero di iterazioni complessive, con qualche modifica delle strutture dati utilizzate e con l’introduzione della memoizzazione.
Se avete suggerimenti per migliorare il codice, non esitate a proporre!

Pubblicato in Intelligenza Artificiale, Python | Contrassegnato , , , , , , , , , , , , , | Lascia un commento

Privacy su Facebook

Ciao a tutti! Oggi vedremo un elemento che sta a cuore a moltissimi utilizzatori di Facebook, il famoso social network, ovvero la privacy dei post pubblicati. Come tutti sappiamo, in alto a destra in ogni post che mettiamo è presente un simbolo indicante la privacy del post, che può cadere in queste categorie:

prVediamo le quattro alternative che Facebook offre per proteggere i nostri post:

  • Pubblica, la prima delle 4 alternative è anche la più pericolosa, in quanto questo livello di visibilità consente a tutti di vederle, e non solo, infatti Facebook consente a chiunque di associarle a tepotranno essere visualizzate se qualcuno effettua una ricerca su Facebook o su un motore di ricerca pubblica, saranno accessibili per i giochi integrati in Facebook, le applicazioni e i siti Web utilizzati da te e dai tuoi amici e saranno accessibili a chiunque utilizzi le API di Facebook. E non è finita qui! Quando gli altri condividono informazioni su di te, possono anche scegliere di renderle pubbliche! Quindi il mio suggerimento è di evitare come la peste questa modalità di privacy, in quanto permette a chiunque di fare quello che volete con i vostri dati (fonte Normativa sull’utilizzo dei dati | Facebook);
  • Amici, la seconda alternativa si spiega da sè, ovvero solamente gli amici possono accedere ai contenuti con questo livello di visibilità;
  • Solo io, ovvero solo il proprietario del post vi può accedere, nessun altro è in grado di visualizzarlo;
  • Personalizzata, che vi consente un controllo a grana più fine su chi può vedere e chi non può vedere il post, infatti selezionandola si aprirà un’altra finestra che vi consentirà di scegliere i destinatari del post (molto utile a mio avviso!).

Il mio consiglio personale è: tenete il livello di visibilità Amici quanto più potete, nel caso personalizzate i destinatari mediante la quarta opzione, ma non usate mai il livello Pubblico a meno che non sappiate esattamente quello che volete che sia fatto con i vostri post.

Alla prossima!

Pubblicato in Misc | Contrassegnato , , | Lascia un commento

Android – impostazioni con summary

Ciao a tutti! Chi programma in Android sa che sviluppare l’Activity per le impostazioni può essere abbastanza noioso. Soprattutto se si vuole inserire un breve summary sotto l’impostazione per visualizzare il valore corrente dell’impostazione, come ad esempio:

Screenshot_2013-04-24-15-24-17

Per avere un risultato simile, basta semplicemente estendere la classe che riguarda la Preference in questione (in questo caso le prime 2 sono EditTextPreference mentre la terza è una ListPreference). La prima classe che vediamo è MyEditTextPreference:

import android.content.*;
import android.preference.*;
import android.util.*;

public class MyEditTextPreference extends EditTextPreference
{
        // Implemento tutti i costruttori
	public MyEditTextPreference(Context context)
	{
		super(context);
	}

	public MyEditTextPreference(Context context, AttributeSet attrs)
	{
		super(context, attrs);
	}

	public MyEditTextPreference(Context context, AttributeSet attrs, int def)
	{
		super(context, attrs, def);
	}

	@Override
        // Ogniqualvolta viene modificato il testo, aggiorna anche il summary.
	public void setText(String text)
	{
		super.setText(text);
		setSummary(text);
	}
}

Come potete vedere, è molto semplice, e per utilizzarla basta usare questa classe al posto di EditTextPreference nella creazione della Preference o, se la si dichiara da XML, indicare il nome completo di package relativo alla classe (nell’esempio com.glennhk.mypref), come nel seguente XML di esempio:

<com.glennhk.mypref.MyEditTextPreference
            android:defaultValue="@string/default"
            android:key="wheel"
            android:persistent="true"
            android:title="@string/pref_title" />

La classe MyListPrefence è analoga alla precedente:

import android.content.*;
import android.preference.*;
import android.util.*;

public class MyListPreference extends ListPreference
{
	public MyListPreference(Context context)
	{
		super(context);
	}

	public MyListPreference(Context context, AttributeSet attrs)
	{
		super(context, attrs);
	}

	@Override
	public void setValue(String value)
	{
		super.setValue(value);
		setSummary(value);
	}
}

L’unica differenza è nel fatto che il metodo si chiama setValue e non setText.

Pubblicato in Android, Informatica | Contrassegnato , , , , | Lascia un commento

Lo Zen di Python

Ciao a tutti! Appassionato di Python quale sono io, non poteva che essere tra i primi articoli del blog uno riguardante lo Zen di Python! Questo semplice componimento, realizzato da Tim Peters, recita i principi chiave che il codice Python dovrebbe rispettare, una sorta di “testo sacro” per i pythonisti (come me :D). Riportiamo questo famoso Zen adesso:

pergamena

L’immagine (realizzata da me), rappresenta in stile “medievale” lo Zen di Python. Discutiamo ora riga per riga i suoi principi:

  • Beautiful is better than ugly (bello è meglio di brutto): ovviamente si! Siamo tutti d’accordo che se un codice è più bello è anche meno pesante da leggere e da capire;
  • Explicit is better than implicit (esplicito è meglio di implicito): se il tuo codice dev’essere letto da altre persone sicuramente sì, inoltre il codice più esplicito è più aperto al cambiamento, soprattutto se si usano spesso list comprehension e roba simile (anche se per il codegolfing non fa bene :D);
  • Simple is better than complex. Complex is better than complicated (semplice è meglio di complesso, complesso è migliore di complicato): chiaramente ci riferiamo al principio KISS (Keep It Simple Stupid!), o al Rasoio Di Occam (e chi più ne ha più ne metta), insomma, non complicatevi la vita!
  • Flat is better than nested (meglio piatto di innestato): beh, qua c’è da vedere caso per caso, in genere è da evitare un’indentazione eccessiva, specie se si segue la larghezza rigida di indentazione della PEP8, anche per motivi di leggibilità;
  • Sparse is better than dense (sparso è meglio di denso): chiaramente, il codice sparso e diviso in blocchi, oltre a lasciare più spazio per i commenti, è molto più leggibile (specie se i blocchi sono organizzati secondo uno schema logico preciso);
  • Readability counts (la leggibilità conta): certamente, la leggibilità è un fattore fondamentale per lo sviluppo di codice che dev’essere letto da altre persone!
  • Special cases aren’t special enough to break the rules although practicality beats purity (i casi speciali non sono speciali abbastanza da infrangere le regole anche se la praticitità sconfigge la purezza): con questo sono d’accordo solo in parte, in quanto la specialità dei casi è molto soggettiva. Certo che Python è stato scritto per avere le “batterie incluse”, fornendo quanti più mezzi possibile per standardizzare tutti i casi, rendendoli di fatto casi generali e non speciali. Se proprio si ha un caso speciale non gestibile in modo standard, allora bisogna documentare chiaramente dove e perché non si sono seguite le regole. Ovviamente la praticità è preferita alla purezza perchè, generalmente, è più semplice e più facilmente comprensibile;
  • Errors should never pass silently. Unless explicitly silenced (gli errori non devono mai essere ignorati, a meno che non siano da ignorare esplicitamente): pienamente d’accordo, gli errori non devono essere ignorati ma gestiti (a meno che non siano previsti e ignorati per motivi validi);
  • In the face of ambiguity, refuse the temptation to guess (di fronte all’ambiguità, fuggi la tentazione di indovinare): ovviamente questa è informatica basilare, se c’è qualche sorgente di ambiguità non si può provare “a caso” ma si deve sciogliere l’ambiguità (quando possibile!);
  • There should be one and preferably only one obvious way to do it. Although that way may not be obvious at first unless you’re Dutch (ci dovrebbe essere uno e preferibilmente un solo ovvio modo per farlo, anche se quel modo potrebbe non essere ovvio a prima vista a meno che tu non sia olandese): magari fosse sempre così! Un problema ha spesso molte soluzioni possibili, che vanno valutate attentamente prima di sceglierne una. (NDR Il riferimento all’Olanda sinceramente non l’ho capito, forse un tributo a Dijkstra?);
  • Now is better than never. Although never is often better than *right* now (ora è meglio di mai, anche se mai è spesso meglio di subito): questa è una lezione di vita non di informatica! Ovviamente se una cosa la puoi fare falla, ma non farla di fretta! Ragionaci su e poi inizia a farla;
  • If the implementation is hard to explain, it’s a bad idea. If the implementation is easy to explain, it may be a good idea. (se l’implementazione è difficile da spiegare è una cattiva idea, se è facile da spiegare potrebbe essere una buona idea): come prima, preferire la semplicità alla complessità, ma non è detto che una soluzione semplice sia sempre buona! 😉
  • Namespaces are one honking great idea let’s do more of those! (i namespaces sono una buona idea, facciamone di più!): ovviamente separare il codice in namespaces è all’insegna dell’ingegneria del software, infatti consente di isolare il codice e di non creare molte dipendenze;

Alla fine, lo Zen di Python mi trova sostanzialmente d’accordo, sono regole sensate e che di solito cerco di rispettare il più possibile. Spero lo facciano anche gli altri :D, voi che ne dite?

Pubblicato in Python | Contrassegnato , , , | 1 commento