【算法】友谊与雪花的舞动,脚本解析器原理

这篇具有很好参考价值的文章主要介绍了【算法】友谊与雪花的舞动,脚本解析器原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在11月的下雪天,小悦身处于温暖的办公室中,窗外的雪花在灯光下翩翩起舞。她盯着电脑屏幕,不经意间,一个熟悉的身影从办公室门口处经过,吸引了她的目光。那个人看上去很像是一个女孩,名叫苏菲,是她在大学时期遇到的国外交换生。

小悦的心跳加速,她有些不敢相信自己的眼睛。在她的记忆中,苏菲是一个温柔、聪明且乐于助人的女孩。她们曾经一起上过计算机科学课,苏菲对数学和编程的热爱给小悦留下了深刻的印象。在课程中,苏菲表现出了非凡的编程天赋和扎实的技术功底,她的编程能力让小悦敬佩不已。

小悦忍不住站起来,快步走向那个人。她轻轻地拍了拍她的肩膀,问道:“苏菲?”

那个女孩转过身来,露出了一张熟悉的面孔。她的眼睛闪烁着聪慧的光芒,嘴角挂着一抹微笑。她惊喜地喊道:“小悦?”

两人紧紧地拥抱在一起,重逢的喜悦让她们感到温暖。她们来到楼下的咖啡厅,开始回忆起大学时期的时光,谈论着彼此的生活和工作。

小悦感到非常惊喜,没想到苏菲会来到她的办公楼。苏菲也并不知道小悦在这里工作。或许是命运的安排,让她们再次相遇。小悦心想,一定要好好和苏菲聊聊天,畅谈彼此的近况,重温那段美好的大学时光。

小悦想起了那次计算机科学课的上机考试。她需要实现一个计算器表达式算法。小悦在考试前尝试着自己实现了一个多项式解析式算法,但是效果并不甚理想。这个问题让她感到很困惑,不知道如何着手。就在这时,苏菲主动帮助了她。

苏菲耐心地解释了如何分析问题、拆分令牌、使用逆波兰表达式算法进行计算,以及如何使用栈来处理后缀表达式。她的思路清晰、有条理,让小悦瞬间明白了问题的解决方法。

在苏菲的帮助下,小悦成功地解决了问题,并且在考试中取得了优异的成绩。她一直感激苏菲的帮助和鼓励。

回忆起那段经历,小悦不禁感慨万分。她对苏菲说:“谢谢你,苏菲。你的帮助和支持让我变得更加自信和勇敢。”

苏菲微笑着说:“我们是朋友,小悦。无论何时何地,我都会尽力帮助你。”

她们聊了很久,谈论着过去的点点滴滴和现在的变化。毕业后,苏菲选择继续深造,攻读计算机科学硕博学位。在国外留学期间,她积累了丰富的经验和技能,志向是成为一名计算机科学家。然而,尽管小悦和苏菲在大学时期形影不离,但毕业后两人便各奔东西,相距太远,渐渐失去了联系。她们还计划着再次回到校园,去湖心岛看看那个曾经给她们带来无限回忆的地方。

在分别前,小悦紧紧地拥抱了苏菲一次。她心里默默地想:谢谢你,苏菲,谢谢你陪我度过了那段美好的时光。我会永远珍惜这份友谊。

随着时间的推移,虽然小悦和苏菲的联系越来越少。但是她们的友谊和回忆永远留在彼此的心中。在这个下雪的日子里,小悦重新与苏菲相遇,让她回忆起了那些青葱校园岁月和湖心岛的甜蜜时光。她感到无比幸福和感激,因为她知道这些美好的回忆将永远伴随着她走过人生的旅程。即使未来还有更多的挑战等待着她,小悦也相信自己的能力能够克服一切困难。


当时小悦和苏菲面对的考试题目为:要求计算一个包含数字和运算符的字符串的结果,包括加减乘除和幂运算,还有括号。数字可以是整数或小数,字符串中可以有空格。要求处理字符串中的所有运算符和数字,并计算出最终的结果。

示例:3 * (4 +       (2 / 3) * 6 - 5)=9;

123      -( 4^ (       3 -   1) * 8 - 8      /(     1 + 1 ) *(3 -1) )=3; 


