【问题标题】:functional programming : understand parser combinator函数式编程:理解解析器组合器
【发布时间】:2016-07-29 12:49:34
【问题描述】:

我试图使用解析器组合器来解决问题。我尝试了以下方法:

注意:以下代码使用combinator library

styleParserItalic : Bool -> Parser ( List (List Char , Style))
styleParserItalic bolded =
let
style = if bolded then Italic else Unstyled
 in
 (end `andThen` always (  succeed ( [] )))
 <|> (string "(!ITALIC!)" `andThen`  \_ -> styleParserItalic ( not bolded )   ) 
 <|> ( anyChar `andThen` \c -> styleParserItalic bolded `andThen` \cs -> succeed ((c :: [],style) :: cs) )

我很难理解这个解析器是如何运行的,因为styleParserItalic 解析器在解析器成功之前被调用。

有人能解释一下解析器在给出一串字符时是如何工作的吗?

如果有人对解析器的用途和完整代码感兴趣,here 是我之前的问题。

这是我目前所了解的

解析器将首先检查它是否是行尾,如果不是,它将尝试解析字符串(!ITALIC!),如果是这种情况,它将使用参数 True 或 false 调用解析器(如果为 false,则将让它成为现实..)

如果解析器没有找到字符串 (!ITALIC!),它将尝试解析任何字符,然后再次调用解析器。

让我困惑的是,只要解析器成功解析任何字符,解析器就会一直调用自己!

edit :* 注意以下不是问题的一部分,如果有人感兴趣,请分享代码

感谢所有回复,我已更新解析器以解析粗斜体下划线...,根据以下屏幕截图

type Style = Bold| Unstyled | Italic | Coded | Lined | Titled | Marked     | Underline

styleParser : Bool ->Bool ->Bool ->Bool-> Bool-> Bool->Bool
                                -> Parser ( List (List Char ,     (Style,Style,Style,Style,Style,Style,Style)))
                                --(bold,italic ,code,line ,Titled,mark)
styleParser bolded italiced coded lined titled marked  underlined=
  let
    style = (
     if bolded     then Bold      else Unstyled
    ,if italiced   then Italic    else Unstyled
    ,if coded      then Coded     else Unstyled
    ,if lined      then Lined     else Unstyled
    ,if titled     then Titled    else Unstyled
    ,if marked     then Marked    else Unstyled
    ,if underlined then Underline else Unstyled
    )
  in
    (end `andThen` always ( succeed ( [] )))
    <|> (string "//"  `andThen` \_ -> styleParser  bolded      italiced         coded       lined       titled       marked        (not underlined))
    <|> (string "**"  `andThen` \_ -> styleParser (not bolded) italiced        coded       lined       titled       marked        underlined)
    <|> (string "*"   `andThen` \_ -> styleParser bolded       (not italiced)  coded       lined       titled       marked        underlined)
    <|> (string "`"   `andThen` \_ -> styleParser bolded       italiced        (not coded) lined       titled       marked        underlined)
    <|> (string "/br" `andThen` \_ -> styleParser bolded       italiced        coded       (not lined) titled       marked        underlined)
    <|> (string "/*"  `andThen` \_ -> styleParser bolded       italiced        coded       lined       (not titled) marked        underlined)
    <|> (string "{-"  `andThen` \_ -> styleParser bolded       italiced        coded       lined       titled       (not marked)  underlined)
    <|> ( anyChar     `andThen` \c -> styleParser bolded       italiced         coded       lined       titled       marked        underlined  `andThen`    \cs -> succeed ((c :: [],style) :: cs) )


foldStyleHtml : List ( List Char , (    Style,Style,Style,Style,Style,Style,Style) ) -> List (Html Msg)
foldStyleHtml lst =
  List.map styleToHtml lst


styleToHtml : ( List Char, (Style ,Style,Style,Style,Style,Style,Style)) -> Html Msg
styleToHtml (a,b) =
  case b of
    (Bold,Italic,_,_,_,_,Unstyled)       -> strong [] [em [][ text   (String.fromList a)]]
    (Bold,Italic,_,_,_,_,Underline)      -> u[][ strong [] [em [][ text (String.fromList a)]]]
    (Bold,Unstyled,_,_,_,_,Underline)    -> u[][ strong [] [text (String.fromList a)]]
    (Unstyled,Italic,_,_,_,_,Underline)  -> u[][ em     [] [text (String.fromList a)]]
