Beh, non sono un esperto, ma come studente sono curioso delle lingue e dei loro modelli / obiettivi di design.
Mi piacerebbe sapere se ci sono dei punti che mi mancano nei seguenti esempi e perché tecniche come questa non sono ampiamente usate nelle lingue popolari. Perché è un'idea migliore in un programma di vita reale definire esplicitamente tutti i test unitari, invece di usare un codice dichiarativo. perché le lingue non usano alcune dichiarazioni come test e implementazione in generale? Perché dividere queste cose in due parti?
Supponiamo di avere i seguenti test unitari:
assertRaises(avg([]), ValueError)
assertEquals(avg([1], 1)
assertEquals(avg([1, -2]), -.5)
assertEquals(avg([1, 2]), .5)
Se prendi questo pseudocodice,
def avg(items):
if len(items) == 0:
raise ValueError
else if len(items) == 1:
return items[0]
else:
return sum(items)/len(items)
la maggior parte delle parti di avg
sembra non fare nulla, o molto poco più che dichiarare solo il modo di superare alcuni test unitari. In altre parole, per me sembra un po 'come la duplicazione del codice. Soprattutto il primo test sembra essere superfluo in questo esempio.
Mi chiedo se sarebbe un buon progetto sbarazzarsi di alcuni test unitari in questo modo:
def avg(items) {
len(items) == 0 => raises ValueError
len(items) == 1 => items[0]
len(items) > 1 => sum(items)/len(items)
(test) items == [1, -2] => $avg == -.5
(test) items == [1, 2] => $avg == .5
(test) avg(items) <= sum(items)
}
In questo esempio, (test) indica quando uno specifico caso di test non è una definizione utile (anche se forse questi potrebbero essere utilizzati anche per l'ottimizzazione in rari casi). Ovviamente, questo pseudocodice non è ben progettato perché è meno leggibile rispetto alla prima implementazione, ma penso che sia un modo più conveniente per dichiarare i test unitari. In realtà i test unitari sembrano essere sbagliati qui, dal momento che non fanno altro che testare la definizione. E l'ultimo test non è banale da eseguire. Ma può essere eseguito ad esempio, quando viene eseguito un altro test, che verifica un codice che contiene avg
in esso, quindi è probabile che fornisca un utile set di parametri di test. Inoltre, ad esempio, anche l'ultimo test unitario ha un ruolo secondario! Se ho un codice come questo:
if avg(a) <= sum(a)
Non è affatto necessario valutare la definizione complessa, poiché in fase di esecuzione uno dei casi di test lo implica direttamente. OK, in questo caso, non è una vera ottimizzazione (anche se un IDE potrebbe avvisare l'utente, che l'espressione sarà sempre vera, che è utile). Tuttavia, posso immaginare, che ci sono molti esempi complessi, che possono essere ottimizzati in questo modo, senza che lo sviluppatore verifichi esplicitamente casi speciali in ogni singolo luogo in cui sono rilevanti.
Forse un esempio migliore per questa ottimizzazione
def power(a, n) {
n == 0 => 1
n == 1 => a
n > 1 => a * power(a, n - 1)
(test) for any (x) power(a, n) % x == power(a % x, n) % x
}
Non è un segreto, penso, che un compilatore intelligente potrebbe usarlo in molti casi, e un interprete intelligente / compilatore just-in-time potrebbe usarli in molti più casi.
Per comprendere meglio test e linguaggi, sono curioso di sapere se i miei pensieri sono utili, e se no, cosa mi manca, o se sì, perché le lingue popolari non implementano tali schemi.