【问题标题】:Matching brackets in a string匹配字符串中的括号
【发布时间】:2011-04-25 07:17:36
【问题描述】:

在字符串中匹配括号的最有效或优雅的方法是什么,例如:

"f @ g[h[[i[[j[2], k[[1, m[[1, n[2]]]]]]]]]] // z"

为了用单字符形式识别和替换[[ Part ]]括号?

我想得到:

其他所有内容都完好无损,例如前缀 @ 和后缀 // 形式完好


为不熟悉的人解释 Mathematica 语法:

函数使用单方括号作为参数:func[1, 2, 3]

部分索引使用双方括号:list[[6]] 或使用单字符 Unicode 双括号:list〚6〛

我的意图是在 ASCII 文本字符串中识别匹配的 [[ ]] 形式,并将其替换为 Unicode 字符 〚 〛

【问题讨论】:

  • @belisarius,确实相关,但这个问题只是关于字符串操作,而不是自动处理。
  • @Mr.我建议去掉这两个通用标签,或者解释一下“[[ ]]”在 Mma 中是如何工作的。否则,非 Mma SO 用户会弄错
  • 看来 leonid 具有您正在寻找的格式的正确解决方案。哇,mathematica 太罗嗦了。
  • @bdares 它通常很简洁!那是一个通用的解析器。如果最佳的“高尔夫”解决方案只有一条线,我不会感到惊讶。

标签: parsing string wolfram-mathematica


【解决方案1】:

好的,这是另一个答案,稍微短一点:

Clear[replaceDoubleBrackets];
replaceDoubleBrackets[str_String, openSym_String, closeSym_String] := 
Module[{n = 0},
  Apply[StringJoin, 
   Characters[str] /. {"[" :> {"[", ++n}, 
     "]" :> {"]", n--}} //. {left___, {"[", m_}, {"[", mp1_}, 
      middle___, {"]", mp1_}, {"]", m_}, right___} /; 
       mp1 == m + 1 :> {left, openSym, middle, 
        closeSym, right} /. {br : "[" | "]", _Integer} :> br]]

例子:

In[100]:= replaceDoubleBrackets["f[g[h[[i[[j[2], k[[1, m[[1, n[2]]]]]]]]]]]", "(", ")"]

Out[100]= "f[g[h(i(j[2], k(1, m(1, n[2]))))]]"

编辑

如果您想用您指定的符号专门替换双括号,您也可以使用 Mathematica 内置工具:

Clear[replaceDoubleBracketsAlt];
replaceDoubleBracketsAlt[str_String] :=
  StringJoin @@ Cases[ToBoxes@ToExpression[str, InputForm, HoldForm],
     _String, Infinity]

In[117]:= replaceDoubleBracketsAlt["f[g[h[[i[[j[2], k[[1, m[[1, n[2]]]]]]]]]]]"]

Out[117]= f[g[h[[i[[j[2],k[[1,m[[1,n[2]]]]]]]]]]]

结果不会在此处正确显示,但它是带有您请求的符号的 Unicode 字符串。

【讨论】:

  • @Mr.向导 我添加了另一个更简单的解决方案 - 请参阅编辑。
  • 第二种方法在其产生的格式变化上与 Shift+Ctrl+N 类似。
  • @Mr.巫师哦,我明白了。这可能是使用内置 mma 解析器的代价 :)
【解决方案2】:

