Come definire una funzione in ghci su più linee?


161

Sto cercando di definire qualsiasi funzione semplice che si estende su più righe in ghci, prendere il seguente esempio:

let abs n | n >= 0 = n
          | otherwise = -n

Finora ho provato a premere Invio dopo la prima riga:

Prelude> let abs n | n >= 0 = n
Prelude>           | otherwise = -n
<interactive>:1:0: parse error on input `|'

Ho anche provato a usare i comandi :{e :}ma non riesco ad andare lontano:

Prelude> :{
unknown command ':{'
use :? for help.

Sto usando GHC Interactive versione 6.6 per Haskell 98 su Linux, cosa mi sto perdendo?


20
Si prega di aggiornare l'installazione di GHC. GHC 6.6 ha quasi 5 anni! Le ultime versioni di Haskell sono qui: haskell.org/platform
Don Stewart,

possibile duplicato di comandi multilinea in GHCi
mmmmmm

1
@Mark Questo OP ha già provato le soluzioni a questo problema. Questo problema è dovuto a un ghci obsoleto, non alla mancanza di conoscenza di cosa fare. Soluzione qui: aggiornamento. Soluzione lì: l'uso :{, :}.
AndrewC,

Risposte:


124

Per le guardie (come il tuo esempio), puoi semplicemente metterle tutte su una linea e funziona (le guardie non si preoccupano della spaziatura)

let abs n | n >= 0 = n | otherwise = -n

Se si desidera scrivere la propria funzione con più definizioni che corrispondono allo schema sugli argomenti, in questo modo:

fact 0 = 1
fact n = n * fact (n-1)

Quindi utilizzare le parentesi graffe con punto e virgola che separano le definizioni

let { fact 0 = 1 ; fact n = n * fact (n-1) }

258

GHCi ora ha una modalità di input multilinea, abilitata con: set + m. Per esempio,

Prelude> :set +m
Prelude> let fac 0 = 1
Prelude|     fac n = n * fac (n-1)
Prelude|
Prelude> fac 10
3628800

39
L'impostazione della modalità multilinea si ghcicomporta in modo molto simile all'interprete Python in questo senso. Molto conveniente! Puoi infatti creare un .ghcifile nella tua home directory in cui metti :set +me la modalità multilinea diventerà quella predefinita ogni volta che inizi ghci!
kqr

2
Questo è davvero fantastico. Ma ho notato che quando ho impostato il mio prompt usando :set prompt "λ "le righe continue dire Preludeinvece di λ. Un modo per aggirare questo?
Abhillman,

2
Vedi qui per la patch per definire un nuovo prompt di continuazione ghc.haskell.org/trac/ghc/ticket/7509#no1
karakfa,

4
Per evitare che Prelude appaia sulle righe di continuazione, aggiungi anche: imposta prompt2 "|" nel tuo .ghci.
Nick,

12
Puoi evitare completamente il rientro usando un trascinamento let. Basta digitare a letseguito da una nuova riga: let⏎. Quindi fac 0 = 1⏎. Quindi fac n = n * fac (n-1)⏎ ⏎ e il gioco è fatto!
Iceland_jack,

62

Dan ha ragione, ma :{e :}ognuno deve apparire sulla propria riga:

> :{ 
> let foo a b = a +
>           b
> :}
> :t foo
foo :: (Num a) => a -> a -> a

Ciò interagisce anche con la regola di layout, quindi quando si utilizza la notazione, potrebbe essere più semplice utilizzare esplicitamente parentesi graffe e punti e virgola. Ad esempio, questa definizione ha esito negativo:

> :{
| let prRev = do
|   inp <- getLine
|   putStrLn $ reverse inp
| :}
<interactive>:1:18:
    The last statement in a 'do' construct must be an expression

Funziona quando vengono aggiunte parentesi graffe e punti e virgola:

> :{
| let prRev = do {
|   inp <- getLine;
|   putStrLn $ reverse inp;
| }
| :}
> :t prRev
prRev :: IO ()

Questo avrà davvero importanza solo quando si incollano le definizioni da un file, dove il rientro potrebbe cambiare.


Questo non funziona se hai una riga che termina con '=' (con la definizione che segue nella riga successiva), almeno nella versione 7.6.3.
AdamC

1
Forse questo fallisce, perché la seconda e la terza riga del let non sono abbastanza rientrate ...? (Altri due spazi.)
Evi1M4chine


7

Se non desideri aggiornare GHC solo per :{e :}, dovrai scrivere tutto su una riga:

> let abs' n | n >= 0 = n | otherwise = -n

Non sono a conoscenza di alcuna definizione singola in Haskell che deve essere scritta su più righe. Quanto sopra funziona davvero in GHCi:

> :t abs'
abs' :: (Num a, Ord a) => a -> a

Per altre espressioni, come i doblocchi, è necessario utilizzare la sintassi non layout con parentesi graffe e punti e virgola (eugh).


0

Sto usando GHCi, versione 8.2.1 su macOS Catalina 10.15.2. Di seguito è riportato il modo in cui metto insieme la dichiarazione del tipo di funzione e le protezioni. Nota che le barre verticali a sinistra sono per più linee GHCi.

λ: let abs' :: (Num a, Ord a) => a -> a
 |     abs' n | n >= 0 = n | otherwise = -n
 | 
λ: abs' 7
7
λ: abs' (-7)
7

1
Se si utilizza :{e :}non è necessario specificare let prima della dichiarazione del tipo, il che significa che non è necessario rientrare alla seconda e alla riga successiva.
David,

Grazie mille David. È esattamente quello che stavo cercando ma non sono riuscito a trovare.
Pollice d'oro

0

Sembra che incollare entrambe le linee contemporaneamente o usare control-enter per ogni nuova linea sia tutto insieme, almeno su https://repl.it/languages/haskell . Vedrai 2 punti all'inizio della seconda riga. Oppure inseriscilo in un file e: carica il file (: l main). Come mai gli addominali non funzionano con i numeri negativi? Oh, devi mettere le parentesi attorno al numero.

   let abs n | n >= 0 = n 
..           | otherwise = -n
   abs (-1)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.