算法实现1:

  1 using System;
  2 using System.Collections.Generic;
  3 
  4 public static class Edm {
  5 
  6   public static double calculate(string input)
  7     {
  8         // 从输入中移除空格
  9         input = input.Replace(" ", "");
 10 
 11         // 将输入转换为令牌列表
 12         List<string> tokens = new List<string>();
 13         string currentToken = "";
 14         foreach (char c in input) {
 15             if (char.IsDigit(c) || c == '.') { // 如果字符是数字或小数点
 16                 currentToken += c; // 将字符添加到当前令牌中
 17             } else {
 18                 if (currentToken != "") { // 如果当前令牌不为空
 19                     tokens.Add(currentToken); // 将当前令牌添加到令牌列表中
 20                     currentToken = ""; // 重置当前令牌
 21                 }
 22                 tokens.Add(c.ToString()); // 将字符转换为字符串并添加到令牌列表中
 23             }
 24         }
 25         if (currentToken != "") { // 如果当前令牌不为空
 26             tokens.Add(currentToken); // 将当前令牌添加到令牌列表中
 27         }
 28 
 29         // 使用逆波兰算法评估令牌
 30         Stack<string> operatorStack = new Stack<string>(); // 操作符栈
 31         Queue<string> outputQueue = new Queue<string>(); // 输出队列
 32         foreach (string token in tokens) {
 33             if (double.TryParse(token, out double num)) { // 如果令牌可以转换为双精度浮点数
 34                 outputQueue.Enqueue(num.ToString()); // 将数字转换为字符串并添加到输出队列中
 35             } else if (IsOperator(token)) { // 如果令牌是操作符
 36                 while (operatorStack.Count > 0 && IsOperator(operatorStack.Peek()) && GetPrecedence(token) <= GetPrecedence(operatorStack.Peek())) {
 37                     outputQueue.Enqueue(operatorStack.Pop()); // 将操作符栈中的操作符弹出并添加到输出队列中
 38                 }
 39                 operatorStack.Push(token); // 将操作符添加到操作符栈中
 40             } else if (token == "(") { // 如果令牌是左括号
 41                 operatorStack.Push(token); // 将左括号添加到操作符栈中
 42             } else if (token == ")") { // 如果令牌是右括号
 43                 while (operatorStack.Count > 0 && operatorStack.Peek() != "(") {
 44                     outputQueue.Enqueue(operatorStack.Pop()); // 将操作符栈中的操作符弹出并添加到输出队列中
 45                 }
 46                 if (operatorStack.Count == 0) {
 47                     throw new Exception("Mismatched parentheses"); // 抛出异常,提示括号不匹配
 48                 }
 49                 operatorStack.Pop(); // 弹出左括号
 50             } else {
 51                 throw new Exception("Invalid token: " + token); // 抛出异常,提示令牌无效
 52             }
 53         }
 54         while (operatorStack.Count > 0) {
 55             if (operatorStack.Peek() == "(") {
 56                 throw new Exception("Mismatched parentheses"); // 抛出异常,提示括号不匹配
 57             }
 58             outputQueue.Enqueue(operatorStack.Pop()); // 将操作符栈中的操作符弹出并添加到输出队列中
 59         }
 60 
 61         // 评估后缀表达式
 62         Stack<double> operandStack = new Stack<double>(); // 操作数栈
 63         foreach (string token in outputQueue) {
 64             if (double.TryParse(token, out double num)) { // 如果令牌可以转换为双精度浮点数
 65                 operandStack.Push(num); // 将数字添加到操作数栈中
 66             } else if (IsOperator(token)) { // 如果令牌是操作符
 67                 double b = operandStack.Pop();
 68                 double a = operandStack.Pop();
 69                 double result = EvaluateOperator(token, a, b); // 计算操作符对应的结果
 70                 operandStack.Push(result); // 将计算结果压入操作数栈中
 71             } else {
 72                 throw new Exception("Invalid token: " + token); // 抛出异常,提示令牌无效
 73             }
 74         }
 75         if (operandStack.Count != 1) {
 76             throw new Exception("Invalid expression"); // 抛出异常,提示表达式无效
 77         }
 78         return operandStack.Pop(); // 返回操作数栈中唯一的元素作为计算结果
 79     }
 80 
 81     static bool IsOperator(string token) {
 82         return token == "+" || token == "-" || token == "*" || token == "/" || token == "^"; // 判断一个字符串是否为操作符(+、-、*、/、^)
 83     }
 84     
 85     static int GetPrecedence(string op) {// 获取操作符优先级
 86         if (op == "^") {
 87             return 3;
 88         } else if (op == "*" || op == "/") {
 89             return 2;
 90         } else if (op == "+" || op == "-") {
 91             return 1;
 92         } else {
 93             return 0;
 94         }
 95     }
 96 
 97    // 计算两个操作数和一个操作符的结果
 98     static double EvaluateOperator(string op, double a, double b) {
 99         if (op == "+") {
100             return a + b;
101         } else if (op == "-") {
102             return a - b;
103         } else if (op == "*") {
104             return a * b;
105         } else if (op == "/") {
106             return a / b;
107         } else if (op == "^") {
108             return Math.Pow(a, b);
109         } else {
110             throw new Exception("Invalid operator: " + op);
111         }
112     }
113 }

