Haskell - Guida rapida

Haskell è un linguaggio di programmazione funzionale che è stato appositamente progettato per gestire il calcolo simbolico e le applicazioni di elaborazione di elenchi. La programmazione funzionale si basa su funzioni matematiche. Oltre ad Haskell, alcuni degli altri linguaggi popolari che seguono il paradigma della programmazione funzionale includono: Lisp, Python, Erlang, Racket, F #, Clojure, ecc.

In conventional programing, le istruzioni sono prese come un insieme di dichiarazioni in una sintassi o formato specifico, ma nel caso di functional programing, tutto il calcolo è considerato come una combinazione di funzioni matematiche separate.

Diventare funzionale con Haskell

Haskell è un linguaggio puramente funzionale ampiamente utilizzato. Qui, abbiamo elencato alcuni punti che rendono questo linguaggio così speciale rispetto ad altri linguaggi di programmazione convenzionali come Java, C, C ++, PHP, ecc.

  • Functional Language- Nel linguaggio di programmazione convenzionale, istruiamo il compilatore su una serie di compiti che non sono altro che dire al tuo computer "cosa fare" e "come fare?" Ma in Haskell diremo al nostro computer "cos'è?"

  • Laziness- Haskell è un linguaggio pigro. Dilazy, intendiamo che Haskell non valuterà alcuna espressione senza alcun motivo. Quando il motore di valutazione rileva che un'espressione deve essere valutata, crea un filethunk data structure raccogliere tutte le informazioni richieste per quella specifica valutazione e un puntatore a quella thunk data structure. Il motore di valutazione inizierà a funzionare solo quando sarà necessario valutare quella specifica espressione.

  • Modularity- Un'applicazione Haskell non è altro che una serie di funzioni. Possiamo dire che un'applicazione Haskell è una raccolta di numerose piccole applicazioni Haskell.

  • Statically Typed- Nel linguaggio di programmazione convenzionale, dobbiamo definire una serie di variabili insieme al loro tipo. Al contrario, Haskell è un linguaggio strettamente digitato. Con il termine, linguaggio Strictly Typed, si intende che il compilatore Haskell è abbastanza intelligente da capire il tipo di variabile dichiarata, quindi non è necessario menzionare esplicitamente il tipo di variabile utilizzata.

  • Maintainability - Le applicazioni Haskell sono modulari e quindi è molto facile ed economico mantenerle.

I programmi funzionali sono più concorrenti e seguono il parallelismo in esecuzione per fornire prestazioni più accurate e migliori. Haskell non fa eccezione; è stato sviluppato in modo da gestiremultithreading effettivamente.

Ciao mondo

È un semplice esempio per dimostrare il dinamismo di Haskell. Dai un'occhiata al seguente codice. Tutto ciò di cui abbiamo bisogno è solo una riga per stampare "Hello Word" sulla console.

main = putStrLn "Hello World"

Una volta che il compilatore Haskell incontra la parte di codice sopra, fornisce prontamente il seguente output:

Hello World

Forniremo molti esempi in questo tutorial per mostrare la potenza e la semplicità di Haskell.

Abbiamo configurato l'ambiente di programmazione Haskell online su: https://www.tutorialspoint.com/compile_haskell_online.php

Questo editor online offre molte opzioni per esercitarsi con gli esempi di programmazione Haskell. Vai alla sezione terminale della pagina e digita"ghci". Questo comando carica automaticamente il compilatore Haskell e avvia Haskell online. Riceverai il seguente output dopo aver utilizzato il fileghci comando.

sh-4.3$ ghci
GHCi,version7.8.4:http://www.haskell.org/ghc/:?forhelp
Loading package ghc-prim...linking...done.
Loading packageinteger gmp...linking... done.
Loading package base...linking...done.
Prelude>

Se desideri comunque utilizzare Haskell offline nel tuo sistema locale, devi scaricare la configurazione Haskell disponibile dalla sua pagina web ufficiale - https://www.haskell.org/downloads

Esistono tre diversi tipi di file installers disponibile sul mercato -

  • Minimal Installer - Fornisce GHC (The Glasgow Haskell Compiler), CABAL (Common Architecture for Building Applications and Libraries) e strumenti Stack.

  • Stack Installer- In questo programma di installazione, GHC può essere scaricato in una multipiattaforma di catena di pedaggio gestita. Installerà la tua applicazione a livello globale in modo tale da poter aggiornare i suoi strumenti API ogni volta che è necessario. Risolve automaticamente tutte le dipendenze orientate a Haskell.

  • Haskell Platform- Questo è il modo migliore per installare Haskell perché installerà l'intera piattaforma nella tua macchina e quella da una posizione specifica. Questo programma di installazione non è distributivo come i due programmi di installazione precedenti.

Abbiamo visto diversi tipi di installatori disponibili sul mercato, ora vediamo come utilizzare questi installatori nella nostra macchina. In questo tutorial useremo il programma di installazione della piattaforma Haskell per installare il compilatore Haskell nel nostro sistema.

Configurazione dell'ambiente in Windows

Per configurare l'ambiente Haskell sul tuo computer Windows, vai al loro sito web ufficiale https://www.haskell.org/platform/windows.html e scarica l'Installer secondo la tua architettura personalizzabile.

Controlla l'architettura del tuo sistema, scarica il file di installazione corrispondente ed eseguilo. Si installerà come qualsiasi altra applicazione Windows. Potrebbe essere necessario aggiornare la configurazione CABAL del sistema.

Configurazione dell'ambiente in MAC

Per configurare l'ambiente Haskell sul tuo sistema MAC, vai al loro sito web ufficiale https://www.haskell.org/platform/mac.html e scarica il programma di installazione per Mac.

Configurazione dell'ambiente in Linux

L'installazione di Haskell su un sistema basato su Linux richiede l'esecuzione di alcuni comandi che non sono così facili come MAC e Windows. Sì, è noioso ma è affidabile.

Puoi seguire i passaggi indicati di seguito per installare Haskell sul tuo sistema Linux -

Step 1 - Per configurare l'ambiente Haskell sul tuo sistema Linux, vai al sito web ufficiale https://www.haskell.org/platform/linux.htmle scegli la tua distribuzione. Troverai la seguente schermata sul tuo browser.

Step 2- Seleziona la tua distribuzione. Nel nostro caso, stiamo usando Ubuntu. Dopo aver selezionato questa opzione, apparirà la seguente pagina sullo schermo con il comando per installare Haskell nel nostro sistema locale.

Step 3 - Apri un terminale premendo Ctrl + Alt + T. Esegui il comando "$ sudo apt-get install haskell-platform"e premere Invio. Inizierà automaticamente a scaricare Haskell sul tuo sistema dopo averti autenticato con la password di root. Dopo l'installazione, riceverai un messaggio di conferma.

Step 4- Vai di nuovo al tuo terminale ed esegui il comando GHCI. Una volta ottenuto il prompt di Prelude, sei pronto per utilizzare Haskell sul tuo sistema locale.

Per uscire dal prologo GHCI, puoi utilizzare il comando ": quit exit".

Haskell è un linguaggio di programmazione puramente funzionale, quindi è molto più interattivo e intelligente di altri linguaggi di programmazione. In questo capitolo, impareremo i modelli di dati di base di Haskell che sono effettivamente predefiniti o in qualche modo decodificati in modo intelligente nella memoria del computer.

