2010-07-10 10 views
13

Scusate se la domanda è molto elementare, sono ancora molto nuovo per Haskell. Diciamo che ho una funzione che può funzionare solo con due numeri che sono nella razione d'oro (1.618), come posso definire i tipi di myfunx y per prendere solo i numeri di rapporto aureo. Cosa succede se invoco myfun senza numeri golden ratio dal mio programma (un errore di compilazione?)? Cosa succede se la chiamata senza numeri golden ratio viene eseguita in runtime tramite l'input dell'utente?Utilizzo dei tipi di Haskell per sostituire le dichiarazioni di asserzione o se assegni in altre lingue

+6

Perché sono necessari 2 parametri? – kennytm

+2

Non si può avere la funzione accetta un argomento e si assume che l'altro numero sia 1,618 volte maggiore? –

+1

Non è possibile applicarlo in fase di compilazione (con due parametri) - e, come si fa notare, non ci sarebbe alcuna protezione contro l'input dell'utente. * Ecco una contro-sfida * dati due numeri, xey, scrivi una funzione per restituire true sef formano la sezione aurea. Ora, modificalo per restituire un rapporto aureo o nulla, a seconda dei casi. –

risposta

18

Si potrebbe desiderare un ADT che può essere costruito solo con i numeri con rapporto dorato, quindi scrivere myfun per accettare quel tipo di dati.

Ho assunto Intero come un tipo di base, ma è possibile utilizzarne altri (es: Double o Float) o persino essere polimorfico.

1) Effettuare l'ADT

module Golden (Gold, getGold, buildGold) where 

data Gold = G Integer Integer 

getGold :: Gold -> (Integer, Integer) 
getGold (G x y) = (x, y) 

buildGold :: Integer -> Integer -> Maybe Gold 
buildGold x y 
    | isGolden x y = Just (G x y) 
    | otherwise = Nothing 

Avviso questo modulo esporta il tipo Gold ma non il costruttore (vale a dire, non G). Quindi l'unico modo per ottenere un valore di tipo Gold è con buildGold che esegue un controllo in fase di esecuzione - ma solo uno - in modo che i valori di Gold possano essere utilizzati e si presumano essere un rapporto aureo da tutti i consumatori senza controllo.

2) Utilizzare l'ADT per costruire myfun

myfun :: Gold -> ??? 
myfun g = expr 
    where (x, y) = getGold g 

Ora, se si tenta di chiamare myfun con un numero non d'oro (un valore non di tipo Gold) quindi si otterrà un errore di compilazione.

Recap Per creare numeri dorati è necessario utilizzare la funzione buildGold, che forza il numero da controllare.

Avviso cosa viene controllato quando! Si dispone di una garanzia di compilazione che myfun e tutte le altre funzioni che si desidera utilizzare con Gold, vengono sempre fornite le percentuali d'oro. L'input del programma (da utente, rete o dove mai) richiede ancora i controlli di runtime ed è ciò che fornisce buildGold; ovviamente non ci sarà mai un programma che possa promettere che l'umano non digiterà qualcosa di indesiderabile.

Anche le alternative fornite nei commenti alla tua domanda sono degne di considerazione.Un ADT è leggermente pesante se tutto ciò di cui hai bisogno è una singola funzione, myfun, che può fallire e quindi avere solo myfun :: (Integer, Integer) -> Maybe ???.

+0

Hai davvero provato a eseguirlo? – kennytm

+0

Dovresti esportare getGold e buildGold e dovresti esportare Gold senza i suoi costruttori. – sepp2k

+2

'data Gold = G Double Double' se si desidera utilizzare un tipo fisso; non troverai mai due 'interi che soddisfano la sezione aurea. –

1

Il meglio che puoi fare praticamente è un controllo in fase di esecuzione. Potrebbero esserci alcuni calcoli a livello di codice che non conosco (vedi il commento di Luqui), ma questo non è pratico in Haskell.

Si potrebbe utilizzare an assert, che è ciò che si desidera sostituire,

checker :: a -> b -> Bool 
checker x y = x * 1.618 `approxEqual` y 

unsafeMyfun :: a -> b -> c 
unsafeMyfun x y = assert (checker x y) (doRealThingWith a b) 

o restituire un Maybe a (o Either err a) per evitare eccezioni che non possono essere catturati in funzioni pure,

myfun :: a -> b -> Maybe c 
myfun x y = do 
       guard $ checker x y 
       return $ doRealThingWith x y 

oppure utilizzare un tipo di contratto personalizzato come nella risposta di Tom, ecc. In alcun modo, non è possibile controllare il vincolo in fase di compilazione. Infatti, a causa della monade IO, qualsiasi vincolo in fase di compilazione non può essere preciso.

+1

No, lui può fare molto meglio usando il sistema di tipi per assicurarsi che tutti i numeri con il minimo numero di oro vengano controllati prima di essere inviati a 'myfun'. –

+0

Inoltre, è meglio non confrontare mai due numeri in virgola mobile per l'equivalenza esatta. – EFraim

+1

@EFraim: Come commentato dall'OP, "Non farti coinvolgere da un cattivo esempio: se aiuta a immaginare xey sono un messaggio e hash salato ...", quindi non concentriamoci su di esso. – kennytm

10

La tecnica più semplice è utilizzare smart constructors, che utilizza una funzione da Int a GoldenInt, che controlla che i valori siano nei rapporti richiesti.

Con uno sforzo maggiore, è possibile utilizzare type level numbers per garantire che non sia necessario alcun controllo di runtime, tuttavia, dato che sei un principiante, mi attenersi al metodo del costruttore intelligente.

La risposta di Tom sopra è un esempio di questo idioma.

+0

Il tipo aritmetico e la fine del collegamento dei costruttori intelligenti sono molto intelligenti (e il primo pezzo di codice che ho visto dal mio primo anno in CS che utilizza i numeri di Peano). Il tipo aritmetico può essere applicato a qualsiasi cosa o solo a una determinata "classe di conteggio" dei problemi? –

Problemi correlati