这段代码实现了一个表达式计算器,能够接受包含基本数学运算的字符串表达式,并返回计算结果。

  1. 导入了System和System.Collections.Generic命名空间,用于使用标准的数据结构和功能。

  2. 定义了一个静态类Edm,其中包含一个名为calculate的静态方法,该方法接受一个字符串输入并返回一个双精度浮点数。

  3. calculate方法首先去除输入中的空格,然后将输入转换为一个令牌列表(tokens)。

  4. 使用逆波兰算法(shunting yard algorithm)对令牌进行评估。它使用了操作符栈(operatorStack)和输出队列(outputQueue)来对表达式进行处理。

  5. 在这段代码中,我们使用了操作符栈和输出队列来处理给定的令牌(tokens)。

    1. 首先,我们遍历令牌数组,对每个令牌进行处理。
    2. 如果令牌是一个数字,我们将其转换为字符串并添加到输出队列中。
    3. 如果令牌是一个操作符,我们需要根据操作符的优先级来决定其在输出队列中的位置。我们将当前操作符与操作符栈顶的操作符进行比较,如果当前操作符的优先级小于或等于栈顶操作符的优先级,则将栈顶操作符弹出并添加到输出队列中,直到满足条件为止,然后将当前操作符压入操作符栈中。
    4. 如果令牌是左括号,我们直接将其压入操作符栈中。
    5. 如果令牌是右括号,我们需要将操作符栈中的操作符弹出并添加到输出队列中,直到遇到左括号为止。如果在弹出操作符时,操作符栈为空,或者没有遇到左括号,就会抛出异常提示括号不匹配。
    6. 如果令牌不是数字、操作符、左括号或右括号,则抛出异常提示令牌无效。
    7. 最后,处理完所有令牌后,将操作符栈中剩余的操作符依次弹出并添加到输出队列中。如果在此过程中遇到左括号,也会抛出异常提示括号不匹配。

    这样,我们可以将中缀表达式转换为后缀表达式,并且在这个过程中处理了操作符的优先级和括号的匹配关系。

  6. 对后缀表达式进行评估。这部分代码使用了操作数栈(operandStack)来计算后缀表达式的值。

  7. 评估后缀表达式的详细解释:

    1. 创建一个空的操作数栈,用于存储操作数。
    2. 遍历后缀表达式中的每个令牌。
    3. 如果令牌可以转换为双精度浮点数,则将其压入操作数栈中。
    4. 如果令牌是操作符,则从操作数栈中弹出两个操作数(假设为a和b),然后根据该操作符对这两个操作数进行计算,得到结果。
    5. 将计算得到的结果压入操作数栈中。
    6. 如果令牌不是数字也不是操作符,则抛出异常提示令牌无效。
    7. 完成对所有令牌的处理后,操作数栈中应该只剩下一个元素,即为最终的计算结果。如果操作数栈中的元素不止一个,说明表达式无效,抛出异常。
  8. IsOperator方法用于检查一个字符串是否为操作符(+、-、*、/、^)。

  9. GetPrecedence方法用于获取操作符的优先级。

  10. EvaluateOperator方法用于计算两个操作数和一个操作符的结果。


 逆波兰表达式算法由荷兰计算机科学家Edsger Dijkstra在1960年代实现,它也是ALGOL60语言的基础算法,因为在ALGOL60上做出原理性贡献,Dijkstra获得了1972年的图灵奖。而该算法的名称来源于其发明者,波兰数学家Jan Łukasiewicz由1929年提出此数学方法。

