1

我需要定义一堆向量序列,它们都是一系列的L, D, R,U用于左、下、右、上或x中断。有可选部分和非此即彼的部分。我一直在使用自己发明的系统来记录它,但我想记录下来,以供其他可能不是程序员的人阅读。

我现在想使用正则表达式的子集(例如,我不打算使用任何通配符或无限重复)来定义向量序列和生成所有可能匹配字符串的脚本......

/LDR/ produces ['LDR']
/LDU?R/ produces ['LDR','LDUR']
/R(LD|DR)U/ produces ['RLDU','RDRU']
/DxR[DL]U?RDRU?/ produces ['DxRDRDR','DxRDRDRU','DxRDURDR','DxRDURDRU','DxRLRDR','DxRLRDRU','DxRLURDR','DxRLURDRU']

是否有可用于生成所有匹配项的现有库?

编辑

我意识到我只需要or语句,因为可选的东西可以由thing or nothinga 或 b 指定,两个可选的都可以是(a|b|). 是否有另一种语言可以用来定义我正在尝试做的事情?

4

3 回答 3

2

通过将@Dukeling提供的链接中的java代码翻译成javascript,我想我已经解决了我的问题......

var Node = function(str){
    this.bracket = false;
    this.children = [];
    this.s = str;
    this.next = null;
    this.addChild = function(child){
        this.children.push(child);
    }
}

var printTree = function(root,prefix){
  prefix = prefix.replace(/\./g, "");
  for(i in root.children){
    var child = root.children[i]
    printTree(child, prefix + root.s);
  }
  if(root.children.length < 1){
    console.log(prefix + root.s);
  }
}

var Stack = function(){
    this.arr = []
    this.push = function(item){
        this.arr.push(item)
    }
    this.pop = function(){
        return this.arr.pop()
    }
    this.peek = function(){
        return this.arr[this.arr.length-1]
    }
}

var createTree = function(s){

    // this line was causing errors for `a(((b|c)d)e)f` because the `(((` was only
    // replacing the forst two brackets.
    // var s = s.replace(/(\(|\||\))(\(|\||\))/g, "$1.$2");
    // this line fixes it
    var s = s.replace(/[(|)]+/g, function(x){ return x.split('').join('.') });

    var str = s.split('');
    var stack = new Stack();
    var root = new Node("");
    stack.push(root); // start node
    var justFinishedBrackets = false;
    for(i in str){
        var c = str[i]
        if(c == '('){
            stack.peek().next = new Node("Y"); // node after brackets
            stack.peek().bracket = true; // node before brackets
        } else if (c == '|' || c == ')'){
            var last = stack.peek(); // for (ab|cd)e, remember b / d so we can add child e to it
            while (!stack.peek().bracket){ // while not node before brackets
                stack.pop();
            }
            last.addChild(stack.peek().next); // for (b|c)d, add d as child to b / c
        } else {
            if (justFinishedBrackets){
                var next = stack.pop().next;
                next.s = "" + c;
                stack.push(next);
            } else {
                var n = new Node(""+c);
                stack.peek().addChild(n);
                stack.push(n);
            }
        }
        justFinishedBrackets = (c == ')');
    }
    return root;
}

// Test it out
var str = "a(c|mo(r|l))e";
var root = createTree(str);
printTree(root, "");
// Prints: ace / amore / amole

我只更改了一行,允许处理两个以上连续的括号,并将原始翻译留在评论中

我还添加了一个函数来返回结果数组,而不是打印它们......

var getTree = function(root,prefix){
  this.out = this.out || []
  prefix = prefix.replace(/\./g, "");
  for(i in root.children){
    var child = root.children[i]
    getTree(child, prefix + root.s, out);
  }
  if(root.children.length < 1){
    this.out.push(prefix + root.s);
  }
  if(!prefix && !root.s){
    var out = this.out;
    this.out = null
    return out;
  }
}

// Test it
var str = "a(b|c)d";
var root = createTree(str);
console.log(getTree(root, ""));
// logs ["abd","acd"]

