Caratteri

In Haskell le costanti carattere si introducono scrivendo i caratteri direttamente tra singoli apici.

'a'
'b'
'a' == 'A'

Le costanti per i caratteri “invisibili” si specificano facendo uso delle stesse sequenze di escape riconosciute da altri linguaggi di programmazione. Le più comuni sono:

'\n' -- ritorno a capo
'\t' -- tabulazione
'\\' -- backslash
'\'' -- apice singolo
'\"' -- doppi apici

È possibile specificare caratteri anche attraverso il loro codice Unicode.

'\32'   -- spazio
'\48'   -- cifra decimale 0
'λ'     -- lettera greca λ
'\x3BB' -- lettera greca λ

Nell’ultimo esempio la x indica che il codice Unicode è scritto in base 16, mentre di norma (come nei primi due esempi) è in base 10.

Il tipo dei caratteri si chiama Char ed è distinto dal (e incompatibile con il) tipo dei numeri interi.

:type '0'

Non esistono conversioni implicite da caratteri a numeri interi e viceversa. Queste conversioni sono realizzate dalle funzioni ord e chr della libreria standard. Per utilizzare queste funzioni è necessario importarle dal modulo Data.Char in cui sono definite, scrivendo la seguente clausola all’inizio dello script nel quale si intende utilizzarle.

import Data.Char (ord, chr)
ord '0'
chr 48

Stringhe

In Haskell le stringhe si introducono scrivendone i caratteri racchiusi tra doppi apici.

""
"Haskell"
"Questa è una stringa con spazi"

Il tipo delle liste si chiama String e in Haskell tale tipo è solo un alias per il tipo [Char]. In altre parole, le stringhe sono rappresentate come liste di caratteri.

['H', 'a', 's', 'k', 'e', 'l', 'l']
[] == ""
['x', 'y', 'z'] /= "xyz"

Tutte le funzioni definite sulle liste sono applicabili anche alle stringhe. Per esempio, è possibile usare la funzione length per calcolare la lunghezza di una stringa e l’operatore ++ per concatenare due stringhe.

length "xyz"
"Has" ++ "kell"

Esercizi

Il simbolo indica una potenziale difficoltà.

  1. Definire una funzione showHex :: Int -> String che, applicata a un numero intero, ne restituisca la rappresentazione in base 16. Fare attenzione alla possibilità che il numero sia negativo. Suggerimento: definire una funzione ausiliaria per convertire un numero intero compreso tra 0 e 15 (estremi inclusi) nel carattere corrispondente in base 16.
    showHex :: Int -> String
    showHex n | n < 0     = '-' : showHex (negate n)
              | n < 16    = [aux n]
              | otherwise = showHex (n `div` 16) ++ [aux (n `mod` 16)]
      where
        aux n | n < 10    = chr (ord '0' + n)
              | otherwise = chr (ord 'A' + n - 10)
    
  2. Senza fare uso di funzioni della libreria standard ad eccezione di ord e chr, definire una funzione readHex :: String -> Int che, applicata a una stringa di cifre esadecimali, restituisca il numero intero (non negativo) corrispondente. Prestare attenzione alla possibilità che le cifre decimali siano minuscole o maiuscole.
    readHex :: String -> Int
    readHex = aux 0
      where
        aux res [] = res
        aux res (c : cs) = aux (res * 16 + readChar c) cs
    
        readChar c | c >= '0' && c <= '9' = ord c - ord '0'
                   | c >= 'a' && c <= 'f' = ord c - ord 'a' + 10
                   | c >= 'A' && c <= 'F' = ord c - ord 'A' + 10
    
  3. Ripetere l’esercizio precedente, questa volta senza fare uso esplicito della ricorsione ma potendo usare tutte le funzioni della libreria standard.
    readHex :: String -> Int
    readHex = foldl (\res c -> res * 16 + readChar c) 0
      where
        readChar c | c >= '0' && c <= '9' = ord c - ord '0'
                   | c >= 'a' && c <= 'f' = ord c - ord 'a' + 10
                   | c >= 'A' && c <= 'F' = ord c - ord 'A' + 10