逆波兰表达式算法的数学原理可以通过栈的数据结构来解释。我们以中缀表达式 "3 + 4 * 2" 为例进行解释。

  1. 遍历中缀表达式中的每个令牌(数字、操作符)。
  2. 如果令牌是数字,则直接将其添加到输出队列中。
  3. 如果令牌是操作符: a. 如果操作符栈为空,或者操作符栈顶的操作符优先级小于当前操作符,则将当前操作符压入操作符栈中。 b. 如果操作符栈顶的操作符优先级大于等于当前操作符,则将操作符栈顶的操作符弹出并添加到输出队列中,直到满足条件为止,然后将当前操作符压入操作符栈中。
  4. 处理完所有令牌后,将操作符栈中剩余的操作符依次弹出并添加到输出队列中。

以中缀表达式 "3 + 4 * 2" 为例,通过上述步骤,我们可以将中缀表达式转换为后缀表达式 "3 4 2 * +"。

中缀表达式转换为后缀表达式的过程是为了更方便地让计算机进行数学表达式的计算。后缀表达式(也称为逆波兰表达式)具有以下优点:

  1. 没有括号:后缀表达式不需要括号来表示运算的优先级,因此避免了括号所带来的歧义和复杂性。

  2. 易于计算:后缀表达式的计算可以通过简单的迭代算法来实现,不需要递归或者栈来保存中间结果,这使得计算更加高效。

对于后缀表达式 "3 4 2 * +",我们可以按照以下步骤进行计算:

  1. 从左到右扫描后缀表达式,遇到操作数就将其压入栈中。
  2. 遇到操作符时,从栈中弹出相应数量的操作数进行计算,并将计算结果压入栈中。
  3. 最终栈中剩下的元素就是整个表达式的计算结果。

对于后缀表达式 "3 4 2 * +",计算过程如下:

  • 遇到 "3",压入栈中
  • 遇到 "4",压入栈中
  • 遇到 "2",压入栈中
  • 遇到 "*",弹出栈顶的两个元素(4和2),计算结果为8,将结果压入栈中
  • 遇到 "+",弹出栈顶的两个元素(3和8),计算结果为11,将结果压入栈中
  • 最终栈中剩下的唯一元素就是整个表达式的计算结果,即11。

因此,后缀表达式的转换和计算可以使数学表达式在计算机中的处理更加简单和高效。

逆波兰表达式算法之所以受到广泛应用,是因为它能够有效地解决中缀表达式计算的问题。中缀表达式需要使用括号来确定操作符的优先级,而逆波兰表达式则不需要,因为它使用后缀表示法,操作符的优先级可以通过操作符的顺序来确定。

逆波兰表达式算法在计算器、编译器、解释器等领域都有广泛应用。在计算器中,用户输入的数学算式,即中缀表达式通常需要转换为逆波兰表达式才能进行计算。在编译器和sql解释器中,逆波兰表达式算法可以用于计算sql表达式的值,以及将sql表达式转换为可执行代码。


算法实现2:

 1 public static double calculate(string s){
 2      s=s.Replace(" ","");
 3      if (Regex.Match(s,"^[(][\\d\\.\\+\\-\\*/\\^]+[)]$").Success) s=Regex.Replace(s,"^[(]|[)]$","");
 4      if (Regex.Match(s,"^-?[\\d\\.]+$").Success) return double.Parse(s);//检测到单个的正负数字,直接返回值
 5      if (Regex.Match(s,"^\\d[\\d+-\\.]+$").Success) return Regex.Matches(s,"-[\\d\\.]+|(?<=^|[+])[\\d\\.]+").OfType<Match>().Sum(x=>double.Parse(x.Value)); //检测到只包含连续+-符号的算式,返回其运算值
 6      if (Regex.Match(s,"^[\\d\\.]+[*/\\^][\\d\\.]+$").Success) { //检测到两个数的乘除次方,返回其运算值
 7        var tmp=Regex.Split(s,"[*/\\^]").Select(double.Parse).ToArray();
 8        return s.Contains('*') ? tmp[0]*tmp[1] : s.Contains('/') ? tmp[0]/tmp[1] : Math.Pow(tmp[0],tmp[1]);
 9      }
10      if (s.Contains('(')) {  //检测到括号,先运算一个括号单元,然后递归
11        var tmp=Regex.Match(s,"[(][\\d\\.\\+\\-\\*/\\^]+[)]").Value;
12        return calculate(s.Substring(0,s.IndexOf(tmp))+calculate(tmp)+s.Substring(s.IndexOf(tmp)+tmp.Length));
13      }
14      if (s.Contains('^')) {  //检测次方符号,优先运算
15        var tmp=Regex.Match(s,"[\\d\\.]+[\\^][\\d\\.]+").Value;
16        return calculate(s.Substring(0,s.IndexOf(tmp))+calculate(tmp)+s.Substring(s.IndexOf(tmp)+tmp.Length));
17      }
18      if (s.Contains('*')||s.Contains('/')) {  //检测乘除符号,优先运算
19        var tmp=Regex.Match(s,"[\\d\\.]+[\\*/][\\d\\.]+").Value;
20        return calculate(s.Substring(0,s.IndexOf(tmp))+calculate(tmp)+s.Substring(s.IndexOf(tmp)+tmp.Length));
21      }
22      return 0;
23    }

