Come faccio a forzare la 'trasparenza referenziale' in questo programma?

1

Di seguito è riportato il programma python scritto per seguire la regola del pollice nella programmazione funzionale.

The simple rule of thumb is: if you can replace any expression, sub-expression or subroutine call with the return value of that expression, sub-expression or subroutine call anywhere in the program, without the program changing its meaning, then you have referential transparency. And what this means, practically speaking is that you can't have any I/O, can't have any mutable state, can't have any side-effects. In every expression, the value of the expression must depend solely on the values of the constituent parts of the expression. And in every subroutine call, the return value must depend solely on the arguments.

def identity(k):
    return k

def cube(k):
    return pow(k, 3)

def summation(n, term):
    total, k = 0, 1
    while k <= n:
        total, k = total + term(k), k + 1
    return total

def sum_naturals(n):
    return summation(n, identity)

def sum_cubes(n):
    return summation(n, cube)

if __name__ == '__main__':
    sum = sum_naturals(4)
    print(sum)

Nel programma sopra, gli oggetti puntati da total e k mantengono immutabilità di stato e I / O non è usato tranne nel codice del driver per confermare solo i risultati.

Come faccio ad applicare la "trasparenza referenziale" nel mio programma per tutte le espressioni / sottoespressioni utilizzate in questo programma?

    
posta overexchange 10.02.2015 - 05:16
fonte

1 risposta

5

Hai già detto nella tua domanda come puoi capire se il tuo programma è referenzialmente trasparente o meno: se puoi sostituire un'espressione con il suo valore, senza cambiare il significato del programma, è referenzialmente trasparente. Se non puoi farlo, non lo è.

Quindi proviamo a sostituire alcune espressioni con i loro valori!

nel tuo metodo summation , nella prima riga, imposta total su 0 . Quindi, se il tuo programma è referenzialmente trasparente, dovremmo essere in grado di sostituire qualsiasi occorrenza di total con 0 . Ad esempio, in questo modo:

def summation(n, term):
    total, k = 0, 1
    while k <= n:
        total, k = 0 + term(k), k + 1
    return total

o in questo modo:

def summation(n, term):
    total, k = 0, 1
    while k <= n:
        total, k = total + term(k), k + 1
    return 0

Lo stesso vale per k :

def summation(n, term):
    total, k = 0, 1
    while k <= n:
        total, k = total + term(k), 1 + 1
    return total

o qui:

def summation(n, term):
    total, k = 0, 1
    while k <= n:
        total, k = total + term(1), k + 1
    return total

o qui:

def summation(n, term):
    total, k = 0, 1
    while 1 <= n:
        total, k = total + term(k), k + 1
    return total

mettiamole insieme:

def summation(n, term):
    total, k = 0, 1
    while 1 <= n:
        total, k = 0 + term(1), 1 + 1
    return 0

Inoltre, qual è il valore di ritorno del ciclo while ? Il ciclo while non ha ha un valore di ritorno, non restituisce nulla. Secondo le regole della trasparenza referenziale, dovremmo essere in grado di sostituire qualsiasi espressione con il suo valore di ritorno senza cambiare il significato del programma, ergo, dovremmo essere in grado di sostituire l'intero ciclo while senza niente:

def summation(n, term):
    total, k = 0, 1
    return total

Di nuovo, se il tuo programma fosse referenzialmente trasparente, questo non dovrebbe cambiare il risultato.

Si noti che un ciclo semplicemente non può essere referenzialmente trasparente. La trasparenza referenziale può anche essere interpretata come "se faccio la stessa cosa, succede la stessa cosa". Ma in un ciclo, facciamo la stessa cosa più e più volte, tuttavia, ci aspettiamo che qualcosa di diverso accada ogni volta, o almeno una volta: il ciclo si ferma.

Ecco un esempio di come implementare summation senza interrompere la trasparenza referenziale:

def summation(n, term):
    if n == 0:
        return term(n)
    else:
        return term(n) + summation(n - 1, term)

Ed ecco un'implementazione in un modo ricorsivo di coda usando lo standard "introdurre un accumulatore":

def summation(n, term):
    def summation_rec(n, total):
        if n == 0:
            return total
        else:
            return summation_rec(n-1, total + term(n))
    return summation_rec(n, 0)

(Sfortunatamente, ciò non aiuta, dal momento che Python non implementa le opportune chiamate di coda, nemmeno la corretta ricorsione di coda. Quindi, questo continuerà a far esplodere lo stack per un% maggiore din. Ma in una lingua con la corretta implementazione di ricorsione di coda o anche chiamate di coda appropriate, questo verrà eseguito nello spazio di stack O (1).)

    
risposta data 10.02.2015 - 13:28
fonte