2009-03-26 2 views
5

Sto provando a mettere una chiamata di funzione 'stampa' in una funzione haskell.haskell: errore nel tentativo di chiamare putStrLn nella funzione

(un semplice messaggio di debug).

Di seguito è riportato il mio codice e il messaggio di errore del compilatore (ghc 6.10).

Non capisco perché sta interrompendo la chiamata di putstr e l'array vuoto.

L'array vuoto è il valore restituito per quel caso particolare (il messaggio di stampa è in realtà solo uno stub per ora).

Qualche idea sul perché questo non funziona?

Grazie

My Code:

 
isAFactor :: Integer -> Integer -> Bool 
isAFactor x y = x `mod` y == 0 

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num = 
    let quotient = div num 2 
    in 
     if(counter > quotient) 
      then do 
       putStrLn ("factorList is : " ++ show quotient) (*** Line 10***) 
       [] 
     else if(isAFactor num counter) 
      then [counter] ++ [quotient] ++ findFactors (counter + 1) num 
     else 
      findFactors (counter + 1) num 

errore da ghc

 
    test.hs:10:4: 
    Couldn't match expected type `[a] -> [Integer]' 
      against inferred type `IO()' 
    In the expression: 
     putStrLn ("factorList is : " ++ show quotient) [] 
    In the expression: 
     do putStrLn ("factorList is : " ++ show quotient) [] 
    In the expression: 
     if (counter > quotient) then 
      do putStrLn ("factorList is : " ++ show quotient) [] 
     else 
      if (isAFactor num counter) then 
        [counter] ++ [quotient] ++ findFactors (counter + 1) num 
      else 
       findFactors (counter + 1) num 
+0

Credo che questo sia dovuto al fatto che findFactors è una funzione pura, quindi non può avere effetti collaterali (come IO). Questo è Haskell. Non ho idea di come debugare la tua funzione, però. In C++ uso sempre il debug-logging. –

+0

"fare" non significa quello che pensi che significhi. È solo lo zucchero di sintassi per il sequenziamento di monade; in realtà non puoi "fare" nulla in Haskell. :-) – ShreevatsaR

risposta

18

E 'importante ricordare che Haskell è un linguaggio funzionale pure. Ciò significa che le funzioni non possono avere effetti collaterali, inclusa la stampa di messaggi di debug sullo schermo.

È tuttavia possibile rompere questa purezza e può essere utile nel debug. Dai uno sguardo al modulo Debug.Trace. Lì troverai una funzione trace :: String -> a -> a. È possibile utilizzare nel vostro codice come questo:

import Debug.Trace 

isAFactor :: Integer -> Integer -> Bool 
isAFactor x y = x `mod` y == 0 

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num = 
    let quotient = div num 2 
    in 
     if(counter > quotient) 
       then trace ("factorList is: " ++ show quotient) [] 
     else if(isAFactor num counter) 
      then [counter] ++ [quotient] ++ findFactors (counter + 1) num 
     else 
      findFactors (counter + 1) num 

come i commenti suggerito:

Haskell è anche un linguaggio lazy. Un'espressione non viene valutata prima che il risultato sia effettivamente necessario. L'uso della funzione di traccia può essere un po 'confuso in un'impostazione lenta, perché non è sempre facile capire quando il messaggio di traccia viene stampato sullo schermo (se è stampato del tutto).

Poiché haskell è un tipo di linguaggio molto diverso, è forse meglio provare e sviluppare programmi in un modo altrettanto diverso. Prova a ragionare sulle tue funzioni invece di usare trace e simili costrutti "non puliti". Impara a sfruttare il potente sistema di tipi haskells e usa (per esempio) QuickCheck per testare la tua funzione una volta passato il controllo di tipo.

+0

Vale la pena notare che poiché Haskell è un linguaggio pigro, anche queste chiamate "trace" si verificano pigramente. Poiché non esiste un sequenziamento esplicito delle operazioni, le chiamate a "trace" potrebbero sembrare fuori uso o addirittura non accadere affatto. Ricorda che hai a che fare con un linguaggio pigro, anche quando fai il debug. –

3

aggiornato con chiarimenti

Il problema è che IO in Haskell è monade, il blocco inizia con do è uno zucchero sintattico per combinare espressioni monadici (a volte chiamato dichiarazioni) con gli operatori monadici. In questo caso, la monade in questione è la monade IO, come si può dedurre dalla chiamata a putStrLn. Il [] nella seconda riga del blocco do è in realtà non il valore dell'intero blocco di do, piuttosto è interpretato come l'ultimo argomento di putStrLn; non che accetta un secondo argomento, ma il compilatore non arriva nemmeno al punto di capirlo, perché termina prima con l'errore di tipo che hai citato. Per rendere tale linea un comando, devi inserire, ad es., Restituire, un'altra funzione monadica, di fronte ad essa (ad esempio, return []). Non sto dicendo, però, che questo ti aiuterà a risolvere il tuo problema.

L'errore di tipo deriva dal fatto che le espressioni monodiche IO hanno sempre il tipo IO _; nel tuo caso, il blocco do ha anche questo tipo, che è ovviamente incompatibile con [Integer], il tipo specificato nella firma.

In generale, poiché Haskell è un linguaggio funzionale puro con IO monadico, una volta dentro la monade IO, non c'è modo di uscirne, è contagioso. cioè, se una funzione ha un blocco do con operazioni IO al suo interno, la sua firma conterrà necessariamente il tipo IO _, così come la firma di tutte le altre funzioni che invocano questa funzione, ecc. (Altre monadi forniscono funzioni di "uscita", ma l'IO monade no.)

+0

Non è veramente interpretato come l'ultimo argomento di putStrLn è? È il secondo argomento di (>> =). i blocchi non sono necessariamente IO(), ci sono molti tipi diversi di blocchi, incluso [a]! – cthulahoops

+0

Qui hai una serie di idee sbagliate. Odio fare un downvote su un post più serio, ma questo oscura più di quello che rivela. –

+0

@cthulahoops: il messaggio di errore "Nell'espressione" putStrLn ("factorList is:" ++ show quotient) [] "" sembra indicare che la mia interpretazione è corretta. –

9

Il post di Jonas copre abbastanza bene la tua domanda, quindi ti darò una riscrittura idiomatica della funzione findFactors. Ho trovato utile per me quando stavo imparando per la prima volta.

Così si desidera trovare tutti i fattori di un dato numero n, cercando in ogni numero da 1 fino a n/2, controllo per vedere se si tratta di un fattore di n e la costruzione di un elenco di quelli che sono.

tua versione (con minime modifiche per farlo funzionare):

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num = 
    let quotient = div num 2 
    in 
     if(counter > quotient) 
      then [] 
     else if(isAFactor num counter) 
      then [counter] ++ findFactors (counter + 1) num 
     else 
      findFactors (counter + 1) num 

Un paio di modifiche di formattazione per renderlo un po 'più leggibile:

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num 
    | counter > div num 2 = [] 
    | otherwise = if num `isAFactor` counter 
       then counter:findFactors (counter+1) num 
       else findFactors (counter + 1) num 

Questo va bene, ma è meno che ideale in un paio di modi. Innanzitutto, ricalcola il quoziente ogni volta che viene chiamato findFactors, ovvero divisioni n/2 (anche se ghc -O2 sembra rendersene conto e calcolarlo solo una volta). In secondo luogo, è un po 'fastidioso dover affrontare quella variabile contatore ovunque. In terzo luogo, è ancora piuttosto imperativo.

Un altro modo di considerare il problema sarebbe quello di prendere l'elenco di numeri interi da 1 fino a n/2 e filtrare solo per quelli che sono fattori di n. Questo si traduce direttamente in bella Haskell:

findFactors :: Integer -> [Integer] 
findFactors num = filter (isAFactor num) [1..(num `div` 2)] 

Potrebbe essere una sorpresa di scoprire che questo ha le stesse caratteristiche di prestazioni della versione di cui sopra. Haskell non ha bisogno di allocare memoria per l'intera lista fino a n/2 contemporaneamente, può solo generare ogni valore come è necessario.

7

Altri hanno fatto un buon lavoro di spiegazione del codice, quindi permettimi di aiutarti a decodificare il messaggio di errore.

Couldn't match expected type `[a] -> [Integer]' 
     against inferred type `IO()' 
In the expression: 
    putStrLn ("factorList is : " ++ show quotient) [] 

Mi mancano tutte le altre parti "Nell'espressione"; mostrano solo un contesto sempre più ampio.

Tutto in Haskell è un'espressione, quindi tutto ha un tipo. Ciò include qualcosa come "putStrLn". Se si digita ": t putStrLn" in GHCi lo vedrete risposta:

putStrLn :: String -> IO() 

Il che significa che putStrLn è una funzione che prende una stringa e restituisce un "" azione IO", che in questo caso è l'azione di mettendo il messaggio sullo schermo.Nel tuo codice hai dato una stringa a "putStrLn", quindi il compilatore ha dedotto che l'espressione "putStrLn (roba)" aveva il tipo "IO()". Questa è la parte "tipo inferito" del messaggio di errore del compilatore.

Nel frattempo il compilatore è stato anche facendo inferenza di tipo nella direzione opposta, dall'esterno verso l'interno. Tra le altre cose è notato che il presente "putStrLn (roba)" espressione sembrava essere applicato ad una lista vuota, che ha scrivi "[a]" (cioè una lista di qualcosa, non sappiamo cosa). Inoltre il risultato dell'intera espressione dovrebbe essere di tipo "[Intero]". Pertanto l'espressione "putStrLn (stuff)" dovrebbe essere una funzione per trasformare "[]" in un elenco di numeri interi, il cui tipo è scritto "[a] -> [Intero]". Questa è la parte "tipo previsto" del messaggio di errore.

A questo punto il compilatore ha concluso che non poteva corrispondere a questi due tipi, quindi ha segnalato l'errore.

"non poteva competere previsto tipo 'Foo' contro il tipo derivato 'Bar'" è probabilmente il messaggio di errore più comune che si ottiene quando si tenta di compilare Haskell, quindi vale la pena provare a leggerlo e capirlo. Guarda il tipo dedotto e cerca di capire quale parte dell'espressione quotata ha quel tipo. Quindi cerca di capire perché il compilatore si aspettava qualcos'altro guardando il codice circostante.

+0

+1, buona spiegazione del messaggio di errore. Non riuscivo a capire come il codice segnalasse l'errore che ha fatto. Fino a quando ho appena realizzato che l'interrogante ha inserito un'interruzione di riga per inserire il commento "riga 10". –