算法2是一个用于计算数学表达式的算法,使用了正则表达式和递归的思想,以及一些基本的操作符处理和字符串处理方法。这种类型的算法通常用于计算机科学和编程中,以便对数学表达式进行求值。

在工作之余,小悦回想起大学时期的那次考试。当时,她运用逆波兰表达式算法成功解决了算式计算器问题。而现在,凭借着工作经验的积累,她采用自己设计的方法——正则表达式和递归算法。

小悦运用正则表达式的强大功能,将输入的算式进行精准的解析和拆分。她巧妙地运用正则表达式的分组和捕获功能,将算式拆分为一个个独立的子表达式。然后,她利用递归算法的特性,对这些子表达式进行递归计算。这种方法的优点在于,它能够处理包含括号、运算符优先级等复杂元素在内的算式,同时还能保证计算过程的灵活性和高效性。

但算法2对于更复杂的脚本语言解释器来说,对于更复杂的表达式,比如函数调用、变量赋值等,就无法处理,可能需要考虑更多的功能和错误处理机制。如果需要实现一个更完整的脚本解释器,还是需要通过算法1进行扩展。


算法1和算法2的比较如下:

算法1优点:

  1. 逆波兰表达式算法不需要使用括号,因此可以减少括号匹配的复杂性。
  2. 逆波兰表达式算法可以直接利用栈结构进行计算,简化了中缀表达式转换为后缀表达式的过程。
  3. 逆波兰表达式算法可以用于解释各种类型的语言和数据格式,从而实现各种解释器。这些解释器可以用于实现各种应用,例如命令行工具、页面脚本语言、配置文件解析器、sql数据库查询解析器等。

算法1缺点:

  1. 中缀表达式转换为后缀表达式的过程可能较为复杂,需要额外的转换步骤。
  2. 逆波兰表达式算法需要额外的数据结构(栈)来进行计算,可能需要更多的内存空间。

算法2优点:

  1. 算法2可以直接对中缀表达式进行递归计算,不需要额外的转换步骤。
  2. 算法2可以直接处理括号、乘除次方等运算符,逻辑相对清晰。

算法2缺点:

  1. 算法2中使用了正则表达式进行匹配,可能在处理复杂表达式时效率较低。
  2. 算法2在处理嵌套括号的情况时可能需要多次递归计算,效率较低。
  3. 算法2对于更复杂的脚本语言解释器来说,不容易扩展和优化。

综合来看,算法1在处理简单表达式时可能更加高效,而算法2在处理复杂数学表达式时可能更加直观和易于理解。具体选择哪种算法取决于实际需求和对算法的偏好。