最后一部分,也允许空字符串,所以...(ab|c|)表示abor cor nothing,以及一个方便的快捷方式,以便ab?c翻译成a(b|)c.

var getMatches = function(str){
    str = str.replace(/(.)\?/g,"($1|)")
    // replace all instances of `(???|)` with `(???|µ)`
    // the µ will be stripped out later
    str = str.replace(/\|\)/g,"|µ)")
    // fix issues where last character is `)` by inserting token `µ`
    // which will be stripped out later
    str = str+"µ"
    var root = createTree(str);
    var res = getTree(root, "");
    // strip out token µ
    for(i in res){
        res[i] = res[i].replace(/µ/g,"")
    }
    // return the array of results
    return res
}

getMatches("a(bc|de?)?f");
// Returns: ["abcf","adef","adf","af"]

最后一部分有点骇人听闻,因为它依赖于µ不在字符串中(对我来说不是问题)并解决了一个错误,其中输入字符串末尾的 a导致错误输出,方法是在末尾)插入 aµ每个字符串,然后从结果中剥离它。我很乐意有人提出更好的方法来处理这些问题,因此它可以作为更通用的解决方案。

这段代码可以满足我的一切需求。感谢你的帮助!

于 2013-02-11T10:58:05.677 回答
1

我想你正在尝试用一棵树很容易(只要它只是或语句)。

将(或任何或语句)解析a(b|c)d为如下所示的树:a有孩子bcb并且c有一个共同的孩子db并且c都可以由 0 个或多个节点组成(例如在c这种g(e|f)h情况下(部分)树将为空a -> g -> e/f (2 nodes) -> h -> dc可能为空,在这种情况下(部分)树将为a -> d空,但实际的物理空节点可能会简化东西,你应该在尝试编写代码时看到)。

使用递归或堆栈生成树应该不会太困难。

一旦你有了一棵树,递归地遍历整个事物并生成所有字符串就很简单了。

此外,这里是一个类似问题的链接,提供一两个库。

编辑:

"shouldn't be too difficult"- 好吧,也许不是

是一个有点复杂的示例(Java),可能需要一些有关堆栈的高级知识。

((由于在每个, )),|(等之间插入了一个特殊字符,这是一个稍微简单的版本 (Java)。

请注意,这些都不是特别有效,关键是要传达这个想法。

于 2013-02-10T14:19:37.450 回答
1

这是一个 JavaScript 示例,用于解析 (a|b) 和 (a|b|) 的可能性,创建一个可能的子字符串数组,并根据这个答案组成匹配项。

var regex = /\([RLUD]*\|[RLUD]*\|?\)/, 
    str = "R(LD|DR)U(R|L|)",
    substrings = [], matches = [], str_tmp = str, find

while (find = regex.exec(str_tmp)){
  var index = find.index

  finds = find[0].split(/\|/)
  substrings.push(str_tmp.substr(0, index))

  if (find[0].match(/\|/g).length == 1) 
    substrings.push([finds[0].substr(1), finds[1].replace(/.$/, '')])
  else if (find[0].match(/\|/g).length == 2){
    substrings.push([finds[0].substr(1), ""])
    substrings.push([finds[1], ""])
  }

  str_tmp = str_tmp.substr(index + find[0].length)
}
if (str_tmp) substrings.push([str_tmp])
console.log(substrings) //>>["R", ["LD", "DR"], "U", ["R", ""], ["L", ""]]

//compose matches
function printBin(tree, soFar, iterations) {
  if (iterations == tree.length) matches.push(soFar)
  else if (tree[iterations].length == 2){
      printBin(tree, soFar + tree[iterations][0], iterations + 1)
      printBin(tree, soFar + tree[iterations][1], iterations + 1)
  }
  else printBin(tree, soFar + tree[iterations], iterations + 1)
}
printBin(substrings, "", 0)
console.log(matches) //>>["RLDURL", "RLDUR", "RLDUL", "RLDU", "RDRURL", "RDRUR", "RDRUL", "RDRU"]
于 2013-02-10T19:34:12.320 回答