Jak zdefiniować funkcję w ghci w wielu wierszach?


161

Próbuję zdefiniować dowolną prostą funkcję, która obejmuje wiele linii w ghci, weźmy jako przykład:

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

Do tej pory próbowałem nacisnąć Enter po pierwszej linii:

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

Próbowałem również użyć poleceń :{i :}, ale nie zajdę daleko:

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

Używam GHC Interactive w wersji 6.6 dla Haskell 98 w systemie Linux. Czego mi brakuje?


20
Zaktualizuj swoją instalację GHC. GHC 6.6 ma prawie 5 lat! Najnowsze wersje Haskell są tutaj: haskell.org/platform
Don Stewart


1
@Mark Ten OP już wypróbował rozwiązania tego problemu. Ten problem jest spowodowany nieaktualnym ghci, a nie brakiem wiedzy o tym, co robić. Rozwiązanie tutaj: uaktualnij. Rozwiązanie tam: stosowanie :{, :}.
AndrewC

Odpowiedzi:


124

W przypadku strażników (takich jak twój przykład) możesz po prostu umieścić je wszystkie w jednej linii i działa (strażnicy nie dbają o odstępy)

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

Jeśli chcesz napisać swoją funkcję z wieloma definicjami, które pasują do wzorca w argumentach, na przykład:

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

Następnie użyjesz nawiasów klamrowych ze średnikami oddzielającymi definicje

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

258

GHCi ma teraz tryb wejścia wieloliniowego, włączany za pomocą: set + m. Na przykład,

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

39
Ustawienie trybu wielowierszowego sprawia, że ghcipod tym względem zachowuje się podobnie jak interpreter Pythona. Bardzo wygodne! W rzeczywistości możesz utworzyć .ghciplik w swoim katalogu domowym, w którym umieścisz, :set +ma tryb wielowierszowy stanie się domyślny za każdym razem, gdy zaczniesz ghci!
kqr

2
To jest naprawdę niesamowite. Ale zauważyłem, że kiedy ustawię znak zachęty, używając :set prompt "λ "linii ciągłych, powiedz Preludezamiast λ. Jakoś to obejść?
abhillman

2
Zobacz tutaj, aby zobaczyć poprawkę definiującą nowy monit o kontynuację ghc.haskell.org/trac/ghc/ticket/7509#no1
karakfa

4
Aby zapobiec pojawianiu się Prelude w liniach kontynuacji, dodaj także: ustaw prompt2 "|" w swoim .ghci.
Nick

12
Możesz całkowicie uniknąć wcięć, używając końcowego let. Po prostu wpisz a, letpo którym następuje nowa linia: let⏎. Następnie fac 0 = 1⏎. Następnie fac n = n * fac (n-1)⏎ ⏎ i gotowe!
Iceland_jack

62

Dan ma rację, ale :{i :}każdy musi pojawić się w osobnym wierszu:

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

To również współdziała z regułą układu, więc podczas korzystania z notacji do wykonania może być łatwiej jawnie użyć nawiasów klamrowych i średników. Na przykład ta definicja zawodzi:

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

Ale działa po dodaniu nawiasów klamrowych i średników:

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

Będzie to miało znaczenie tylko w przypadku wklejania definicji z pliku, gdzie wcięcie może się zmienić.


To nie działa, jeśli masz linię kończącą się na '=' (z definicją podaną w następnej linii), przynajmniej w wersji 7.6.3.
AdamC

1
Może się to nie udaje, bo druga i trzecia linijka let nie są wystarczająco wcięte…? (
Jeszcze


7

Jeśli nie chcesz aktualizować GHC tylko dla :{i :}, musisz zapisać wszystko w jednym wierszu:

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

Nie znam żadnej definicji w Haskell, która musi być zapisana w wielu wierszach. Powyższe rzeczywiście działa w GHCi:

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

W przypadku innych wyrażeń, takich jak dobloki, musisz użyć składni innej niż układ z nawiasami klamrowymi i średnikami (eugh).


0

Używam GHCi w wersji 8.2.1 na macOS Catalina 10.15.2. Poniżej opisano, w jaki sposób zestawiłem razem deklarację typu funkcji i osłony. Zwróć uwagę, że pionowe paski po lewej stronie dotyczą wielu linii GHCi.

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

1
Jeśli używasz :{i :}nie musisz określać let przed deklaracją typu, co oznacza, że ​​nie musisz wciskać drugiego i kolejnych wierszy.
davidA

Dziękuję bardzo davida. Dokładnie tego szukałem, ale nie udało mi się znaleźć.
Golden Thumb

0

Wygląda na to, że wklejenie obu wierszy na raz lub użycie Ctrl-Enter dla każdego nowego wiersza utrzymuje wszystko razem, przynajmniej na https://repl.it/languages/haskell . Na początku drugiej linii zobaczysz 2 kropki. Lub umieść go w pliku i: załaduj plik (: l main). Dlaczego abs nie działa z liczbami ujemnymi? Och, musisz umieścić liczbę w nawiasach.

   let abs n | n >= 0 = n 
..           | otherwise = -n
   abs (-1)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.