Как использовать входную строку в парсере

Я новичок в Хаскеле. Это мой парсер:

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

Но выдает ошибку отступа. Есть идеи?

Исходное определение уже имеет доступ к входной строке — оно просто называет ее c и cs вместо имени inp!

Daniel Wagner 08.07.2024 22:53
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы близки. Давайте сосредоточимся на этой части:

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 — это имя гипотетической строки, которую она получит, когда/если она когда-либо будет вызвана, а не какой-либо конкретной строки, которая существует «сейчас».

Ben 08.07.2024 03:02

@user20102550 user20102550 Легко думать об этом как о простом «получении доступа к входной строке» (и разумно использовать в качестве мысленного сокращения). Но, конечно, сам Parser не может предоставить вам доступ к входной строке, потому что он понятия не имеет, какой текст вы в конечном итоге попытаетесь проанализировать! Это происходит позже, в коде, использующем синтаксический анализатор (что может произойти несколько раз с разными входными данными или никогда, поэтому такой вещи, как входная строка, не существует). Следовательно, почему парсеры содержат функции, говорящие, как они могут обрабатывать входные данные, если/когда они есть, а не содержат сами входные данные.

Ben 08.07.2024 03:09

@user20102550 user20102550 Вы можете думать о значении типа Parser a как о функции, которая помещается в поле. Пока он находится внутри коробки, это не функция, поэтому его нельзя применить. Чтобы построить Parser a, нам сначала нужно определить функцию (sf inp = ...), а затем поместить эту функцию в поле (MkParser sf). Чтобы использовать Parser a, нам нужно вывести функцию за рамки, и это можно сделать так же, как вы это сделали в convert f (MkParser p1) = .... Там p1 — это содержимое поля, то есть это настоящая функция.

chi 08.07.2024 10:23

Другие вопросы по теме