(Unstyled,Italic,_,_,_,_,_)          -> em[] [text (String.fromList a)]
(Bold,Unstyled,_,_,_,_,_)            -> strong [][ text (String.fromList a)]
 (_,_,Coded,_,_,_,_)                  -> code   [codeStyle ][text     (String.fromList a)]
(_,_,_,Lined,_,_,_)                  -> br [][text " "]
  --  (_,_,_,_,Titled,_,_)                 -> div [][text (String.fromList a)]
    (_,_,_,_,_,Marked,_)                 -> mark [][text (String.fromList a)]
    (_,_,_,_,_,_,Underline)              -> u [][text (String.fromList a)]
   (_,_,_,_,_,_,_)                      -> text  (String.fromList a)

htmlParser : Parser  (List (Html Msg))
htmlParser =
 styleParser False False False False False False False `andThen` (succeed << foldStyleHtml )

runParser : Parser (List (Html Msg)) -> String -> Html Msg
runParser parser str                                    =
  case parse parser str of
    (Ok htmls,_)-> div [] htmls
    (Err err, _) -> div [ style [("color", "red")] ] [ text <| toString <| err]

【问题讨论】:

标签: elm


【解决方案1】:

解析器组合器(通常)在成功时使用输入。在这个库中,如果string "(!ITALIC!)" 失败,它不会消耗任何输入。由于使用了&lt;|&gt; 组合符,它会尝试使用以anyChar 开头的代码的下一部分。

anyChar 成功时,它会使用该单个字符并在andThen 之后在c 中捕获它。然后当递归调用styleParserItalic bolded 时,剩余的字符串(除了anyChar 捕获的字符之外的所有字符串)将被“抓取”。第二个andThen 将递归组合器的输出捕获到cs 中,并将捕获的字符添加到递归调用的其余字符列表中。

我认为要记住的重要部分是组合器在成功时消耗输入,并且(通常)在失败时不消耗输入。

【讨论】:

  • 谢谢,所以在每次调用递归 styleParserItalic 时,它会将其结果传递给 cs 并运行成功的解析器,或者它会等待所有递归调用完成(所有字符解析)然后解析结果到cs?
  • 由于它是递归的,它会在返回链之前递归到字符串的最后一个字符。这就是为什么有end 的第一个组合子,它检查输入的结尾并在匹配时返回空列表(如果没有end,它将永远旋转)。然后递归树折叠并通过将每个 c 附加到从递归调用返回的 cs 上来构建列表。
  • 可以说我正在尝试解析字符串“ABC”,最终结果应该是成功的 (('A',Unstyled) :: (B ,Unstyled) :: (C ,Unstyled) : :总是(成功([])))对吗?那么如何总是将 (succeed ([])) 添加到元组列表中?
  • succeed 是一个函数,它接受一个原始值并将其放在解析器的上下文中(Parser 类型是一个 monad,即使 elm 避开该术语)。这意味着在 Parser 的上下文中,结果列表是 ('A',Unstyled) :: ('B' ,Unstyled) :: ('C' ,Unstyled) :: [],或者使用列表语法,是 [('A',Unstyled), ('B' ,Unstyled), ('C' ,Unstyled)]
【解决方案2】:

先简单说几句……

(1) 签名中内部List的每个元素:

styleParserItalic : Bool -> Parser ( List (List Char , Style))
                                           ^^^^^^^^^

只是一个字符。只需从最后一行删除:: []

 <|> ... `andThen` \cs -> succeed ((c ,style) :: cs) )
                                    ^^^
                                      removed `:: []`

你可以让它有这个签名。

(2) 请注意,bolded 参数仅影响样式 - 它对控制流没有影响。它确实应该被称为italic,因为如果参数为True,则输出中出现的样式为Italic,否则为Unstyled

另外注意一旦这个参数设置为True,在 所有后续的递归调用。

所以现在算法是:

  1. 如果在行尾,则返回空列表。
  2. 如果位于 (!ITALIC!),则使用 Italic 作为解析其余部分中的样式。
  3. 否则,解析一个字符,解析该行的其余部分并连接结果。

一个近似的 Python 算法应该是这样的:

def parseLine(style, line):
  result = []
  while line:
    if line.startsWith('(!ITALIC!)'):
      line = line[8:]
      style = Italic
      # loop around
    else:
      result.append( (line[0], style) )
      line = line[1:]
  return result

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-15
    • 2012-03-12
    • 2012-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多