测试用例:

  1  using NUnit.Framework;
  2   using System;
  3   [TestFixture]
  4   public class CalculatorTest
  5   {
  6     public bool close(double a, double b)
  7     {
  8       if (Math.Abs(a-b)<0.000000001) return true;
  9       return false;
 10     }
 11     [Test]
 12     public void EasyTests()
 13     {
 14       Assert.AreEqual(true, close(Edm.calculate("3 + 5"), 8));
 15       Assert.AreEqual(true, close(Edm.calculate("5 + 41"), 46));
 16       Assert.AreEqual(true, close(Edm.calculate("5 - 3"), 2));
 17       Assert.AreEqual(true, close(Edm.calculate("5 - 5"), 0));
 18       Assert.AreEqual(true, close(Edm.calculate("3 * 5"), 15));
 19       Assert.AreEqual(true, close(Edm.calculate("2 * 23"), 46));
 20       Assert.AreEqual(true, close(Edm.calculate("123 / 3"), 41));
 21       Assert.AreEqual(true, close(Edm.calculate("22 / 1"), 22));
 22     }
 23     
 24     [Test]
 25     public void MediumTests()
 26     {
 27       Assert.AreEqual(true, close(Edm.calculate("3 + 5 * 2"), 13));
 28       Assert.AreEqual(true, close(Edm.calculate("5 - 3 * 8 / 8"), 2));
 29       Assert.AreEqual(true, close(Edm.calculate("6*(2 + 3)"), 30));
 30       Assert.AreEqual(true, close(Edm.calculate("2 ^ 5"), 32));
 31       Assert.AreEqual(true, close(Edm.calculate("5 ^0"), 1));
 32       Assert.AreEqual(true, close(Edm.calculate("23.2- 15.2"), 8));
 33       Assert.AreEqual(true, close(Edm.calculate("22 / 5"), 22.0/5));
 34     }
 35     
 36     [Test]
 37     public void HardTests()
 38     {
 39       Assert.AreEqual(true, close(Edm.calculate("3 * (4 +       (2 / 3) * 6 - 5)"), 9));
 40       Assert.AreEqual(true, close(Edm.calculate("123      -( 4^ (       3 -   1) * 8 - 8      /(     1 + 1 ) *(3 -1) )"), 3));
 41       Assert.AreEqual(true, close(Edm.calculate("4 + 2 * ( (226 - (5 * 3) ^ 2) ^ 2 + (10.7 - 7.4) ^ 2 - 6.89)"),14));
 42       Assert.AreEqual(true, close(Edm.calculate(" (226 - (5 * 3) ^ 2) ^ 2"),1));
 43     }
 44     
 45     [Test]
 46     public void RandomTest() //some really simple random tests just to get this older kata out of beta (@smile67, 26.10.2017)
 47     {
 48        string [] f=new string []{"a * b   -c","a + b/   (b +  c)","(a   + c+  b)  * b  *a"};
 49        Random rnd = new Random();
 50        for (int i=1;i<51;i++) {
 51           int r= rnd.Next(0,3), a= rnd.Next(0,100), b= rnd.Next(0,100), c= rnd.Next(0,100);
 52           string t=f[r].Replace("a",a+"").Replace("b",b+"").Replace("c",c+"");
 53           double s1=Edm.calculate(t), s2=calculate2(t);
 54           Console.WriteLine(i+". Tested for: "+t+", Expected: "+s2+", Got: "+s1);
 55           Assert.AreEqual(true, close(s1, s2));
 56        }
 57     }
 58     
 59     //my old solution (no recursion, so only one function name/call to change - i'm lazy;-))
 60         static int[] priority = new int[11] { 1, 2, 0, -1, -1, 1, 0, 0, 0, 0, 1 };
 61         static double[] numStack = new double[256];
 62         static char[] buffer = new char[256];
 63         
 64         public static double calculate2 (string input)
 65         {
 66             input = input.Replace(" ", "").Replace("^","&")+")";
 67             string number = "";
 68             char ch, chBefore = '(';
 69             int prio, numCount = 0, opCount = 1;
 70             buffer[0] = '('; buffer[1] = '(';
 71             for (int i = 0; i < input.Length; i++)
 72             {
 73                 if (((ch = input[i]) >= '0') || (ch == '.')) number += ch;
 74                 else
 75                 {
 76                     if (chBefore < '0') if (chBefore != ')') if (ch == '-') ch = '%'; 
 77                     if (number.Length > 0) { numStack[++numCount] = Convert.ToDouble(number); number = ""; }
 78                     if (ch > '(')
 79                     {
 80                         prio = priority[ch - 37];
 81                         while (opCount > 0 && prio <= priority[buffer[opCount] - 37])
 82                         {
 83                             --numCount;
 84                             switch (buffer[opCount])
 85                             {
 86                                 case '+': numStack[numCount] = numStack[numCount] + numStack[numCount + 1]; break;
 87                                 case '-': numStack[numCount] = numStack[numCount] - numStack[numCount + 1]; break;
 88                                 case '*': numStack[numCount] = numStack[numCount] * numStack[numCount + 1]; break;
 89                                 case '/': numStack[numCount] = numStack[numCount] / numStack[numCount + 1]; break;
 90                                 case '&': numStack[numCount] = Math.Pow(numStack[numCount], numStack[numCount + 1]); break;
 91                                 case '%': numCount++; numStack[numCount] = -numStack[numCount]; break;
 92                                 case '(': numCount++; prio = 5; break;
 93                             }
 94                             opCount--;
 95                         }
 96                     }
 97                     if (ch != ')') buffer[++opCount] = ch;
 98                 }
 99                 chBefore = ch;
100             }
101             return numStack[1]; 
102         }        
103     
104   }

 文章来源地址https://www.toymoban.com/news/detail-746317.html