Durante questo tutorial, utilizzeremo la piattaforma online Haskell disponibile sul nostro sito Web (https://www.tutorialspoint.com/codingground.htm).

Numeri

Haskell è abbastanza intelligente da decodificare un numero come numero. Pertanto, non è necessario menzionare il suo tipo esternamente come facciamo di solito nel caso di altri linguaggi di programmazione. Come per esempio, vai al prompt dei comandi di preludio, esegui semplicemente "2 + 2" e premi invio.

sh-4.3$ ghci 
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
Prelude> 2+2

Riceverai il seguente output come risultato.

4

Nel codice precedente, abbiamo appena passato due numeri come argomenti al compilatore GHCI senza predefinirne il tipo, ma il compilatore potrebbe facilmente decodificare queste due voci come numeri.

Ora, proviamo un calcolo matematico un po 'più complesso e vediamo se il nostro compilatore intelligente ci fornisce l'output corretto o meno. Prova con "15+ (5 * 5) -40"

Prelude> 15+(5*5)-40

L'espressione sopra restituisce "0" in base all'output previsto.

0

Personaggi

Come i numeri, Haskell può identificare in modo intelligente un carattere dato come input per esso. Vai al prompt dei comandi Haskell e digita qualsiasi carattere con virgolette doppie o singole.

Forniamo la seguente riga come input e controlliamo il suo output.

Prelude> :t "a"

Produrrà il seguente output:

"a" :: [Char]

Ricorda che usi (:t) fornendo l'input. Nell'esempio sopra,(:t)consiste nell'includere il tipo specifico relativo agli input. Impareremo di più su questo tipo nei prossimi capitoli.

Dai un'occhiata al seguente esempio in cui stiamo passando un input non valido come carattere che a sua volta porta a un errore.

Prelude> :t a 
<interactive>:1:1: Not in scope: 'a'  

Prelude> a 
<interactive>:4:1: Not in scope: 'a'

Con il messaggio di errore "<interactive>: 4: 1: Not in scope:` a '"il compilatore Haskell ci avverte che non è in grado di riconoscere il tuo input. Haskell è un tipo di linguaggio in cui tutto è rappresentato utilizzando un numero.

Haskell segue lo stile di codifica ASCII convenzionale. Diamo un'occhiata al seguente esempio per capirne di più:

Prelude> '\97' 
'a'  
Prelude> '\67' 
'C'

Guarda come il tuo input viene decodificato in formato ASCII.

Corda

UN stringnon è altro che una raccolta di personaggi. Non esiste una sintassi specifica per l'utilizzo di stringhe, ma Haskell segue lo stile convenzionale di rappresentare una stringa con virgolette doppie.

Dai un'occhiata al seguente esempio in cui stiamo passando la stringa "Tutorialspoint.com".

Prelude> :t "tutorialspoint.com"

Produrrà il seguente output sullo schermo:

"tutorialspoint.com" :: [Char]

Guarda come l'intera stringa è stata decodificata solo come array di Char. Passiamo all'altro tipo di dati e alla sua sintassi. Una volta che avremo iniziato la nostra pratica effettiva, saremo abituati a tutti i tipi di dati e al loro utilizzo.

Booleano

Anche il tipo di dati booleano è molto semplice come gli altri tipi di dati. Guarda il seguente esempio in cui useremo diverse operazioni booleane utilizzando alcuni input booleani come "True" o "False".

Prelude> True && True 
True  
Prelude> True && False 
False   
Prelude> True || True 
True  
Prelude> True || False 
True

Nell'esempio precedente, non è necessario menzionare che "True" e "False" sono i valori booleani. Haskell stesso può decodificarlo e fare le rispettive operazioni. Modifichiamo i nostri input con "true" o "false".

Prelude> true

Produrrà il seguente output:

<interactive>:9:1: Not in scope: 'true'

Nell'esempio precedente, Haskell non poteva distinguere tra "true" e un valore numerico, quindi il nostro input "true" non è un numero. Quindi, il compilatore Haskell genera un errore che indica che il nostro input non è il suo ambito.

Elenco e comprensione degli elenchi

Come altri tipi di dati, Listè anche un tipo di dati molto utile utilizzato in Haskell. Ad esempio, [a, b, c] è un elenco di caratteri, quindi, per definizione, List è una raccolta dello stesso tipo di dati separato da virgola.

Come altri tipi di dati, non è necessario dichiarare un elenco come elenco. Haskell è abbastanza intelligente da decodificare l'input guardando la sintassi usata nell'espressione.

Dai un'occhiata al seguente esempio che mostra come Haskell tratta una lista.

Prelude> [1,2,3,4,5]

Produrrà il seguente output:

[1,2,3,4,5]

Gli elenchi in Haskell sono di natura omogenea, il che significa che non ti consentiranno di dichiarare un elenco di diversi tipi di dati. Qualsiasi elenco come [1,2,3,4,5, a, b, c, d, e, f] produrrà un errore.

Prelude> [1,2,3,4,5,a,b,c,d,e,f]

Questo codice produrrà il seguente errore:

<interactive>:17:12: Not in scope: 'a' 
<interactive>:17:14: Not in scope: 'b' 
<interactive>:17:16: Not in scope: 'c' 
<interactive>:17:18: Not in scope: 'd' 
<interactive>:17:20: Not in scope: 'e' 
<interactive>:17:22: Not in scope: 'f'

Comprensione delle liste

La comprensione dell'elenco è il processo di generazione di un elenco utilizzando un'espressione matematica. Guarda il seguente esempio in cui stiamo generando un elenco utilizzando un'espressione matematica nel formato [output | intervallo, condizione].

Prelude> [x*2| x<-[1..10]] 
[2,4,6,8,10,12,14,16,18,20]  
Prelude> [x*2| x<-[1..5]] 
[2,4,6,8,10]  
Prelude> [x| x<-[1..5]] 
[1,2,3,4,5]

Questo metodo per creare una lista usando un'espressione matematica è chiamato come List Comprehension.

Tupla

Haskell fornisce un altro modo per dichiarare più valori in un singolo tipo di dati. È noto comeTuple. Una tupla può essere considerata come una lista, tuttavia ci sono alcune differenze tecniche tra una tupla e una lista.

Una tupla è un tipo di dati immutabile, poiché non possiamo modificare il numero di elementi in fase di runtime, mentre una lista è un tipo di dati mutabile.

D'altra parte, List è un tipo di dati omogeneo, ma Tuple è di natura eterogenea, perché una tupla può contenere diversi tipi di dati al suo interno.

Le tuple sono rappresentate da parentesi singole. Dai un'occhiata al seguente esempio per vedere come Haskell tratta una tupla.

Prelude> (1,1,'a')

Produrrà il seguente output:

(1,1,'a')

Nell'esempio sopra, abbiamo usato una tupla con due number digitare le variabili e un file char tipo variabile.

In questo capitolo, impareremo a conoscere i diversi operatori utilizzati in Haskell. Come altri linguaggi di programmazione, Haskell gestisce in modo intelligente alcune operazioni di base come addizione, sottrazione, moltiplicazione, ecc. Nei prossimi capitoli, impareremo di più sui diversi operatori e sul loro utilizzo.

In questo capitolo, useremo diversi operatori in Haskell utilizzando la nostra piattaforma online (https://www.tutorialspoint.com/codingground.htm). Ricorda che stiamo usando solointeger digitare i numeri perché ne sapremo di più decimal digitare i numeri nei capitoli successivi.

Operatore di addizione

Come suggerisce il nome, l'operatore di addizione (+) viene utilizzato per la funzione di addizione. Il codice di esempio seguente mostra come aggiungere due numeri interi in Haskell:

main = do 
   let var1 = 2 
   let var2 = 3 
   putStrLn "The addition of the two numbers is:" 
   print(var1 + var2)

Nel file sopra, abbiamo creato due variabili separate var1 e var2. Alla fine, stampiamo il risultato utilizzando il fileadditionoperatore. Utilizzare ilcompile e execute pulsante per eseguire il codice.

Questo codice produrrà il seguente output sullo schermo:

The addition of the two numbers is:
5

Operatore di sottrazione

Come suggerisce il nome, questo operatore viene utilizzato per l'operazione di sottrazione. Il codice di esempio seguente mostra come sottrarre due numeri interi in Haskell:

main = do 
   let var1 = 10 
   let var2 = 6 
   putStrLn "The Subtraction of the two numbers is:" 
   print(var1 - var2)

In questo esempio, abbiamo creato due variabili var1 e var2. Successivamente, utilizziamo l'operatore di sottrazione (-) per sottrarre i due valori.

Questo codice produrrà il seguente output sullo schermo:

The Subtraction of the two numbers is:
4

Operatore di moltiplicazione

Questo operatore viene utilizzato per le operazioni di moltiplicazione. Il codice seguente mostra come moltiplicare due numeri in Haskell utilizzando l'operatore di moltiplicazione:

main = do 
   let var1 = 2 
   let var2 = 3 
   putStrLn "The Multiplication of the Two Numbers is:" 
   print(var1 * var2)

Questo codice produrrà il seguente output, quando lo esegui nella nostra piattaforma online:

The Multiplication of the Two Numbers is:
6

Operatore di divisione

Dai un'occhiata al seguente codice. Mostra come puoi dividere due numeri in Haskell -

main = do 
   let var1 = 12 
   let var2 = 3 
   putStrLn "The Division of the Two Numbers is:" 
   print(var1/var2)

Produrrà il seguente output:

The Division of the Two Numbers is: 
4.0

Operatore sequenza / intervallo

Sequence or Range è un operatore speciale in Haskell. È indicato con "(..)". È possibile utilizzare questo operatore durante la dichiarazione di un elenco con una sequenza di valori.

Se vuoi stampare tutti i valori da 1 a 10, puoi usare qualcosa come "[1..10]". Allo stesso modo, se vuoi generare tutti gli alfabeti da "a" a "z", puoi semplicemente digitare"[a..z]".

Il codice seguente mostra come utilizzare l'operatore Sequence per stampare tutti i valori da 1 a 10 -

main :: IO() 
main = do 
   print [1..10]

Genererà il seguente output:

[1,2,3,4,5,6,7,8,9,10]

Il processo decisionale è una funzionalità che consente ai programmatori di applicare una condizione nel flusso di codice. Il programmatore può eseguire una serie di istruzioni a seconda di una condizione predefinita. Il seguente diagramma di flusso mostra la struttura decisionale di Haskell:

Haskell fornisce i seguenti tipi di dichiarazioni decisionali:

Sr.No. Dichiarazione e descrizione
1 istruzione if – else

Uno if dichiarazione con un elsedichiarazione. Le istruzioni inelse block verrà eseguito solo quando la condizione booleana data non riesce a soddisfare.

2 Istruzione if-else annidata

Molteplici if blocchi seguiti da else blocchi

Haskell è un linguaggio funzionale ed è strettamente tipizzato, il che significa che il tipo di dati utilizzato nell'intera applicazione sarà noto al compilatore in fase di compilazione.

Classe di tipo integrato

In Haskell, ogni affermazione è considerata come un'espressione matematica e la categoria di questa espressione è chiamata come a Type. Puoi dire che "Tipo" è il tipo di dati dell'espressione utilizzata in fase di compilazione.

Per saperne di più sul Type, useremo il comando ": t". In modo generico,Type può essere considerato un valore, mentre Type Classpuò essere considerato come un insieme di tipi simili di tipi. In questo capitolo, impareremo a conoscere diversi tipi incorporati.

Int

Intè una classe di tipo che rappresenta i dati dei tipi Integer. Ogni numero intero compreso tra 2147483647 e -2147483647 rientra nelInttipo di classe. Nell'esempio seguente, la funzionefType() si comporterà in base al tipo definito.

fType :: Int -> Int -> Int 
fType x y = x*x + y*y
main = print (fType 2 4)

Qui abbiamo impostato il tipo di funzione fType() come int. La funzione richiede dueint valori e ne restituisce uno intvalore. Se compili ed esegui questo pezzo di codice, produrrà il seguente output:

sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts 
sh-4.3$ main
20

Numero intero

Integer può essere considerato come un superset di Int. Questo valore non è limitato da alcun numero, quindi un numero intero può essere di qualsiasi lunghezza senza alcuna limitazione. Per vedere la differenza fondamentale traInt e Integer tipi, modifichiamo il codice sopra come segue:

fType :: Int -> Int -> Int 
fType x y = x*x + y*y 
main = print (fType 212124454 44545454454554545445454544545)

Se compili la parte di codice sopra, verrà visualizzato il seguente messaggio di errore:

main.hs:3:31: Warning:            
   Literal 44545454454554545445454544545 is out of the Int range -
   9223372036854775808..9223372036854775807 
Linking main ...

Questo errore si è verificato perché la nostra funzione fType () si aspettava un valore di tipo Int e stiamo passando un valore di tipo Int davvero grande. Per evitare questo errore, modifichiamo il tipo "Int" con "Integer" e osserviamo la differenza.

fType :: Integer -> Integer -> Integer 
fType x y = x*x + y*y 
main = print (fType 212124454 4454545445455454545445445454544545)

Ora, produrrà il seguente output:

sh-4.3$ main
1984297512562793395882644631364297686099210302577374055141

Galleggiante

Dai un'occhiata al seguente pezzo di codice. Mostra come funziona il tipo Float in Haskell -

fType :: Float -> Float -> Float 
fType x y = x*x + y*y 
main = print (fType 2.5 3.8)

La funzione accetta due valori float come input e restituisce un altro valore float come output. Quando compili ed esegui questo codice, produrrà il seguente output:

sh-4.3$ main
20.689999

Doppio

Doubleè un numero in virgola mobile con doppia precisione alla fine. Dai un'occhiata al seguente esempio:

fType :: Double -> Double -> Double 
fType x y = x*x + y*y 
main = print (fType 2.56 3.81)

Quando esegui la parte di codice sopra, genererà il seguente output:

sh-4.3$ main 
21.0697

Bool

Boolè un tipo booleano. Può essere vero o falso. Esegui il codice seguente per capire come funziona il tipo Bool in Haskell -

main = do  
   let x = True 
   
   if x == False 
      then putStrLn "X matches with Bool Type" 
   else putStrLn "X is not a Bool Type"

Qui stiamo definendo una variabile "x" come bool e confrontandola con un altro valore booleano per verificarne l'originalità. Produrrà il seguente output:

sh-4.3$ main
X is not a Bool Type

Char

I caratteri rappresentano i personaggi. Qualsiasi cosa all'interno di una singola citazione è considerata come un personaggio. Nel codice seguente, abbiamo modificato il nostro precedentefType() funzione per accettare il valore Char e restituire il valore Char come output.

fType :: Char-> Char 
fType x = 'K' 
main = do  
   let x = 'v' 
   print (fType x)

La parte di codice sopra verrà chiamata fType() funzione con a charvalore di "v" ma restituisce un altro valore di carattere, ovvero "K". Ecco il suo output:

sh-4.3$ main 
'K'

Nota che non useremo questi tipi esplicitamente perché Haskell è abbastanza intelligente da catturare il tipo prima che venga dichiarato. Nei capitoli successivi di questo tutorial, vedremo come diversi tipi e classi Type rendono Haskell un linguaggio fortemente tipizzato.

Classe di tipo EQ

EQtype class è un'interfaccia che fornisce la funzionalità per testare l'uguaglianza di un'espressione. Qualsiasi classe Type che desideri verificare l'uguaglianza di un'espressione dovrebbe far parte di questa classe EQ Type.

Tutte le classi Type standard sopra menzionate ne fanno parte EQclasse. Ogni volta che controlliamo un'uguaglianza utilizzando uno dei tipi sopra menzionati, stiamo effettivamente effettuando una chiamata aEQ tipo di classe.

Nell'esempio seguente, stiamo usando il EQ Digitare internamente utilizzando l'operazione "==" o "/ =".

main = do 
   if 8 /= 8 
      then putStrLn "The values are Equal" 
   else putStrLn "The values are not Equal"

Produrrà il seguente output:

sh-4.3$ main 
The values are not Equal

Ord Type Class

Ordè un'altra classe di interfaccia che ci offre la funzionalità di ordinamento. Tutti itypes che abbiamo utilizzato finora ne fanno parte Ordinterfaccia. Come l'interfaccia EQ, l'interfaccia Ord può essere chiamata usando ">", "<", "<=", "> =", "compare".

Di seguito è riportato l'esempio in cui è stata utilizzata la funzionalità di "confronto" di questa classe di tipo.

main = print (4 <= 2)

Qui, il compilatore Haskell controllerà se 4 è minore o uguale a 2. Poiché non lo è, il codice produrrà il seguente output:

sh-4.3$ main 
False

Spettacolo

Showha una funzionalità per stampare il suo argomento come una stringa. Qualunque sia il suo argomento, stampa sempre il risultato come una stringa. Nell'esempio seguente, stamperemo l'intero elenco utilizzando questa interfaccia. "show" può essere utilizzato per chiamare questa interfaccia.

main = print (show [1..10])

Produrrà il seguente output sulla console. Qui, le virgolette doppie indicano che si tratta di un valore di tipo String.

sh-4.3$ main 
"[1,2,3,4,5,6,7,8,9,10]"

Leggere

Readl'interfaccia fa la stessa cosa di Show, ma non stampa il risultato in formato String. Nel codice seguente, abbiamo usato ilread interfaccia per leggere un valore di stringa e convertire lo stesso in un valore Int.

main = print (readInt "12") 
readInt :: String -> Int 
readInt = read

Qui, stiamo passando una variabile String ("12") al file readIntmetodo che a sua volta restituisce 12 (un valore Int) dopo la conversione. Ecco il suo output:

sh-4.3$ main 
12

Enum

Enumè un altro tipo di classe Type che abilita la funzionalità sequenziale o ordinata in Haskell. È possibile accedere a questa classe Type tramite comandi comeSucc, Pred, Bool, Char, eccetera.

Il codice seguente mostra come trovare il valore successore di 12.

main = print (succ 12)

Produrrà il seguente output:

sh-4.3$ main
13

Delimitato

Tutti i tipi con limiti superiori e inferiori rientrano in questa classe di tipo. Per esempio,Int i dati di tipo hanno un limite massimo di "9223372036854775807" e un limite minimo di "-9223372036854775808".

Il codice seguente mostra come Haskell determina il limite massimo e minimo del tipo Int.

main = do 
   print (maxBound :: Int) 
   print (minBound :: Int)

Produrrà il seguente output:

sh-4.3$ main
9223372036854775807
-9223372036854775808

Ora, prova a trovare il limite massimo e minimo dei tipi Char, Float e Bool.

Num

Questa classe di tipo viene utilizzata per le operazioni numeriche. Tipi come Int, Integer, Float e Double rientrano in questa classe Type. Dai un'occhiata al seguente codice:

main = do 
   print(2 :: Int)  
   print(2 :: Float)

Produrrà il seguente output:

sh-4.3$ main
2
2.0

Integrante

Integralpuò essere considerato come una sottoclasse della classe Num Type. La classe Num Type contiene tutti i tipi di numeri, mentre la classe Integral viene utilizzata solo per i numeri interi. Int e Integer sono i tipi in questa classe Type.

Galleggiante

Come Integral, anche Floating fa parte della classe Num Type, ma contiene solo numeri in virgola mobile. Quindi,Float e Double rientrare in questo tipo di classe.

Classe di tipo personalizzato

Come qualsiasi altro linguaggio di programmazione, Haskell consente agli sviluppatori di definire tipi definiti dall'utente. Nell'esempio seguente, creeremo un tipo definito dall'utente e lo useremo.

data Area = Circle Float Float Float  
surface :: Area -> Float   
surface (Circle _ _ r) = pi * r ^ 2   
main = print (surface $ Circle 10 20 10 )

Qui abbiamo creato un nuovo tipo chiamato Area. Successivamente, utilizziamo questo tipo per calcolare l'area di un cerchio. Nell'esempio precedente, "surface" è una funzione che accettaArea come input e produce Float come output.

Tieni presente che "dati" è una parola chiave qui e tutti i tipi definiti dall'utente in Haskell iniziano sempre con una lettera maiuscola.

Produrrà il seguente output:

sh-4.3$ main
314.15927

Le funzioni giocano un ruolo importante in Haskell, poiché è un linguaggio di programmazione funzionale. Come altre lingue, Haskell ha una propria definizione e dichiarazione funzionale.

  • La dichiarazione di funzione consiste nel nome della funzione e nel suo elenco di argomenti insieme al suo output.

  • La definizione della funzione è dove si definisce effettivamente una funzione.

Facciamo un piccolo esempio di add funzione per comprendere questo concetto in dettaglio.

add :: Integer -> Integer -> Integer   --function declaration 
add x y =  x + y                       --function definition 

main = do 
   putStrLn "The addition of the two numbers is:"  
   print(add 2 5)    --calling a function

Qui, abbiamo dichiarato la nostra funzione nella prima riga e nella seconda riga, abbiamo scritto la nostra funzione effettiva che prenderà due argomenti e produrrà un output di tipo intero.

Come la maggior parte degli altri linguaggi, Haskell inizia a compilare il codice dal mainmetodo. Il nostro codice genererà il seguente output:

The addition of the two numbers is:
7

Pattern Matching

Pattern Matching è il processo di corrispondenza di un tipo specifico di espressioni. Non è altro che una tecnica per semplificare il tuo codice. Questa tecnica può essere implementata in qualsiasi tipo di classe Type. If-Else può essere utilizzato come opzione alternativa di corrispondenza del modello.

Il Pattern Matching può essere considerato come una variante del polimorfismo dinamico in cui in fase di esecuzione possono essere eseguiti diversi metodi a seconda del loro elenco di argomenti.

Dai un'occhiata al seguente blocco di codice. Qui abbiamo utilizzato la tecnica del Pattern Matching per calcolare il fattoriale di un numero.

fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact ( n - 1 ) 

main = do 
   putStrLn "The factorial of 5 is:" 
   print (fact 5)

Sappiamo tutti come calcolare il fattoriale di un numero. Il compilatore inizierà a cercare una funzione chiamata "fact" con un argomento. Se l'argomento non è uguale a 0, il numero continuerà a chiamare la stessa funzione con 1 inferiore a quello dell'argomento effettivo.

Quando il pattern dell'argomento corrisponde esattamente a 0, chiamerà il nostro pattern che è "fact 0 = 1". Il nostro codice produrrà il seguente output:

The factorial of 5 is:
120

Guardie ✔

Guardsè un concetto molto simile al pattern matching. Nella corrispondenza dei modelli, di solito abbiniamo una o più espressioni, ma usiamoguards per testare alcune proprietà di un'espressione.

Sebbene sia consigliabile utilizzare il pattern matching su guards, ma dal punto di vista di uno sviluppatore, guardsè più leggibile e semplice. Per gli utenti alle prime armi,guards possono sembrare molto simili alle istruzioni If-Else, ma sono funzionalmente differenti.

Nel codice seguente abbiamo modificato il nostro factorial programma utilizzando il concetto di guards.

fact :: Integer -> Integer 
fact n | n == 0 = 1 
       | n /= 0 = n * fact (n-1) 
main = do 
   putStrLn "The factorial of 5 is:"  
   print (fact 5)

Qui ne abbiamo dichiarati due guards, separati da "|" e chiamando ilfact funzione da main. Internamente, il compilatore funzionerà nello stesso modo del caso del pattern matching per produrre il seguente output:

The factorial of 5 is:
120

Dove la clausola

Whereè una parola chiave o una funzione incorporata che può essere utilizzata in fase di esecuzione per generare l'output desiderato. Può essere molto utile quando il calcolo della funzione diventa complesso.

Considera uno scenario in cui il tuo input è un'espressione complessa con più parametri. In questi casi, puoi suddividere l'intera espressione in piccole parti utilizzando la clausola "dove".

Nell'esempio seguente, stiamo prendendo un'espressione matematica complessa. Mostreremo come trovare le radici di un'equazione polinomiale [x ^ 2 - 8x + 6] usando Haskell.

roots :: (Float, Float, Float) -> (Float, Float)  
roots (a,b,c) = (x1, x2) where 
   x1 = e + sqrt d / (2 * a) 
   x2 = e - sqrt d / (2 * a) 
   d = b * b - 4 * a * c  
   e = - b / (2 * a)  
main = do 
   putStrLn "The roots of our Polynomial equation are:" 
   print (roots(1,-8,6))

Notare la complessità della nostra espressione per calcolare le radici di una data funzione polinomiale. È abbastanza complesso. Quindi, stiamo rompendo l'espressione usando ilwhereclausola. Il pezzo di codice sopra genererà il seguente output:

The roots of our Polynomial equation are:
(7.1622777,0.8377223)

Funzione di ricorsione

La ricorsione è una situazione in cui una funzione chiama se stessa ripetutamente. Haskell non fornisce alcuna funzionalità per eseguire il loop di qualsiasi espressione per più di una volta. Invece, Haskell vuole che tu suddivida l'intera funzionalità in una raccolta di funzioni diverse e utilizzi la tecnica di ricorsione per implementare la tua funzionalità.

Consideriamo nuovamente il nostro esempio di pattern matching, in cui abbiamo calcolato il fattoriale di un numero. Trovare il fattoriale di un numero è un classico caso di utilizzo della ricorsione. Qui potresti: "In che modo la corrispondenza del pattern è diversa dalla ricorsione?" La differenza tra questi due sta nel modo in cui vengono utilizzati: il pattern matching lavora sulla configurazione del vincolo terminale, mentre la ricorsione è una chiamata di funzione.

Nell'esempio seguente, abbiamo utilizzato sia il pattern matching che la ricorsione per calcolare il fattoriale di 5.

fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact ( n - 1 ) 

main = do 
   putStrLn "The factorial of 5 is:" 
   print (fact 5)

Produrrà il seguente output:

The factorial of 5 is:
120

Funzione di ordine superiore

Fino ad ora, quello che abbiamo visto è che le funzioni Haskell ne prendono una type come input e produrne un altro typecome output, che è abbastanza simile in altri linguaggi imperativi. Le funzioni di ordine superiore sono una caratteristica unica di Haskell in cui è possibile utilizzare una funzione come argomento di input o di output.

Sebbene sia un concetto virtuale, ma nei programmi del mondo reale, ogni funzione che definiamo in Haskell utilizza un meccanismo di ordine superiore per fornire l'output. Se hai la possibilità di esaminare la funzione di libreria di Haskell, scoprirai che la maggior parte delle funzioni di libreria sono state scritte in un modo di ordine superiore.

Facciamo un esempio in cui importeremo una mappa di funzioni di ordine superiore incorporata e utilizzeremo la stessa per implementare un'altra funzione di ordine superiore in base alla nostra scelta.

import Data.Char  
import Prelude hiding (map) 

map :: (a -> b) -> [a] -> [b] 
map _ [] = [] 
map func (x : abc) = func x : map func abc  
main = print $ map toUpper "tutorialspoint.com"

Nell'esempio sopra, abbiamo usato il toUpper funzione della classe di tipo Charper convertire il nostro input in maiuscolo. Qui, il metodo "map" accetta una funzione come argomento e restituisce l'output richiesto. Ecco il suo output:

sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts sh-4.3$ main
"TUTORIALSPOINT.COM"

Espressione lambda

A volte dobbiamo scrivere una funzione che verrà utilizzata una sola volta, per l'intera durata di un'applicazione. Per affrontare questo tipo di situazioni, gli sviluppatori Haskell utilizzano un altro blocco anonimo noto comelambda expression o lambda function.

Una funzione senza avere una definizione è chiamata funzione lambda. Una funzione lambda è indicata dal carattere "\". Prendiamo il seguente esempio in cui aumenteremo il valore di input di 1 senza creare alcuna funzione.

main = do 
   putStrLn "The successor of 4 is:"  
   print ((\x -> x + 1) 4)

Qui abbiamo creato una funzione anonima che non ha un nome. Prende l'intero 4 come argomento e stampa il valore di output. Fondamentalmente stiamo operando una funzione senza nemmeno dichiararla correttamente. Questa è la bellezza delle espressioni lambda.

La nostra espressione lambda produrrà il seguente output:

sh-4.3$ main
The successor of 4 is:
5

Fino ad ora, abbiamo discusso molti tipi di funzioni Haskell e utilizzato modi diversi per chiamare tali funzioni. In questo capitolo, impareremo alcune funzioni di base che possono essere facilmente utilizzate in Haskell senza importare alcuna classe Type speciale. La maggior parte di queste funzioni fa parte di altre funzioni di ordine superiore.

Funzione testa

Headfunzione funziona su una lista. Restituisce il primo dell'argomento di input che è fondamentalmente un elenco. Nell'esempio seguente, stiamo passando una lista con 10 valori e stiamo generando il primo elemento di quella lista usando ilhead funzione.

main = do 
   let x = [1..10]   
   putStrLn "Our list is:"  
   print (x) 
   putStrLn "The first element of the list is:" 
   print (head x)

Produrrà il seguente output:

Our list is: 
[1,2,3,4,5,6,7,8,9,10]
The first element of the list is:
1

Funzione di coda

Tail è la funzione che completa il headfunzione. Ci vuole unlistcome input e restituisce l'intera lista senza la parte di testa. Ciò significa che il filetailla funzione restituisce l'intero elenco senza il primo elemento. Dai un'occhiata al seguente esempio:

main = do 
   let x = [1..10]   
   putStrLn "Our list is:"  
   print (x) 
   putStrLn "The tail of our list is:" 
   print (tail x)

Produrrà il seguente output:

Our list is:
[1,2,3,4,5,6,7,8,9,10]
The tail of our list is:
[2,3,4,5,6,7,8,9,10]

Ultima funzione

Come suggerisce il nome, restituisce l'ultimo elemento dell'elenco fornito come input. Controlla il seguente esempio.

main = do 
   let x = [1..10]   
   putStrLn "Our list is:"  
   print (x) 
   putStrLn "The last element of our list is:" 
   print (last x)

Produrrà il seguente output:

Our list is:
[1,2,3,4,5,6,7,8,9,10]
The last element of our list is:
10

Funzione Init

Init funziona esattamente come l'opposto di tailfunzione. Accetta un elenco come argomento e restituisce l'intero elenco senza l'ultima voce.

main = do 
   let x = [1..10]   
   putStrLn "Our list is:"  
   print (x) 
   putStrLn "Our list without the last entry:"  
   print (init x)

Ora, osserva il suo output -

Our list is:
[1,2,3,4,5,6,7,8,9,10]
Our list without the last entry:
[1,2,3,4,5,6,7,8,9]

Funzione nulla

Null è una funzione di controllo booleano che funziona su una stringa e restituisce True solo quando la lista data è vuota, altrimenti ritorna False. Il codice seguente controlla se l'elenco fornito è vuoto o meno.

main = do 
   let x = [1..10]   
   putStrLn "Our list is:"  
   print (x) 
   putStrLn "Is our list empty?"  
   print (null x)

Produrrà il seguente output:

Our list is:
[1,2,3,4,5,6,7,8,9,10]
Is our list empty?
False

Funzione inversa

Funziona su un input String e converte l'intero input in ordine inverso e fornisce un output come risultato. Di seguito è riportato il codice di base per questa funzione.

main = do 
   let x = [1..10]  
   putStrLn "Our list is:" 
   print (x) 
   putStrLn "The list in Reverse Order is:" 
   print (reverse x)

Produrrà il seguente output:

Our list is:
[1,2,3,4,5,6,7,8,9,10]
The list in Reverse Order is:
[10,9,8,7,6,5,4,3,2,1]

Funzione di lunghezza

Questa funzione viene utilizzata per calcolare la lunghezza del file listdato come argomento. Dai un'occhiata al seguente esempio:

main = do 
   let x = [1..10]   
   putStrLn "Our list is:" 
   print (x) 
   putStrLn "The length of this list is:" 
   print (length x)

Abbiamo 10 elementi nella nostra lista, quindi il nostro codice restituirà 10 come output.

Our list is:
[1,2,3,4,5,6,7,8,9,10]
The length of this list is:
10

Prendi la funzione

Takeviene utilizzata per creare una sottostringa da un'altra stringa. Il codice seguente mostra come utilizzare la funzione Take in Haskell:

main = print(take 5 ([1 .. 10]))

Il codice genera una sottostringa contenente 5 elementi dalla lista fornita -

[1,2,3,4,5]

Funzione Drop

Questa funzione viene utilizzata anche per generare una sottostringa. Funziona come l'opposto ditakefunzione. Guarda il seguente pezzo di codice:

main = print(drop 5 ([1 .. 10]))

Il codice elimina i primi 5 elementi dall'elenco fornito e stampa i restanti 5 elementi. Produrrà il seguente output:

[6,7,8,9,10]

Massima funzionalità

Questa funzione viene utilizzata per trovare l'elemento con il valore massimo dalla lista fornita. Vediamo come usarlo in pratica -

main = do 
   let x = [1,45,565,1245,02,2]   
   putStrLn "The maximum value element of the list is:"  
   print (maximum x)

Il pezzo di codice sopra genererà il seguente output:

The maximum value element of the list is:
1245

Funzione minima

Questa funzione viene utilizzata per trovare l'elemento con il valore minimo dalla lista fornita. È esattamente l'opposto dimaximum funzione.

main = do 
   let x = [1,45,565,1245,02,2]   
   putStrLn "The minimum value element of the list is:"  
   print (minimum x)

L'output del codice sopra è -

The minimum value element of the list is:
1

Funzione Sum

Come suggerisce il nome, questa funzione restituisce la somma di tutti gli elementi presenti nell'elenco fornito. Il codice seguente accetta un elenco di 5 elementi e restituisce la loro somma come output.

main = do 
   let x = [1..5] 
   putStrLn "Our list is:" 
   print (x) 
   putStrLn "The summation of the list elements is:" 
   print (sum x)

Produrrà il seguente output:

Our list is:
[1,2,3,4,5]
The summation of the list elements is:
15

Funzione del prodotto

È possibile utilizzare questa funzione per moltiplicare tutti gli elementi di un elenco e stamparne il valore.

main = do 
   let x = [1..5] 
   putStrLn "Our list is:" 
   print (x) 
   putStrLn "The multiplication of the list elements is:" 
   print (product x)

Il nostro codice produrrà il seguente output:

Our list is:
[1,2,3,4,5]
The multiplication of the list elements is: 
120

Funzione Elem

Questa funzione viene utilizzata per verificare se l'elenco fornito contiene o meno un elemento specifico. Di conseguenza, restituisce un filetrue o a false.

Il codice seguente controlla se l'elenco di elementi fornito contiene il valore 786.

main = do 
   let x = [1,45,155,1785] 
   putStrLn "Our list is:" 
   print (x) 
   putStrLn "Does it contain 786?" 
   print (elem 786 (x))

Produrrà il seguente output:

Our list is:
[1,45,155,1785]
Does it contain 786?
False

Utilizzare lo stesso codice per verificare se l'elenco fornito contiene il valore 1785 o meno.

Function Compositionè il processo di utilizzo dell'output di una funzione come input di un'altra funzione. Sarà meglio se impariamo la matematica che c'è dietrocomposition. In matematica,composition è indicato da f{g(x)} dove g() è una funzione e il suo output viene utilizzato come input di un'altra funzione, ovvero f().

La composizione della funzione può essere implementata utilizzando due funzioni qualsiasi, a condizione che il tipo di output di una funzione corrisponda al tipo di input della seconda funzione. Usiamo l'operatore punto (.) Per implementare la composizione della funzione in Haskell.

Dai un'occhiata al seguente codice di esempio. Qui, abbiamo usato la composizione della funzione per calcolare se un numero di input è pari o dispari.

eveno :: Int -> Bool 
noto  :: Bool -> String 

eveno x = if x `rem` 2 == 0 
   then True 
else False 
noto x = if x == True 
   then "This is an even Number" 
else "This is an ODD number" 

main = do 
   putStrLn "Example of Haskell Function composition" 
   print ((noto.eveno)(16))

Qui, nel main funzione, stiamo chiamando due funzioni, noto e eveno, contemporaneamente. Il compilatore chiamerà prima la funzione"eveno()" con 16come argomento. Successivamente, il compilatore utilizzerà l'output dieveno metodo come input di noto() metodo.

Il suo output sarebbe il seguente:

Example of Haskell Function composition                
"This is an even Number"

Dato che stiamo fornendo il numero 16 come input (che è un numero pari), il eveno() restituisce la funzione true, che diventa l'input per noto() e restituisce l'output: "Questo è un numero pari".

Se hai lavorato su Java, allora sapresti come tutte le classi sono legate in una cartella chiamata package. Allo stesso modo, Haskell può essere considerato come una raccolta di filemodules.

Haskell è un linguaggio funzionale e tutto è indicato come un'espressione, quindi un modulo può essere chiamato come una raccolta di tipi di funzioni simili o correlati.

Puoi importuna funzione da un modulo in un altro modulo. Tutte le istruzioni "import" dovrebbero venire prima prima di iniziare a definire altre funzioni. In questo capitolo, impareremo le diverse caratteristiche dei moduli Haskell.

Modulo elenco

List fornisce alcune meravigliose funzioni con cui lavorare listtipo di dati. Una volta importato il modulo List, hai a disposizione un'ampia gamma di funzioni.

Nell'esempio seguente, abbiamo utilizzato alcune importanti funzioni disponibili nel modulo List.

import Data.List  

main = do  
   putStrLn("Different methods of List Module") 
   print(intersperse '.' "Tutorialspoint.com") 
   print(intercalate " " ["Lets","Start","with","Haskell"]) 
   print(splitAt 7 "HaskellTutorial") 
   print (sort [8,5,3,2,1,6,4,2])

Qui abbiamo molte funzioni senza nemmeno definirle. Questo perché queste funzioni sono disponibili nel modulo List. Dopo aver importato il modulo List, il compilatore Haskell ha reso disponibili tutte queste funzioni nello spazio dei nomi globale. Quindi, potremmo usare queste funzioni.

Il nostro codice produrrà il seguente output:

Different methods of List Module
"T.u.t.o.r.i.a.l.s.p.o.i.n.t...c.o.m"
"Lets Start with Haskell"
("Haskell","Tutorial")
[1,2,2,3,4,5,6,8]

Modulo Char

Il Charha molte funzioni predefinite per lavorare con il tipo di carattere. Dai un'occhiata al seguente blocco di codice:

import Data.Char 

main = do  
   putStrLn("Different methods of Char Module") 
   print(toUpper 'a') 
   print(words "Let us study tonight") 
   print(toLower 'A')

Qui, le funzioni toUpper e toLower sono già definiti all'interno del file Charmodulo. Produrrà il seguente output:

Different methods of Char Module
'A'
["Let","us","study","tonight"]
'a'

Modulo mappa

Mapè un tipo di dati di tipo coppia a valore aggiunto non ordinato. È un modulo ampiamente utilizzato con molte funzioni utili. L'esempio seguente mostra come utilizzare una funzione predefinita disponibile nel modulo Mappa.

import Data.Map (Map) 
import qualified Data.Map as Map  --required for GHCI  

myMap :: Integer -> Map Integer [Integer] 
myMap n = Map.fromList (map makePair [1..n]) 
   where makePair x = (x, [x])  

main = print(myMap 3)

Produrrà il seguente output:

fromList [(1,[1]),(2,[2]),(3,[3])]

Imposta modulo

Il modulo Set ha alcune funzioni predefinite molto utili per manipolare dati matematici. Un insieme è implementato come un albero binario, quindi tutti gli elementi in un insieme devono essere univoci.

Dai un'occhiata al seguente codice di esempio

import qualified Data.Set as Set   

text1 = "Hey buddy"   
text2 = "This tutorial is for Haskell"   

main = do  
   let set1 = Set.fromList text1   
       set2 = Set.fromList text2 
   print(set1) 
   print(set2)

Qui stiamo modificando una String in un Set. Produrrà il seguente output. Si noti che il set di output non ha ripetizioni di caratteri.

fromList " Hbdeuy"
fromList " HTaefhiklorstu"

Modulo personalizzato

Vediamo come possiamo creare un modulo personalizzato che può essere chiamato in altri programmi. Per implementare questo modulo personalizzato, creeremo un file separato chiamato"custom.hs" insieme al nostro "main.hs".

Creiamo il modulo personalizzato e definiamo alcune funzioni in esso.

custom.hs

module Custom ( 
   showEven, 
   showBoolean 
) where 

showEven:: Int-> Bool 
showEven x = do 

if x 'rem' 2 == 0 
   then True 
else False 
showBoolean :: Bool->Int 
showBoolean c = do 

if c == True 
   then 1 
else 0

Il nostro modulo personalizzato è pronto. Ora, importiamolo in un programma.

main.hs

import Custom 

main = do 
   print(showEven 4) 
   print(showBoolean True)

Il nostro codice genererà il seguente output:

True
1

Il showEven restituisce la funzione True, poiché "4" è un numero pari. IlshowBoolean funzione restituisce "1" poiché la funzione booleana che abbiamo passato alla funzione è "True".

Tutti gli esempi che abbiamo discusso finora sono di natura statica. In questo capitolo impareremo a comunicare dinamicamente con gli utenti. Impareremo diverse tecniche di input e output utilizzate in Haskell.

File e flussi

Finora abbiamo codificato tutti gli input nel programma stesso. Abbiamo preso input da variabili statiche. Ora impariamo a leggere e scrivere da un file esterno.

Creiamo un file e chiamiamolo "abc.txt". Quindi, inserisci le seguenti righe in questo file di testo: "Benvenuto in Tutorialspoint. Qui troverai la migliore risorsa per imparare Haskell."

Successivamente, scriveremo il seguente codice che visualizzerà il contenuto di questo file sulla console. Qui stiamo usando la funzione readFile () che legge un file finché non trova un carattere EOF.

main = do  
   let file = "abc.txt" 
   contents <- readFile file 
   putStrLn contents

La parte di codice precedente leggerà il file "abc.txt" come una stringa fino a quando non incontra un carattere di fine file. Questa parte di codice genererà il seguente output.

Welcome to Tutorialspoint
Here, you will get the best resource to learn Haskell.

Osserva che qualunque cosa stia stampando sul terminale è scritta in quel file.

Argomento della riga di comando

Haskell fornisce anche la possibilità di gestire un file tramite il prompt dei comandi. Torniamo al nostro terminale e scriviamo"ghci". Quindi, digita il seguente set di comandi:

let file = "abc.txt" 
writeFile file "I am just experimenting here." 
readFile file

Qui abbiamo creato un file di testo chiamato "abc.txt". Successivamente, abbiamo inserito un'istruzione nel file utilizzando il comandowriteFile. Infine, abbiamo utilizzato il comandoreadFileper stampare il contenuto del file sulla console. Il nostro codice produrrà il seguente output:

I am just experimenting here.

Eccezioni

Un exceptionpuò essere considerato un bug nel codice. È una situazione in cui il compilatore non ottiene l'output previsto in fase di esecuzione. Come ogni altro buon linguaggio di programmazione, Haskell fornisce un modo per implementare la gestione delle eccezioni.

Se hai familiarità con Java, potresti conoscere il blocco Try-Catch in cui di solito generiamo un errore e catturiamo lo stesso nel catchbloccare. In Haskell, abbiamo anche la stessa funzione per rilevare gli errori di runtime.

La definizione della funzione di tryè simile a "try :: Exception e => IO a -> IO (Either ea)". Dai un'occhiata al seguente codice di esempio. Mostra come catturare l'eccezione "Divide by Zero".

import Control.Exception 

main = do 
   result <- try (evaluate (5 `div` 0)) :: IO (Either SomeException Int) 
   case result of 
      Left ex   -> putStrLn $ "Caught exception: " ++ show ex 
      Right val -> putStrLn $ "The answer was: " ++ show val

Nell'esempio sopra, abbiamo usato il file integrato try funzione del Control.Exceptionmodulo, quindi stiamo intercettando l'eccezione in anticipo. La parte di codice sopra restituirà l'output sotto lo schermo.

Caught exception: divide by zero

Functorin Haskell è una sorta di rappresentazione funzionale di diversi tipi che possono essere mappati. È un concetto di alto livello di implementazione del polimorfismo. Secondo gli sviluppatori Haskell, tutti i tipi come List, Map, Tree, ecc.Sono l'istanza del Haskell Functor.

UN Functor è una classe incorporata con una definizione di funzione come -

class Functor f where 
   fmap :: (a -> b) -> f a -> f b

Con questa definizione, possiamo concludere che il file Functor è una funzione che prende una funzione, diciamo, fmap()e restituisce un'altra funzione. Nell'esempio sopra,fmap() è una rappresentazione generalizzata della funzione map().

Nel seguente esempio, vedremo come funziona Haskell Functor.

main = do  
   print(map (subtract 1) [2,4,8,16])      
   print(fmap (subtract 1) [2,4,8,16])

Qui abbiamo usato entrambi map() e fmap()su un elenco per un'operazione di sottrazione. Puoi osservare che entrambe le istruzioni produrranno lo stesso risultato di una lista contenente gli elementi [1,3,7,15].

Entrambe le funzioni chiamano un'altra funzione chiamata subtract() per ottenere il risultato.

[1,3,7,15]
[1,3,7,15]

Allora, qual è la differenza tra map e fmap? La differenza sta nel loro utilizzo. Functor ci consente di implementare più funzionalisti in diversi tipi di dati, come "just" e "Nothing".

main = do 
   print (fmap  (+7)(Just 10)) 
   print (fmap  (+7) Nothing)

La parte di codice precedente produrrà il seguente output sul terminale:

Just 17
Nothing

Funtore applicativo

Un Functor applicativo è un normale Functor con alcune funzionalità extra fornite dalla classe del tipo applicativo.

Usando Functor, di solito mappiamo una funzione esistente con un'altra funzione definita al suo interno. Ma non c'è alcun modo per mappare una funzione definita all'interno di un Functor con un altro Functor. Ecco perché abbiamo chiamato un'altra strutturaApplicative Functor. Questa funzionalità di mappatura è implementata dalla classe Applicative Type definita inControlmodulo. Questa classe ci fornisce solo due metodi con cui lavorare: uno èpure e l'altro è <*>.

Di seguito è riportata la definizione della classe del Functor applicativo.

class (Functor f) => Applicative f where   
   pure :: a -> f a   
   (<*>) :: f (a -> b) -> f a -> f b

Secondo l'implementazione, possiamo mappare un altro Functor usando due metodi: "Pure" e "<*>". Il metodo "Pure" dovrebbe assumere un valore di qualsiasi tipo e restituirà sempre un Functor applicativo di quel valore.

L'esempio seguente mostra come funziona un Funtore applicativo:

import Control.Applicative 

f1:: Int -> Int -> Int 
f1 x y = 2*x+y  
main = do  
   print(show $ f1 <$> (Just 1) <*> (Just 2) )

Qui, abbiamo implementato funtori applicativi nella chiamata di funzione della funzione f1. Il nostro programma produrrà il seguente output.

"Just 4"

Monoidi

Sappiamo tutti che Haskell definisce tutto sotto forma di funzioni. Nelle funzioni, abbiamo opzioni per ottenere il nostro input come output della funzione. Questo è ciò che aMonoid è.

UN Monoidè un insieme di funzioni e operatori in cui l'output è indipendente dal suo input. Prendiamo una funzione (*) e un intero (1). Ora, qualunque sia l'input, il suo output rimarrà solo lo stesso numero. Cioè, se moltiplichi un numero per 1, otterrai lo stesso numero.

Ecco una definizione di classe tipo di monoide.

class Monoid m where  
   mempty :: m 
   mappend :: m -> m -> m  
   mconcat :: [m] -> m 
   mconcat = foldr mappend mempty

Dai un'occhiata al seguente esempio per comprendere l'uso di Monoid in Haskell.

multi:: Int->Int 
multi x = x * 1 
add :: Int->Int 
add x = x + 0 

main = do  
   print(multi 9)  
   print (add 7)

Il nostro codice produrrà il seguente output:

9
7

In questo caso, la funzione "multi" moltiplica l'ingresso per "1". Allo stesso modo, la funzione "aggiungi" aggiunge l'input con "0". In entrambi i casi, l'output sarà lo stesso dell'input. Quindi, le funzioni{(*),1} e {(+),0} sono gli esempi perfetti di monoidi.

Monadsnon sono altro che un tipo di Funtore Applicativo con alcune funzionalità extra. È una classe Type che governa tre regole di base note comemonadic rules.

Tutte e tre le regole sono strettamente applicabili su una dichiarazione della Monade che è la seguente:

class Monad m where  
   return :: a -> m a 
   (>>=) :: m a -> (a -> m b) -> m b 
   (>>) :: m a -> m b -> m b 
   x >> y = x >>= \_ -> y 
   fail :: String -> m a  
   fail msg = error msg

Le tre leggi fondamentali che sono applicabili su una dichiarazione della Monade sono:

  • Left Identity Law - Il returnla funzione non cambia il valore e non dovrebbe cambiare nulla nella Monade. Può essere espresso come "return> => mf = mf".

  • Right Identity Law - Il returnla funzione non cambia il valore e non dovrebbe cambiare nulla nella Monade. Può essere espresso come "mf> => return = mf".

  • Associativity- Secondo questa legge, sia i Functors che l'istanza di Monad dovrebbero funzionare allo stesso modo. Può essere espresso matematicamente come "(f> ==> g)> => h = f> => (g> = h)".

Le prime due leggi ripetono lo stesso punto, cioè a return dovrebbe avere un comportamento di identità su entrambi i lati del file bind operatore.

Abbiamo già utilizzato molte Monadi nei nostri esempi precedenti senza renderci conto che sono Monadi. Considera il seguente esempio in cui stiamo usando una List Monad per generare una lista specifica.

main = do
   print([1..10] >>= (\x -> if odd x then [x*2] else []))

Questo codice produrrà il seguente output:

[2,6,10,14,18]

Zippers in Haskell sono fondamentalmente dei puntatori che puntano a una posizione specifica di una struttura dati come un file tree.

Consideriamo a tree avere 5 elementi [45,7,55,120,56]che può essere rappresentato come un perfetto albero binario. Se voglio aggiornare l'ultimo elemento di questo elenco, devo attraversare tutti gli elementi per raggiungere l'ultimo elemento prima di aggiornarlo. Destra?

Ma cosa succederebbe se potessimo costruire il nostro albero in modo tale che un albero dell'avere N elementi è una raccolta di [(N-1),N]. Quindi, non è necessario attraversare tutto ciò che non si desidera(N-1)elementi. Possiamo aggiornare direttamente l'ennesimo elemento. Questo è esattamente il concetto di Zipper. Si concentra o punta a una posizione specifica di un albero dove possiamo aggiornare quel valore senza attraversare l'intero albero.

Nell'esempio seguente, abbiamo implementato il concetto di Zipper in a List. Allo stesso modo, si può implementare Zipper in un filetree o a file struttura dati.

data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
type Zipper_List a = ([a],[a])    

go_Forward :: Zipper_List a -> Zipper_List a   
go_Forward (x:xs, bs) = (xs, x:bs)   
   
go_Back :: Zipper_List a -> Zipper_List a   
go_Back (xs, b:bs) = (b:xs, bs)    

main = do 
   let list_Ex = [1,2,3,4] 
   print(go_Forward (list_Ex,[]))       
   print(go_Back([4],[3,2,1]))

Quando compili ed esegui il programma sopra, produrrà il seguente output:

([2,3,4],[1]) 
([3,4],[2,1])

Qui ci stiamo concentrando su un elemento dell'intera stringa mentre andiamo avanti o mentre torniamo indietro.