Как определить функцию в ghci через несколько строк?


161

Я пытаюсь определить любую простую функцию, которая занимает несколько строк в GHCI, взять в качестве примера следующее:

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

До сих пор я пытался нажать Enter после первой строки:

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

Я также попытался использовать :{и :}команды , но я не получаю далеко:

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

Я использую GHC Interactive версии 6.6 для Haskell 98 в Linux, чего мне не хватает?


20
Пожалуйста, обновите вашу установку GHC. GHC 6.6 почти 5 лет! Последние версии Haskell находятся здесь: haskell.org/platform
Дон Стюарт

возможный дубликат многострочных команд в
GHCi

1
@Mark Этот OP уже попробовал решения этой проблемы. Эта проблема связана с устаревшей ghci, а не недостатком знаний о том, что делать. Решение здесь: обновить. Решение есть: использование :{, :}.
AndrewC

Ответы:


124

Для охранников (как ваш пример), вы можете просто поместить их всех в одну строку, и это работает (охранники не заботятся о расстоянии)

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

Если вы хотите написать свою функцию с несколькими определениями, этот шаблон соответствует аргументам, например так:

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

Тогда вы бы использовали фигурные скобки с точкой с запятой, разделяющие определения

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

258

GHCi теперь имеет режим многострочного ввода, который включается с помощью: set + m. Например,

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

39
Настройка многострочного режима делает ghciповедение во многом похожим на интерпретатор Python. Очень удобно! На самом деле вы можете создать .ghciфайл в своем домашнем каталоге, в который вы положите, :set +mи многострочный режим станет по умолчанию при каждом запуске ghci!
Kqr

2
Это действительно круто. Но я заметил, что, когда я установил подсказку, используя :set prompt "λ "продолжение, скажем, Preludeвместо λ. Есть ли способ обойти это?
Абхиллман

2
Смотрите здесь патч для определения новой подсказки продолжения ghc.haskell.org/trac/ghc/ticket/7509#no1
karakfa

4
Чтобы предотвратить появление Prelude в строках продолжения, также добавьте: set prompt2 "|" в вашем .ghci.
Ник

12
Вы можете полностью избежать отступов, используя трейлинг let. Просто введите с letпоследующим символом новой строки: let⏎. Тогда fac 0 = 1⏎. Тогда fac n = n * fac (n-1)⏎ ⏎ и все готово!
Iceland_jack

62

Дэн прав, но :{и :}каждый должен появляться в своей строке:

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

Это также взаимодействует с правилом макета, поэтому при использовании нотации может быть проще явно использовать фигурные скобки и точки с запятой. Например, это определение не выполняется:

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

Но это работает, когда добавляются фигурные скобки и точки с запятой:

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

Это будет действительно иметь значение только при вставке определений из файла, где может измениться отступ.


Это не работает, если у вас есть строка, заканчивающаяся на '=' (с определением, следующим за следующей строкой), по крайней мере, в версии 7.6.3.
AdamC

1
Возможно, это не удастся, потому что вторая и третья строки let не достаточно отступы…? (Еще два пробела.)
Evi1M4chine


7

Если вы не хотите , чтобы обновить GHC только для :{и :}вы должны будете написать все это на одной строке:

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

Я не знаю ни одного определения в Haskell, которое должно быть написано в несколько строк. Вышесказанное действительно работает в GHCi:

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

Для других выражений, таких как doблоки, вам нужно использовать синтаксис без макета с фигурными скобками и точками с запятой (eugh).


0

Я использую GHCi, версия 8.2.1 на macOS Catalina 10.15.2. Ниже показано, как я объединил и объявление типа функции, и охрану. Обратите внимание, что вертикальные полосы слева предназначены для нескольких строк GHCi.

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

1
Если вы используете :{и :}вам не нужно указывать let перед объявлением типа, это означает, что вам не нужно делать отступ во второй и последующих строках.
Давида

Большое спасибо, Дэвид. Это именно то, что я искал, но не смог найти.
Золотой большой палец

0

Похоже, что вставка обеих строк одновременно или использование control-enter для каждой новой строки позволяет сохранить все это вместе, по крайней мере, по адресу https://repl.it/languages/haskell . Вы увидите 2 точки в начале второй строки. Или поместите его в файл и: загрузите файл (: l main). Почему пресс не работает с отрицательными числами? О, вы должны поставить круглые скобки вокруг числа.

   let abs n | n >= 0 = n 
..           | otherwise = -n
   abs (-1)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.