当我编写我的第一个解决方案时,我没有注意到您只是想将字符串中的[[ 替换为,而不是表达式。您始终可以使用HoldFormDefer 作为

但我想您已经知道了,并且您希望将表达式作为字符串,就像输入一样(上面的ToString@ 不起作用)

由于到目前为止所有答案都集中在字符串操作上,我将采用数字方法而不是与字符串搏斗,这对我来说更自然。 [ 的字符代码是 91,] 是 93。所以执行以下操作

0/1 向量的形式给出括号的位置。我已经否定了右括号,只是为了帮助思考过程并供以后使用。

注意:我只检查了 91 和 93 的可分性,因为我当然不希望您输入以下任何字符,但是如果出于某种原因您选择了,你可以很容易地AND 上面的结果,并带有一个与 91 或 93 相等的布尔列表。

由此可知Part的双括号对中第一个的位置为

事实上,在 mma 中,表达式不以 [ 开头,并且两个以上的 [ 不能像 [[[... 一样连续出现,在上述计算中已隐含假设。

现在关闭对实现起来比较棘手,但很容易理解。思路如下:

  • 对于closeBracket 中的每个非零位置,例如i,转到openBracket 中的相应位置,并找到它左侧的第一个非零位置(例如j)。
  • 设置doubleCloseBrackets[[i-1]]=closeBracket[[i]]+openBracket[[j]]+doubleOpenBrackets[[j]]
  • 您可以看到doubleCloseBracketsdoubleOpenBrackets 的对应物,并且在Part]] 对的第一个位置处非零。

所以现在我们有一组布尔位置用于第一个开括号。我们只需要将charCode 中的对应元素替换为 的等效项,同样,使用第一个右括号的布尔位置,我们将charCode 中的对应元素替换为 的等效项。

最后,通过删除已更改元素旁边的元素,您可以将修改后的字符串用[[]] 替换为〚 〛

注意 2:

我的许多 MATLAB 习惯已经潜入上述代码,并且在 Mathematica 中并不完全符合习惯。但是,我认为逻辑是正确的,并且有效。我会把它留给你来优化它(我认为你可以取消Do[])并把它变成一个模块,因为它会花费我更长的时间。

代码为文本

Clear["Global`*"]
str = "f[g[h[[i[[j[2], k[[1, m[[1, n[2]]]]]]]]]]]";
charCode = ToCharacterCode@str;
openBracket = Boole@Divisible[charCode, First@ToCharacterCode["["]];
closeBracket = -Boole@
    Divisible[charCode, First@ToCharacterCode["]"]];
doubleOpenBracket = 
  Append[Differences@Accumulate[openBracket], 0] openBracket;
posClose = Flatten@Drop[Position[closeBracket, Except@0, {1}], 1];

doubleCloseBracket = ConstantArray[0, Dimensions@doubleOpenBracket];
openBracketDupe = openBracket + doubleOpenBracket;
Do[
  tmp = Last@
    Flatten@Position[openBracketDupe[[1 ;; i]], Except@0, {1}];
  doubleCloseBracket[[i - 1]] = 
   closeBracket[[i]] + openBracketDupe[[tmp]];
  openBracketDupe[[tmp]] = 0;,
  {i, posClose}];

changeOpen = 
  Cases[Range[First@Dimensions@charCode]  doubleOpenBracket, Except@0];
changeClosed = 
  Cases[Range[First@Dimensions@charCode]  doubleCloseBracket, 
   Except@0];
charCode[[changeOpen]] = ToCharacterCode["\[LeftDoubleBracket]"];
charCode[[changeClosed]] = ToCharacterCode["\[RightDoubleBracket]"];
FromCharacterCode@
 Delete[Flatten@charCode, 
  List /@ (Riffle[changeOpen, changeClosed] + 1)]

【讨论】:

  • 这看起来不错。您愿意将代码发布为文本而不是图像以供复制吗?但是,将输出保留为图像会很好。
  • @Mr.向导:我已将代码添加为文本,但我保留了旧图像。
【解决方案3】:

这是我的尝试。由于存在特殊字符,粘贴的 ASCII 代码非常难以阅读,所以我首先提供一张它在 MMA 中的外观图片。

基本上它的作用是:左括号始终可以唯一标识为单括号或双括号。问题在于右括号。左括号始终具有模式 string-of-characters-containing-no-brackets + [ 或 [[。不可能有 [ 跟在 [[ 之后,反之亦然,中间没有其他字符(至少,在无错误代码中)。

因此,我们将其用作钩子并开始寻找某些匹配的括号对,即中间没有任何其他括号的括号。由于我们知道类型,“[...]”或“[[...]]”,我们可以用双括号符号替换后者,用未使用的字符替换前者(我使用笑脸)。这样做是为了让它们在模式匹配过程的下一次迭代中不再起作用。

我们重复直到处理完所有括号,最后笑脸再次转换为单括号。

你看,解释比代码使用更多的字符;-)。

ASCII:

s = "f @ g[hh[[i[[jj[2], k[[1, m[[1, n[2]]]]]]]]]] // z";

myRep[s_String] :=
 StringReplace[s,
  {
   Longest[y : Except["[" | "]"] ..] ~~ "[" ~~ 
     Longest[x : Except["[" | "]"] ..] ~~ "]" :> 
    y <> "\[HappySmiley]" <> x <> "\[SadSmiley]",
   Longest[y : Except["[" | "]"] ..] ~~ "[" ~~ Whitespace ... ~~ "[" ~~
      Longest[x : Except["[" | "]"] ..] ~~ "]" ~~ Whitespace ... ~~ 
     "]" :> y <> "\[LeftDoubleBracket]" <> x <> "\[RightDoubleBracket]"
   }
  ]

StringReplace[FixedPoint[myRep, s], {"\[HappySmiley]" -> "[","\[SadSmiley]" -> "]"}]

哦,Whitespace 部分是因为在 Mathematica 中,双括号不必彼此相邻。 a[ [1] ]a[[1]] 一样合法。

【讨论】:

  • @belisarius 感谢您的编辑。为了使图像可点击,我以后要做的就是在括号中重复它的数字?
  • ![image][n] -> [![image][n]][n]
【解决方案4】:

您需要一个堆栈才能正确执行此操作;没有办法使用正则表达式正确地做到这一点。

您需要识别[[ 以及这些括号的深度,并将它们与具有相同深度的]] 匹配。 (堆栈可以很好地做到这一点。只要它们不溢出:P)

如果不使用某种计数器,这是不可能的。如果没有定义一些最大深度,就不可能用有限状态自动机来表示它,所以不可能用正则表达式来做到这一点。

注意:这是一个不能被正则表达式正确解析的字符串示例:

[1+[[2+3]*4]] = 21

这会变成

[1 + 2 + 3] * 4 = 24

这是一些类似java的伪代码:

public String minimizeBrackets(String input){
    Stack s = new Stack();
    boolean prevWasPopped = false;
    for(char c : input){
        if(c=='['){
            s.push(i);
            prevWasPopped = false;
        }
        else if(c==']'){
            //if the previous step was to pop a '[', then we have two in a row, so delete an open/close pair
            if(prevWasPopped){
                input.setChar(i, " ");
                input.setChar(s.pop(), " ");
            }
            else s.pop();
            prevWasPopped = true;
        }
        else prevWasPopped = false;
    }
    input = input.stripSpaces();
    return input;
}

请注意,我只是将它们变成空格,然后删除空格……这不会像我宣传的那样做,它也会破坏原始字符串中的所有空格。您可以简单地记录所有位置,而不是将它们更改为空格,然后在不记录位置的情况下复制原始字符串。

另外请注意,最后我没有检查堆栈的状态。假定为空,因为输入字符串中的每个[ 字符都假定有其唯一的] 字符,反之亦然。如果堆栈在任何时候抛出“你试图在我为空时弹出我”异常,或者在运行结束时不为空,你就知道你的字符串格式不正确。

【讨论】:

  • 感谢您的建议。所以对我来说很清楚,请您举一个伪代码示例吗?
  • 哦,不是删除,而是合并它们...在这种情况下,您必须实际更改代码,以便在找到匹配。 (而不是删除一个并仍然留下一个。)
  • +1 获取完整说明。我坚持一个“聪明的” Mathematica 实现(这个或其他的),但这在逻辑上应该有效。谢谢。
  • @Mr.Wizard @bdares 好吧,据我所知,我设法找到了有效的正则表达式。这些是 Mathematica StringExpressions,它们非常相似。技巧之一是重复应用它们,直到没有任何变化。在此页面的某处查看我的解决方案。
  • @Sjoerd 这是作弊 :P 正如我在帖子顶部所说的那样,如果您想使用正则表达式执行此操作,则需要与括号深度一样多的状态。如果您允许外部控制循环,那也是一样的(计数器相当于堆栈!)。
【解决方案5】:

我认为,其他答案已经没有实际意义,但这是 yoda 的第一个解决方案的更符合 Mathematica 习惯的版本。此外,对于足够长的字符串,它的某些部分可能会更有效率。

str = "f @ g[h[[i[[j[2], k[[1, m[[1, n[2]]]]]]]]]] // z";
charCode = ToCharacterCode@str;
openBracket = Boole@Thread[charCode == 91];
closeBracket = -Boole@Thread[charCode == 93];
doubleOpenBracket = openBracket RotateLeft@openBracket;
posClose = Flatten@Position[closeBracket, -1, {1}];
doubleCloseBracket = 0*openBracket;
openBracketDupe = openBracket + doubleOpenBracket;
Do[
 tmp = Last@DeleteCases[Range@i*Sign@openBracketDupe[[1 ;; i]], 0];
 doubleCloseBracket[[i - 1]] = 
  closeBracket[[i]] + openBracketDupe[[tmp]];
 openBracketDupe[[tmp]] = 0, {i, posClose}]
counter = Range@Length@charCode;
changeOpen = DeleteCases[doubleOpenBracket*counter, 0];
changeClosed = DeleteCases[doubleCloseBracket*counter, 0];
charCode[[changeOpen]] = First@ToCharacterCode["\[LeftDoubleBracket]"];
charCode[[changeClosed]] = 
  First@ToCharacterCode["\[RightDoubleBracket]"];
FromCharacterCode@Delete[charCode, List /@ Flatten@{1 + changeOpen, 1 + changeClosed}]

这种设置“tmp”的方式可能效率较低,但我认为它很有趣。

【讨论】:

    【解决方案6】:

    我可以提供一种繁重的方法(不太优雅)。下面是我对基本 Mathematica 解析器的实现(它仅适用于包含完整形式代码的字符串,双括号可能例外 - 我将在此处使用),基于广度优先解析器的相当一般的功能我开发主要是为了实现HTML parser

    ClearAll[listSplit, reconstructIntervals, groupElements, 
    groupPositions, processPosList, groupElementsNested];
    
    listSplit[x_List, lengthlist_List, headlist_List] := 
      MapThread[#1 @@ Take[x, #2] &, {headlist, 
        Transpose[{Most[#] + 1, Rest[#]} &[
          FoldList[Plus, 0, lengthlist]]]}];
    
    reconstructIntervals[listlen_Integer, ints_List] := 
      Module[{missed, startint, lastint},
        startint  = If[ints[[1, 1]] == 1, {}, {1, ints[[1, 1]] - 1}];
        lastint = 
           If[ints[[-1, -1]] == listlen, {}, {ints[[-1, -1]] + 1, listlen}];
        missed = 
          Map[If[#[[2, 1]] - #[[1, 2]] > 1, {#[[1, 2]] + 1, #[[2, 1]] - 1}, {}] &, 
          Partition[ints, 2, 1]];
        missed = Join[missed, {lastint}];
        Prepend[Flatten[Transpose[{ints, missed}], 1], startint]];
    
    groupElements[lst_List, poslist_List, headlist_List] /; 
     And[OrderedQ[Flatten[Sort[poslist]]], Length[headlist] == Length[poslist]] := 
      Module[{totalheadlist, allints, llist},
        totalheadlist = 
         Append[Flatten[Transpose[{Array[Sequence &, {Length[headlist]}], headlist}], 1], Sequence];
      allints = reconstructIntervals[Length[lst], poslist];
      llist = Map[If[# === {}, 0, 1 - Subtract @@ #] &, allints];
      listSplit[lst, llist, totalheadlist]];
    
      (* To work on general heads, we need this *)
    
    groupElements[h_[x__], poslist_List, headlist_List] := 
       h[Sequence @@ groupElements[{x}, poslist, headlist]];
    
    (* If we have a single head *)
    groupElements[expr_, poslist_List, head_] := 
        groupElements[expr, poslist, Table[head, {Length[poslist]}]];
    
    
    groupPositions[plist_List] :=
         Reap[Sow[Last[#], {Most[#]}] & /@ plist, _, List][[2]];
    
    
    processPosList[{openlist_List, closelist_List}] :=
       Module[{opengroup, closegroup, poslist},
        {opengroup, closegroup} = groupPositions /@ {openlist, closelist} ;
        poslist =  Transpose[Transpose[Sort[#]] & /@ {opengroup, closegroup}];
        If[UnsameQ @@ poslist[[1]],
           Return[(Print["Unmatched lists!", {openlist, closelist}]; {})],
           poslist = Transpose[{poslist[[1, 1]], Transpose /@ Transpose[poslist[[2]]]}]
        ]
    ];
    
    groupElementsNested[nested_, {openposlist_List, closeposlist_List}, head_] /; Head[head] =!= List := 
     Fold[
      Function[{x, y}, 
        MapAt[groupElements[#, y[[2]], head] &, x, {y[[1]]}]], 
      nested, 
      Sort[processPosList[{openposlist, closeposlist}], 
       Length[#2[[1]]] < Length[#1[[1]]] &]];
    
    ClearAll[parse, parsedToCode, tokenize, Bracket ];
    
    (* "tokenize" our string *)
    tokenize[code_String] := 
     Module[{n = 0, tokenrules},
       tokenrules = {"[" :> {"Open", ++n}, "]" :> {"Close", n--}, 
           Whitespace | "" ~~ "," ~~ Whitespace | ""};
       DeleteCases[StringSplit[code, tokenrules], "", Infinity]];
    
    (* parses the "tokenized" string in the breadth-first manner starting 
       with the outermost brackets, using Fold and  groupElementsNested*)
    
    parse[preparsed_] := 
      Module[{maxdepth = Max[Cases[preparsed, _Integer, Infinity]], 
       popenlist, parsed, bracketPositions},
       bracketPositions[expr_, brdepth_Integer] := {Position[expr, {"Open", brdepth}], 
       Position[expr, {"Close", brdepth}]};  
       parsed = Fold[groupElementsNested[#1, bracketPositions[#1, #2], Bracket] &,
                   preparsed, Range[maxdepth]];
       parsed =  DeleteCases[parsed, {"Open" | "Close", _}, Infinity];
       parsed = parsed //. h_[x___, y_, Bracket[z___], t___] :> h[x, y[z], t]];
    
     (* convert our parsed expression into a code that Mathematica can execute *)
     parsedToCode[parsed_] :=
     Module[{myHold},
       SetAttributes[myHold, HoldAll];   
       Hold[Evaluate[
         MapAll[# //. x_String :> ToExpression[x, InputForm, myHold] &, parsed] /.
          HoldPattern[Sequence[x__][y__]] :> x[y]]] //. myHold[x___] :> x
    
     ];
    

    (注意在最后一个函数中使用了MapAll)。现在,这里是你如何使用它:)

    In[27]:= parsed = parse[tokenize["f[g[h[[i[[j[2], k[[1, m[[1, n[2]]]]]]]]]]]"]]
    
    Out[27]= {"f"["g"["h"[Bracket[
     "i"[Bracket["j"["2"], 
       "k"[Bracket["1", "m"[Bracket["1", "n"["2"]]]]]]]]]]]}
    
    In[28]:= parsed //. a_[Bracket[b__]] :> "Part"[a, b]
    
    
    Out[28]= {"f"["g"["Part"["h", 
    "Part"["i", "j"["2"], 
     "Part"["k", "1", "Part"["m", "1", "n"["2"]]]]]]]}
    

    现在你可以使用parseToCode

    In[35]:= parsedToCode[parsed//.a_[Bracket[b__]]:>"Part"[a,b]]//FullForm
    
    Out[35]//FullForm= Hold[List[f[g[Part[h,Part[i,j[2],Part[k,1,Part[m,1,n[2]]]]]]]]]
    

    编辑

    这里是根据要求仅进行字符替换所需的补充:

    Clear[stringify, part, parsedToString];
    stringify[x_String] := x;
    stringify[part[open_, x___, close_]] := 
       part[open, Sequence @@ Riffle[Map[stringify, {x}], ","], close];
    stringify[f_String[x___]] := {f, "[",Sequence @@ Riffle[Map[stringify, {x}], ","], "]"};
    
    parsedToString[parsed_] := 
     StringJoin @@ Flatten[Apply[stringify, 
      parsed //. Bracket[x__] :> part["yourOpenChar", x, "yourCloseChar"]] //. 
        part[x__] :> x];
    

    我们可以这样使用它:

    In[70]:= parsedToString[parsed]
    
    Out[70]= "f[g[h[yourOpenChari[yourOpenCharj[2],k[yourOpenChar1,m[\
      yourOpenChar1,n[2]yourCloseChar]yourCloseChar]yourCloseChar]\
       yourCloseChar]]]"
    

    【讨论】:

    • 重是对的! 8-s 我希望有一个更简单的解决方案,但这可能会在未来派上用场。
    • 我在实现中有效地使用了堆栈。在我解析的方式上有些隐藏,也就是给token(括号)赋值括号深度。
    • Leonid,很抱歉,但这并没有达到我想要的效果。由于它重新解析代码,我失去了中缀/后缀形式和所有这些。正如西蒙在相关问题中向我展示的那样,我只需要 Shift+Ctrl+N 来进行自动解析。我仍在寻找一种方法来更改 only 括号。
    • @Mr.Wizard - 如果您想保留所有中缀形式等,而无需完整的 mma 解析器,我不确定您的要求是否可行。问题是没有办法知道,在这样的字符串中:"a[[1,b[[2]]]]&gt;=c[[3,d[[4]]]]",只有c 是右上方Part 的一部分,而不是&gt;=c。当然,您可以添加启发式规则,但这将是朝着制作 ad-hoc mma 解析器迈出的一步。如果您只想用单字符形式替换,那应该是可能的。
    • @Leonid 我可能只是愚蠢,但我不明白这一点。是c还是&gt;=c有什么关系?
    【解决方案7】:

    编辑

    tl;dr 版本:

    我正在无意中解决基本问题,但正则表达式不能计算括号,所以使用堆栈实现。

    加长版:

    尊敬的同事是正确的,解决此问题的最佳方法是堆栈实现。如果字符串中的[[ 的数量与]] 的数量相同,则正则表达式可能能够将[[]] 分别更改为[],但是如果练习是使用匹配 [] 中的文本,然后正则表达式不是要走的路。正则表达式不能计算括号,嵌套逻辑对于一个简单的正则表达式来说太复杂了。所以简而言之,我相信正则表达式可用于解决基本要求,即将匹配的[[]] 更改为匹配的[],但是您应该真正使用堆栈,因为它允许更轻松地操作结果字符串。

    抱歉,我完全错过了 mathematica 标签!我会把我的答案留在这里,以防万一有人像我一样兴奋并跳出枪来。

    结束编辑

    使用不情愿量词的正则表达式应该能够逐步确定 [[]] 标记在字符串中的位置,并确保仅当 [[ 的数量等于 ]] 的数量时才进行匹配.

    所需的正则表达式类似于[[{1}?(?!]])*?]]{1}?,用简单的英语来说是:

    • [[{1}?,从字符串的开头每次前进一个字符,直到遇到 [[ 的一个实例
    • (?!]])*? 如果存在任何与 ]] 不匹配的字符,则一次一个地遍历它们
    • ]]{1}? 匹配右括号

    要将双方括号更改为单方括号,请通过在第一个和第三个粒子周围添加括号来识别正则表达式中的组:

    ([[{1}?)(?!]])*?(]]{1}?)
    

    这允许您选择[[]] 标记,然后将它们替换为[]

    【讨论】:

    • 这看起来像是我所希望的,但我对正则表达式的无知真是太棒了。你使用 Mathematica,还是像 Perl 这样的东西?无论哪种情况,请附加完整的代码,以便我可以尝试完成它。感谢您的回答,请耐心等待。
    • 我认为你的陈述从左到右第一个[[应该与第一个匹配]]是不正确的。例如 f [[ a[7] ]](用于显示所需匹配的空格)
    • @Mr.Wizard 抱歉,我错过了 Mathematica 标签,在查看其他答案/cmets 后,我同意人群推荐堆栈实现。编辑我的答案以反映这些情绪。仅供参考,这种正则表达式可以在很多编程语言中使用,甚至可以在 Unix 命令行中使用。无论如何,再次抱歉,祝你好运!
    • 没问题,谢谢。 Mathematica 确实有一个品牌的正则表达式,但我对它的理解不够好,无法插入像你这样的代码。
    【解决方案8】:

    已编辑(出现错误)

    这是不是太天真了?

    doubleB[x_String] :=
      StringReplace[
       ToString@StandardForm@
         ToExpression["Hold[" <> x <> "]"], 
      {"Hold[" -> "", RegularExpression["\]\)$"] -> "\)"}];
    
    doubleB["f[g[h[[i[[j[2], k[[1, m[[1, n[2]]]]]]]]]]]"]
    ToExpression@doubleB["f[g[h[[i[[j[2], k[[1, m[[1, n[2]]]]]]]]]]]"]
    

    ->

    只是想利用 Mma 自己的解析器...

    【讨论】:

    • 使用 Mathematica 的解析器的问题在于它也改变了其他东西,这可以通过简单的 Ctrl+Shift+N 完成(感谢 Simon)。我刚刚更新了我的问题,使其在这方面更加具体。不过还是谢谢你。
    【解决方案9】:

    这是另一个模式匹配,可能类似于 Sjoerd C. de Vries 所做的,但这个是在程序上首先创建的嵌套列表结构上运行的:

    FirstStringPosition[s_String, pat_] :=
        Module[{f = StringPosition[s, pat, 1]},
          If[Length@f > 0, First@First@f, Infinity]
        ];
    FirstStringPosition[s_String, ""] = Infinity;
    
    $TokenizeNestedBracePairsBraces = {"[" -> "]", "{" -> "}", "(" -> ")"(*,
      "<"\[Rule]">"*)};
    (*nest substrings based on parentheses {([*) (* TODO consider something like http://stackoverflow.com/a/5784082/524504, though non procedural potentially slower*)
    TokenizeNestedBracePairs[x_String, closeparen_String] :=
        Module[{opString, cpString, op, cp, result = {}, innerResult,
          rest = x},
    
          While[rest != "",
    
            op = FirstStringPosition[rest,
              Keys@$TokenizeNestedBracePairsBraces];
            cp = FirstStringPosition[rest, closeparen];
    
            Assert[op > 0 && cp > 0];
    
            Which[
            (*has opening parenthesis*)
              op < cp
    
              ,(*find next block of [] *)
              result~AppendTo~StringTake[rest, op - 1];
              opString = StringTake[rest, {op}];
              cpString = opString /. $TokenizeNestedBracePairsBraces;
              rest = StringTake[rest, {op + 1, -1}];
    
              {innerResult, rest} = TokenizeNestedBracePairs[rest, cpString];
              rest = StringDrop[rest, 1];
    
              result~AppendTo~{opString, innerResult, cpString};
    
              , cp < Infinity
              ,(*found searched closing parenthesis and no further opening one \
    earlier*)
              result~AppendTo~StringTake[rest, cp - 1];
              rest = StringTake[rest, {cp, -1}];
              Return@{result, rest}
    
              , True
              ,(*done*)
              Return@{result~Append~rest, ""}
            ]
          ]
        ];
    (* TODO might want to get rid of empty strings "", { generated here:
    TokenizeNestedBracePairs@"f @ g[h[[i[[j[2], k[[1, m[[1, n[2]]]]]]]]]] \
    // z"
    *)
    
    TokenizeNestedBracePairs[s_String] :=
        First@TokenizeNestedBracePairs[s, ""]
    

    然后有了这些定义

    StringJoin @@ 
     Flatten[TokenizeNestedBracePairs@
        "f @ g[h[[i[[j[2], k[[1, m[[1, n[2]]]]]]]]]] // z" //. {"[", {"", \
    {"[", Longest[x___], "]"}, ""}, "]"} :> {"\[LeftDoubleBracket]", {x}, 
         "\[RightDoubleBracket]"}]
    

    给予

    【讨论】:

    • 解析总是一个有趣的问题;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-07-06
    • 1970-01-01
    • 1970-01-01
    • 2011-07-22
    • 2016-02-28
    • 2015-07-22
    • 1970-01-01
    相关资源
    最近更新 更多