Я новичок в Хаскеле. Это мой парсер:
data Parser a = MkParser (String -> Maybe a)
Это анализирует любую строку, дает первый символ:
-- anyChar
anyChar :: Parser Char
anyChar = MkParser sf
where
sf "" = Nothing
sf (c:cs) = Just c
Оно работает. Сейчас я учусь по учебнику. Там говорится, что я могу преобразовать ответ парсера и запустить его через функцию, вот так (создание нового парсера):
-- convert a parsers answer based on a function
convert :: (a -> b) -> Parser a -> Parser b
convert f (MkParser p1) = MkParser sf
where
sf inp = case p1 inp of
Nothing -> Nothing
Just x -> Just (f x)
Похоже, это работает! Здесь я понял, что могу получить доступ к входной строке в своих функциях синтаксического анализа. (Несмотря на то, что определение convert не принимает входную строку. Я хочу воссоздать anyChar, чтобы он также использовал входную строку (просто чтобы я мог понять синтаксис и что происходит). (Я пришел с помощью Python и Я новичок в Haskell)
Это то, что я попробовал
-- apparently we can use "inp" to signifiy the input string
-- let's recreate anyChar to use input
anyCharInp :: Parser Char
anyCharInp = MkParser sf
where
case inp of
(c:cs) -> Just c
_ -> Nothing
Но выдает ошибку отступа. Есть идеи?





Вы близки. Давайте сосредоточимся на этой части:
anyCharInp :: Parser Char
anyCharInp = MkParser sf
Здесь используется переменная sf, но она не определяет ее в этой строке, поэтому нам нужно определить ее ниже, используя where.
where
case inp of
(c:cs) -> Just c
_ -> Nothing
Здесь мы не находим определения sf. Обратите внимание, что после where компилятор ожидает определения, а не выражения, и выдаст ошибку, если не найдет их. Давайте это исправим:
anyCharInp :: Parser Char
anyCharInp = MkParser sf
where
sf inp = case inp of
(c:cs) -> Just c
_ -> Nothing
Теперь sf определен. Также не известно, как sf является функцией, принимающей inp в качестве аргумента. Это необходимо, поскольку нам нужно определить inp, прежде чем мы сможем использовать case inp of ....
Возможны несколько альтернатив. Вот тот, который позволяет избежать case of:
anyCharInp :: Parser Char
anyCharInp = MkParser sf
where
sf (c:cs) = Just c
sf _ = Nothing
(Однако это очень похоже на то, что вы опубликовали.)
Вот еще один пример использования лямбда-выражения, чтобы избежать where.
anyCharInp :: Parser Char
anyCharInp = MkParser (\inp ->
case inp of
(c:cs) -> Just c
_ -> Nothing
)
Существуют и другие альтернативы, но я считаю, что это наиболее распространенные.
@ user20102550 Здесь говорится, что MkParser принимает функцию в качестве аргумента. Эта функция принимает строку в качестве аргумента. Когда вы применяете MkParser, вы не предоставляете строку и не имеете к ней доступа, вы просто предоставляете функцию. Однако один из способов определить функцию — написать правило, определяющее, что она будет возвращать при вызове с аргументом, что и делает синтаксис типа where sf inp = .... sf — это имя функции, которую вы определяете, а inp — это имя гипотетической строки, которую она получит, когда/если она когда-либо будет вызвана, а не какой-либо конкретной строки, которая существует «сейчас».
@user20102550 user20102550 Легко думать об этом как о простом «получении доступа к входной строке» (и разумно использовать в качестве мысленного сокращения). Но, конечно, сам Parser не может предоставить вам доступ к входной строке, потому что он понятия не имеет, какой текст вы в конечном итоге попытаетесь проанализировать! Это происходит позже, в коде, использующем синтаксический анализатор (что может произойти несколько раз с разными входными данными или никогда, поэтому такой вещи, как входная строка, не существует). Следовательно, почему парсеры содержат функции, говорящие, как они могут обрабатывать входные данные, если/когда они есть, а не содержат сами входные данные.
@user20102550 user20102550 Вы можете думать о значении типа Parser a как о функции, которая помещается в поле. Пока он находится внутри коробки, это не функция, поэтому его нельзя применить. Чтобы построить Parser a, нам сначала нужно определить функцию (sf inp = ...), а затем поместить эту функцию в поле (MkParser sf). Чтобы использовать Parser a, нам нужно вывести функцию за рамки, и это можно сделать так же, как вы это сделали в convert f (MkParser p1) = .... Там p1 — это содержимое поля, то есть это настоящая функция.
Исходное определение уже имеет доступ к входной строке — оно просто называет ее
cиcsвместо имениinp!