将带括号的数字字符串转换为树

问题描述 投票:0回答:1

我正在开发一个函数,将带括号的数字字符串转换为树。所以,举个例子,

treeFromString "((1)((3)(4)))" == Right (Nd (Lf 1) (Nd (Lf 3) (Lf 4)))

这是我的尝试。我假设一个格式良好的字符串来解决一个更简单的问题,但类型签名应该是

Either String (BinL' Int)

import Data.Char

data BinL' a = Lf a | Nd (BinL' a) (BinL' a) deriving Show

treeFromString :: String -> BinL' Int
treeFromString [] = undefined
treeFromString ('(':cs) = case span isDigit cs of
                            ("", remaining) -> treeFromString cs
                            (digits, remaining) -> Nd (Lf (read digits)) (treeFromString remaining)
                            

当传入的参数为空列表时,我应该返回什么值?

我的总体方法是否是实现我想要的目标的明智方法?

编辑:

有一个补充函数可以将树转换为带括号的字符串。我正在开发的功能应该执行相反的转换。示例:

showBinL' :: Show a => BinL' a -> String

showBinL' (Nd (Lf 1) (Nd (Lf 3) (Lf 4)))
"((1)((3)(4)))"
parsing haskell tree
1个回答
0
投票

如果你不太关心效率,我推荐标准的初学者友好方法:

事物的解析器
是字符串的函数
到配对列表
事物和字符串。
——格雷厄姆·赫顿

而且,您很幸运:语言标准

Read
类将其解析器放入此框架中。

reads :: Read a => String -> [(a, String)]

因此,如果您正在构建能够顺利处理此类类型的机制,那么当您需要读取数字时,您可以干净地插入

reads
,事实上甚至可以从读取数字树推广到读取树任何
Read
实例。

所以这是我建议您首先尝试实现的类型。如果你必须有一个特定的类型,你可以在它周围写一个小包装器来转换。

treeFromString :: Read a => String -> [(BinL' a, String)]

一般情况下,如果字符串解析成功,结果列表将有一个元素,包含树和尚未解析的字符串的后缀(如果有);如果解析失败,结果列表应该为空。

在你坐下来开始编写解析代码之前,首先尝试写下语法通常是一个好主意。这是我对合理语法的猜测:一棵树要么是一片叶子,在这种情况下它直接是该叶子的值,要么它是一个节点,在这种情况下它是被括号包围的两棵树。像这样:

T ::= '(' T T ')'
    | <leaf>

一个实施计划是构建一个小型解析器库,可以将它们组合起来生成此语法。我推荐以下套装:

  • 解析器消耗单个字符,并且不返回任何有趣的数据。

    char :: Char -> String -> [((), String)]
    
  • 一种对两个解析器进行排序的方法,即解析第一个事物,然后使用第一个解析器中的任何剩余文本来解析第二个事物。

    andThen :: (String -> [(a, String)])
            -> (String -> [(b, String)])
            -> (String -> [((a, b), String)])
    
  • 一种交替使用两个解析器的方法,即在同一输入上尝试两个解析器,返回其中一个解析器给出的任何结果。

    orElse :: (String -> [(a, String)])
           -> (String -> [(b, String)])
           -> (String -> [(Either a b, String)])
    
  • 一种在解析器成功时转换解析器返回的数据的方法。

    finally :: (String -> [(a, String)])
            -> (a -> b)
            -> (String -> [(b, String)])
    

有了这些,你可以直接将上面的语法音译成解析器:

t :: Read a => String -> [(a, String)]
-- T ::=       '('           T           T                ')'
t      = (char '(' `andThen` t `andThen` t `andThen` char ')' `finally` \(_, (l, (r, _))) -> Nd l r)
--     |         <leaf>
       `orElse` (reads `finally` Lf)
© www.soinside.com 2019 - 2024. All rights reserved.