// 返回操作数栈中唯一的元素作为计算结果

到了这里,关于【算法】友谊与雪花的舞动,脚本解析器原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 分布式ID生成算法:雪花算法

    雪花算法(Snowflake)是一种分布式ID生成算法,可以生成唯一的、有序的、不重复的ID号,广泛应用于分布式系统中。其生成的ID号由64位二进制数组成,可以转换成16进制或10进制的字符串表示。 雪花算法的核心思想是将一个64位的二进制数分成四部分,分别表示时间戳、数据

    2024年02月15日
    浏览(37)
  • 分布式ID生成算法——雪花算法

    一、分布式ID ID可以唯一标识一条记录。 对于单体架构,我们可以使用自增ID来保证ID的唯一性。但是,在分布式系统中,简单的使用自增ID就会导致ID冲突。这也就引出了 分布式ID 问题。分布式ID也要求满足分布式系统的 高性能、高可用、高并发 的特点。 二、雪花算法 世界

    2024年02月06日
    浏览(48)
  • 【排序算法】推排序算法解析:从原理到实现

    目录 1. 引言 2. 推排序算法原理 3. 推排序的时间复杂度分析 4. 推排序的应用场景 5. 推排序的优缺点分析 5.1 优点: 5.2 缺点: 6. Java、JavaScript 和 Python 实现推排序算法 6.1 Java 实现: 6.2 JavaScript 实现: 6.3 Python 实现: 7. 总结         推排序(Heap Sort)是一种高效的排序算法,

    2024年03月23日
    浏览(50)
  • 雪花算法的使用(java)

    雪花算法( Snowflake )是一种分布式唯一 ID 生成算法,能够生成唯一的、有序的、高可用的 ID,常用于分布式系统中作为全局唯一标识符(GUID)。雪花算法生成的 ID 是一个 64 位的整数,其中高位是时间戳,中间位是机器 ID,低位是序列号。 雪花算法生成的 ID 包含以下信息

    2024年02月01日
    浏览(66)
  • 雪花算法

    雪花算法( SnowFlake ),是Twitter开源 的 分布式ID生成算法  主要是由 64bit 的 long 型生成的全局 ID, 其中分为四个部分: ① 最高1位固定值 0 ,因为生成的 id     是 正整数 ② 后面的41位存储毫秒级 时间戳 ③ 第三部分的10位 存储机器码 ,包括      5位 datacenterId 和5位 workerI

    2024年02月01日
    浏览(28)
  • 细说雪花算法

    需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量。 数据库的扩展方式主要包括:业务分库、主从复制,数据库分表。 雪花算法:Twitter的分布式自增ID算法,Snowflake(雪花算法是由Twitter公布的分布式主键生成算法,它能够保证不同表的主键的

    2024年02月07日
    浏览(28)
  • 拆解雪花算法生成规则

    雪花算法(Snowflake)是一种生成分布式全局唯一ID的算法,生成的ID称为Snowflake IDs或snowflakes。这种算法由Twitter创建,并用于推文的ID。目前仓储平台生成ID是用的雪花算法修改后的版本。 雪花算法几个特性 生成的ID分布式唯一和按照时间递增有序,毫秒数在高位,自增序列在

    2024年02月16日
    浏览(39)
  • UUID的弊端以及雪花算法

    前言 问题 一般通用解决方案 雪花算法 系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠结。 这篇文章就是给各位看官提供一个生成分布式唯一全局id生成方案的思路,希望能帮助到大家。 不足之处,请多多指教!! 基于 Spring Boot + MyBatis

    2024年02月07日
    浏览(81)
  • 雪花算法生成唯一数字id

    2024年02月02日
    浏览(47)
  • 长文解析Resnet50的算法原理

    从打算写图像识别系列文章开始已经快2个月了,目前写了有9篇文章,几乎涵盖了Renset50这一CNN网络95%的算法。 今天整理了下,修复一些笔误和表述错误,整理成了pdf, 同时本文也是整理汇总版。 这篇文章算是偏专业性的深度科普文,将Resnet50这一神经网络(为什么叫这个名字

    2024年02月06日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包