我有一个奇怪的问题,我接收计算机生成的方程式(作为一个字符串),其中偶尔存在零或一和零的乘法/除法。这些方程将以字符串形式呈现给用户。
我知道我可以通过实现一种解析器来删除等式中的这些冗余部分,但我很好奇是否可以使用正则表达式来删除它们。
在我最终放弃了(非常有限的)正则表达式技能之前,我想出了以下内容:
/([^\+\-]*(?:0|0\*|\*0){1,}[^\+\-]*)|(?:1\*|\*1)/g
它似乎只在以下情况下有用:
它也不适用于括号。不幸的是,括号很常见。
请注意,删除上述冗余部分可能会导致冗余括号或“零”括号(即它可能会变成()*x
,相当于0*x
)。冗余括号不是一个问题,但我认为可以通过类似于第一次查找()
的第二次传递来删除“零”括号。如果其中任何一个都可以在与解决问题的正则表达式相同的正则表达式中完成,那么我会留下非常深刻的印象。
所以我转向你的Stack Overflow正则大师。可以吗?
对于字符串化方程,可以假设如下:
[expr]/0
甚至是[expr]/0
评估为[expr]/sin(0)
的表达式,例如+
。-
*
/
和-
。 sin
)包括减法和否定,尽管否定是 始终 括在括号内。cos
,pow
,^
等)都将显示为函数调用。 (没有%
"(0+(0/0.5+(0+1*cos(p)+0*0+0*sin(p))*cos(k)+(0+1*0+0*1+0*0)*(-sin(k))+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*x+(0+(0+1*cos(p)+0*0+0*sin(p))*sin(k)+(0+1*0+0*1+0*0)*cos(k)+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*y+(0+(0+1*cos(p)+0*0+0*sin(p))*0+(0+1*0+0*1+0*0)*0+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*1)*z)"
等){{1}}
不是很乱吗?
答案 0 :(得分:3)
在发表评论后,我无法抗拒它:)
你最大的问题是嵌套括号。正则表达式本身在处理嵌套结构时非常糟糕。这是我的口头禅的一个主要例子“正则表达式是工具,而不是解决方案”。
以正则表达式作为工具,您可以为此树结构应用一种“叶子优先”(或自下而上)方法,这就是我在第一部分while (sEq.match(...)) {...}
中所做的。之后,我可以浏览创建的array
并进行一些简单的文本编辑。
我还补充说,1*
,*1
和/1
会被删除,因为它们同样不会影响等式。您可以扩展它以使其足够智能,分别用sin(0)
和cos(0)
替换0
/ 1
,然后在某些情况下解决方案会更小。
(如代码注释中所述,如果等式包含5.0*4
等内容,则会中断,因为JavaScript正则表达式没有lookbehind所以我相信\b
字边界为我做这项工作。只需添加删除不必要的小数的逻辑就可以解决这个问题。像sEq = sEq.replace(/\.0+\b/g, '');
这样的东西,但我不知道你的用例是否有必要。) 编辑:现在已修复,5.0*4
应保持原状
虽然没有经过彻底的测试,欢迎回复。
var sEq = "(0+(0/0.5+(0+1*cos(p)+0*0+0*sin(p))*cos(k)+(0+1*0+0*1+0*0)*(-sin(k))+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*x+(0+(0+1*cos(p)+0*0+0*sin(p))*sin(k)+(0+1*0+0*1+0*0)*cos(k)+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*y+(0+(0+1*cos(p)+0*0+0*sin(p))*0+(0+1*0+0*1+0*0)*0+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*1)*z)";
var aParts = [];
document.getElementById('output').value = sEq + '\n';
while (sEq.match(/\([^()]*\)/)) {
// while there are still "leafs", save them to aParts and replace with
// a reference to their index in aParts, making their parent a new
// "leaf" because it now doesn't contain the round brackets anymore
sEq = sEq.replace(/([a-z]*)\(([^()]*)\)/gi, function(m, f, a) {
var n = aParts.length;
aParts[n] = {
'found':m,
'funct':f,
'arith':a
};
return '[' + n + ']';
});
}
for (var i = 0; i < aParts.length; i++) {
// isolate divisions/multiplications
var dms = aParts[i]['arith'].split(/(?=[+-])/);
for (var j = 0; j < dms.length; j++) {
// if the isolated part is multiplied by or divided into 0, replace with 0
if (dms[j].match(/([^.]|^)\b0[*\/]|\*0(?!\.?0*[1-9])/)) {
dms[j] = dms[j].replace(/([+-]?).*/, '$1'+'0');
}
// remove /1, *1 and 1*
dms[j] = dms[j].replace(/[\/*]1\b(?!\.0*[1-9])(?:\.0*)?/g, '').replace(/([^.]|^)\b1\*/g, '$1');
}
// join back together, remove 0+, +0 and -0; 0- results in negation
aParts[i]['arith'] = dms.join('').replace(/[+-]0(?!\.?0*[1-9])(?:\.?0*)?/g, '').replace(/([^.]|^)\b0(?:\+|(-))/g, '$1$2');
// if after this the part contains just 0, perpetuate down to further eliminate
if (aParts[i]['funct']=='' && aParts[i]['arith']=='0') {
for (var j = i + 1; j < aParts.length; j++) {
if (aParts[j]['arith'].indexOf('[' + i + ']') != -1) {
aParts[j]['arith'] = aParts[j]['arith'].replace('[' + i + ']', '0');
break;
}
}
}
// add back parts previously simplified by replacing [n] with the content of aParts[n]
aParts[i]['arith'] = aParts[i]['arith'].replace(/\[(\d+)\]/g, function (m, n) {
return aParts[parseInt(n)]['funct'] + '(' + aParts[parseInt(n)]['arith'] + ')';
});
// This is just to show the progress of the algorithm
document.getElementById('parts').value += i + '\t' + aParts[i]['found'] + '\n';
var tmp = [];
for (var a = 0; a < aParts.length; a++) {
tmp[a] = {
'funct':aParts[a]['funct'],
'arith':aParts[a]['arith'].replace(/\[(\d+)\]/g, function (m, n) {
return tmp[parseInt(n)]['funct'] + '(' + tmp[parseInt(n)]['arith'] + ')';
})
};
}
// some steps didn't change after analysing, only append when significant
if (document.getElementById('output').value.indexOf('\n' + tmp[tmp.length-1]['arith'] + '\n') ==-1)
document.getElementById('output').value += tmp[tmp.length-1]['arith'] + '\n';
}
document.getElementById('solution').innerHTML =
aParts[aParts.length-1]['funct'] +
'(' + aParts[aParts.length-1]['arith'] + ')';
<h3>Parts isolated:</h3>
<textarea id="parts" rows="10" style="width:100%" wrap="off"></textarea>
<h3>Steps that simplified the equation:</h3>
<textarea id="output" rows="10" style="width:100%" wrap="off"></textarea>
<h3>Solution:</h3>
<pre id="solution"></pre>
答案 1 :(得分:2)
我最终实现了一个完全非正则表达式的递归方法来解决问题。 cleanupEqn()
函数实际上是通过运算符对每个字符串进行拆分(顶级括号被分组为单个操作数),对每个子部分进行递归操作,然后在备份函数调用链的过程中执行另一个操作。
Comparing this with funkwurm's regex solution in jsperf显示它明显更快(至少在我的个人Chrome和Firefox浏览器中)。
它还没有经过全面测试,我确信可以进行改进,所以我欢迎任何反馈。
窃取funkwurm的片段显示以显示我的解决方案:
var sEq = "(0+(0/0.5+(0+1*cos(p)+0*0+0*sin(p))*cos(k)+(0+1*0+0*1+0*0)*(-sin(k))+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*x+(0+(0+1*cos(p)+0*0+0*sin(p))*sin(k)+(0+1*0+0*1+0*0)*cos(k)+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*0)*y+(0+(0+1*cos(p)+0*0+0*sin(p))*0+(0+1*0+0*1+0*0)*0+(0+1*(-sin(p))/0.5+0*0+0*cos(p))*1)*z)";
var operators = ['+','-','*','/'];
var level = 0;
var result = cleanupEqn(sEq);
document.getElementById('solution').innerHTML = result;
function cleanupEqn(eqn){
var parts = removeRedundant(splitByParen(eqn));
level++;
document.getElementById('output').value += 'Level ' + level + ': Processing ' + eqn + '\n';
for(var i=0; i < parts.length; i++){
document.getElementById('parts').value += parts[i] + '\n';
if(parts[i].charAt(0) === '('){
// Clean up the expression inside the parentheses
var tmp = cleanupEqn(parts[i].substring(1,parts[i].length-1));
// If it was reduced to a zero, don't add the parentheses back
if(tmp === '0'){
parts[i] = '0';
}
else {
parts[i] = '(' + tmp + ')';
}
}
}
// Finally, remove redundancies again, since some might have bubbled up.
removeRedundant(parts);
document.getElementById('output').value += 'Level ' + level + ': Completed ' + eqn + '\n' + JSON.stringify(parts, null, '\t') + '\n';
level--;
// Join it all into a string and return
return parts.join('');
}
function splitByParen(str){
var out = [];
var exprStart = 0;
var count = 0;
var i;
for (i = 0; i < str.length; i++) {
var t = str.charAt(i);
if(str.charAt(i) === '('){
if(i > exprStart && count === 0){
out.push(str.substring(exprStart, i));
exprStart = i;
}
count++;
}
else if(str.charAt(i) === ')'){
count--;
if(count === 0){
out.push(str.substring(exprStart, i+1));
exprStart = i+1;
}
}
else if(count === 0 && operators.indexOf(str.charAt(i)) > -1){
if(i > exprStart){
out.push(str.substring(exprStart, i));
}
out.push(str.charAt(i));
exprStart = i+1;
}
}
// Add the last part
if(i > exprStart){
out.push(str.substring(exprStart, i));
}
return out;
}
function removeRedundant(parts) {
for(var i=0; i < parts.length; i++){
if(parts[i] === '0'){
if(i === 0){
switch(parts[i+1]){
case '*':
case '/':
if(parts[i+1] === '*' || parts[i+1] === '/'){
parts.splice(i, 3, '0');
}
else {
parts.splice(i, 2);
}
i--;
break;
case '+':
parts.splice(i, 2);
i--;
break;
case '-':
parts.splice(i, 1);
i--;
}
}
else {
switch(parts[i-1]){
case '*':
if(parts[i+1] === '*' || parts[i+1] === '/'){
// Check if the prior portion is part of a function call
if(i > 2 && operators.indexOf(parts[i-3]) < 0){
// Check if the next portion is part of a function call (or undefined)
if(i+3 < parts.length && operators.indexOf(parts[i+3]) < 0){
parts.splice(i-3, 6, '0');
i -= 4;
}
else {
parts.splice(i-3, 5, '0');
i -= 4;
}
}
else {
parts.splice(i-2, 4, '0');
i -= 3;
}
}
else {
parts.splice(i-2, 3, '0');
i -= 3;
}
break;
case '+':
case '-':
if(parts[i+1] === '*' || parts[i+1] === '/'){
// Check if the next portion is part of a function call (or undefined)
if(i+3 < parts.length && operators.indexOf(parts[i+3]) < 0){
parts.splice(i, 4, '0');
}
else {
parts.splice(i, 3, '0');
}
i--;
}
else if(parts[i+1] === '+'){
parts.splice(i-1, 2);
i -= 2;
}
else {
parts.splice(i-1, 2);
i -= 2;
}
}
}
}
else if(parts[i] === '1'){
if(i === 0){
switch(parts[i+1]){
case '*':
parts.splice(i, 2);
i--;
break;
case '+':
case '-':
if(parts[i+1] === '*'){
parts.splice(i, 2);
i--;
}
}
}
switch(parts[i-1]){
case '*':
case '/':
if(parts[i+1] !== '/'){
parts.splice(i-1, 2);
i -= 2;
}
break;
case '+':
case '-':
if(parts[i+1] === '*'){
parts.splice(i, 2);
i--;
}
}
}
}
return parts;
}
&#13;
<h3>Parts isolated:</h3>
<textarea id="parts" rows="10" style="width:100%" wrap="off"></textarea>
<h3>Steps that simplified the equation:</h3>
<textarea id="output" rows="10" style="width:100%" wrap="off"></textarea>
<h3>Solution:</h3>
<pre id="solution"></pre>
<script src="new.js"></script>
&#13;