2018年6月
逆波兰式(Reverse Polish notation)
using System; using System.Collections.Generic; using System.Text; namespace Test1 { class Program { static bool IsNumber(string c) { int result = 0; return int.TryParse(c,out result); } static bool IsOperator(string c) { return c == "+" || c == "-" || c == "*" || c == "/" || c == "(" || c == ")"; } static bool ComparePriority(string op1, string op2) { return GetPriority(op1) > GetPriority(op2); } static int GetPriority(string c) { int priority; switch (c) { case "+": priority = 1; break; case "-": priority = 1; break; case "*": priority = 2; break; case "/": priority = 2; break; default: priority = 0; break; } return priority; } static int Calculate(string op, int num1, int num2) { int result = -1; switch (op) { case "+": result = num1 + num2; break; case "-": result = num1 - num2; break; case "*": result = num1 * num2; break; case "/": result = num1 / num2; break; } return result; } static Stack<string> ChangeExpression(List<string> beforeExps) { Stack<string> numberStack = new Stack<string>(); Stack<string> operatorStack = new Stack<string>(); //遍历中序表示 int length = beforeExps.Count; //判断是否为操作数 for (int i = 0; i < length; i++) { string c = beforeExps[i]; if (IsNumber(c)) { //操作数 存在操作数栈 numberStack.Push(c); } else { //为运算符 //若运算符为"("直接存入到运算符栈中 if (c == "(") { operatorStack.Push(c); } else if (c == ")") { //该运算符为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为止。 将"("出栈 while (operatorStack.Peek() != "(") { string stringvalue = operatorStack.Pop(); numberStack.Push(stringvalue); } operatorStack.Pop(); } else { // 该运算符为非括号运算符: //考虑栈顶为空的情况 if (operatorStack.Count <= 0) { operatorStack.Push(c); continue; } // (a) 若运算符堆栈栈顶的运算符为括号,则直接存入运算符堆栈。 ////符合为左括号 直接存入运算符 if (operatorStack.Peek() == "(") { operatorStack.Push(c); } else { //(b) 若比运算符堆栈栈顶的运算符优先级高或相等,则直接存入运算符堆栈。 if (ComparePriority(c, operatorStack.Peek())) { operatorStack.Push(c); } else { // (c) 若比运算符堆栈栈顶的运算符优先级低,则输出栈顶运算符到操作数堆栈,并将当前运算符压入运算符堆栈。 string stringvalue = operatorStack.Pop(); numberStack.Push(stringvalue); operatorStack.Push(c); } } } } } //4、当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。 while (operatorStack.Count > 0) { string stringvalue = operatorStack.Pop(); numberStack.Push(stringvalue); } //反转operand 获取正常的会缀表达式 Stack<string> resultSt = new Stack<string>(); while (numberStack.Count > 0) { string stringvalue = numberStack.Pop(); resultSt.Push(stringvalue); } return resultSt; } //转换string为list表 static List<string> changeStrToList(string str) { List<string> resultSt = new List<string>(); List<int> sortNum = new List<int>(); bool isConNum = false; foreach (char c in str) { if (IsOperator(c.ToString())) { if (isConNum && sortNum.Count > 0) { //添加数字 int num = 0; for (int i = sortNum.Count - 1; i >= 0; i--) { if (i == sortNum.Count - 1) { num = num + sortNum[i]; } else { num = num + sortNum[i] * 10 * (sortNum.Count - 1 - i); } } resultSt.Add(num.ToString()); sortNum.Clear(); } isConNum = false; //如果是操作符直接添加 resultSt.Add(c.ToString()); } else { //如果是数字 isConNum = true; sortNum.Add(int.Parse(c.ToString())); } } if (sortNum.Count > 0) { //添加数字 int num = 0; for (int i = sortNum.Count - 1; i >= 0; i--) { if (i == sortNum.Count - 1) { num = num + sortNum[i]; } else { num = num + sortNum[i] * 10 * (sortNum.Count - 1 - i); } } resultSt.Add(num.ToString()); sortNum.Clear(); } return resultSt; } //计算逆波兰公式 static int calculateExpression(Stack<string> st) { //临时存储计算数据 Stack<string> reslutSt = new Stack<string>(); while (st.Count > 0) { string numStr = st.Peek(); if (IsNumber(numStr)) { //如果字符是一个操作数,把它压入堆栈。 reslutSt.Push(numStr); } else { //如果字符是个操作符,弹出两个操作数,执行恰当操作, //然后把结果压入堆栈。如果您不能够弹出两个操作数,后缀表达式的语法就不正确。 int number1 = int.Parse(reslutSt.Pop()); int number2 = int.Parse(reslutSt.Pop()); int value = Calculate(numStr, number2, number1); reslutSt.Push(value.ToString()); } st.Pop(); } return int.Parse(reslutSt.Peek()); } static void Main(string[] args) { Console.WriteLine("C# 逆波兰公式 输入您要计算的公式,按enter结束"); //string inputStr = Console.ReadLine(); string teststr = Console.ReadLine(); List<string> inputStr = changeStrToList(teststr); Console.WriteLine("公式为:"); foreach (string str in inputStr) { Console.Write("{0} ", str); } Console.Write(" ===> "); Stack<string> changeSt = ChangeExpression(inputStr); foreach (string str in changeSt) { Console.Write("{0} ", str); } Console.WriteLine("逆波兰公式计算:"); //计算公式的值 int resultValue = calculateExpression(changeSt); Console.WriteLine("{0} = {1} ", teststr, resultValue); Console.ReadKey(); } } }
Dijkstra 双栈算术表达式
public static Double Evaluate(string str) { Stack<double> values = new Stack<double>(); Stack<Char> ops = new Stack<char>(); char[] chs = str.ToCharArray(); for(int i = 0; i < chs.Length; i++) { if(chs[i] == '+') { ops.Push(chs[i]); }else if(chs[i] == '-') { ops.Push(chs[i]); } else if(chs[i] == '*') { ops.Push(chs[i]); } else if(chs[i] == '/') { ops.Push(chs[i]); } else if(chs[i] == '(') { } else if(chs[i] == ')') { char op = ops.Pop(); double value = values.Pop(); if(op == '+') { value = value + values.Pop(); } else if(op == '-') { value = values.Pop() - value; } else if(op == '*') { value = values.Pop() * value; } else if(op == '/') { value = values.Pop() / value; } values.Push(value); } else { values.Push(double.Parse(chs[i].ToString())); } } return values.Pop(); }
function baz() { // 当前调用栈是: baz // 因此,当前调用位置是全局作用域 console.log("baz"); bar(); // <-- bar的调用位置 } function bar() { // 当前调用栈是baz -> bar // 因此,当前调用位置在baz中 console.log("bar"); foo(); // <-- foo的调用位置 } function foo() { // 当前调用栈是baz -> bar -> foo // 因此,当前调用位置在bar中 console.log("foo"); } baz(); // <-- baz的调用位置
<script type="text/javascript"> function foo() { // this == window console.log(this.a);debugger; } var a = 2; foo(); </script>
debugger;
函数this的默认绑定为全局对象,严格模式下"use strict",this默认绑定到undefined,因为全局对象无法使用默认绑定
// 无论是直接在obj中定义还是先定义再添加为引用属性,这个函数严格来说i都不属于obj对象 function foo() { console.log(this.a); } var obj = { a:2, foo:foo }; // 调用位置会使用obj上下文来引用函数,因此你可以说函数被调用时obj对象"拥有"或者"包含"它 // 当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象. // 因为调用foo()时this被绑定到obj,因此this.a和obj.a是一样的 obj.foo();
var obj = { a:2, foo:foo }; // 虽然bar是obj.foo的一个引用,但实际上,它引用的是foo函数本身 // 因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定 var bar = obj.foo; var a = "oops,global"; bar();
function foo() { console.log(this.a); } function doFoo(fn) { // 参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值. // fn其实引用的是foo fn(); } var obj = { a:2, foo:foo }; var a = "oops,global"; doFoo(obj.foo);
function foo() { console.log(this.a); } var obj = { a:2, foo:foo }; var a = "oops,global"; setTimeout(obj.foo, 100); // pseudo setTimeout function setTimeout(fn,delay) { fn(); }
function foo(something) { console.log(this.a,something); return this.a + something; } var obj = { a:2 }; // bind(..)会返回一个硬编码的新函数,它会把参数设置为this的上下文并调用原始函数 var bar = foo.bind(obj); var b = bar(3); console.log(b);
function foo(a) { this.a = a; } // 实际上并不存在所谓的"构造函数",只有对于函数的"构造调用" // 使用new来调用foo(..)时,我们会构造一个新对象并把它绑定到foo(..)调用中的this上. // new是最后一种可以影响函数调用时this绑定行为的方法,我们称之为new绑定 var bar = new foo(2); console.log(bar.a);
function foo() { console.log(this.a); } var obj1 = { a:2, foo:foo }; var obj2 = { a:3, foo:foo }; obj1.foo(); obj2.foo(); // 显式绑定优先级更高 obj1.foo.call(obj2); obj2.foo.call(obj1);
function foo(something) { this.a = something; } var obj1 = { foo:foo }; var obj2 = {} obj1.foo(2); console.log(obj1.a); // 2 obj1.foo.call(obj2,3); console.log(obj2.a); // 3 var bar = new obj1.foo(4); console.log(obj1.a); // 2 console.log(bar.a); // 4
function foo(something) { this.a = something; } var obj1 = { foo:foo }; var obj2 = {} obj1.foo(2); console.log(obj1.a); // 2 obj1.foo.call(obj2,3); console.log(obj2.a); // 3 var bar = new obj1.foo(4); console.log(obj1.a); // 2 console.log(bar.a); // 4 new绑定比隐式绑定优先级高
function foo(something) { this.a = something; } var obj1 = {} var bar = foo.bind(obj1); bar(2); console.log(obj1.a); // 2 var baz = new bar(3); console.log(obj1.a); // 2 console.log(baz.a); // 3
// 判断this // 1. 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象 var bar = new foo(); // 2. 函数是否通过call,apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象 var bar = foo.call(obj2); // 3. 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象. var bar = obj1.foo(); // 4. 如果都不是的话,使用默认绑定.如果在严格模式下,就绑定到undefined,否则绑定到全局对象 var bar = foo()
// ES6中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定this, // 具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定到什么). // 这其实和ES6之前代码中的self=this机制一样 function foo() { // 返回一个箭头函数 return (a) => { // this 继承自foo() console.log(this.a); }; } var obj1 = { a:2 }; var obj2 = { a:3 }; // foo()内部创建的箭头函数会捕获调用时foo()的this. // 由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1, // 箭头函数的绑定无法被修改(new也不行!) var bar = foo.call(obj1); bar.call(obj2); // 2
// 简单基本类型 console.log(typeof "a"); // string console.log(typeof 1); // number console.log(typeof true); // boolean console.log(typeof null); // object 语言本身的bug console.log(typeof undefined); // undefined console.log(typeof {}); // object // javascript中有许多特殊的对象子类型,我们可以称之为复杂基本类型 函数 数组 // 内置对象,实际上只是一些内置函数,这些函数可以当作构造函数来使用, String Number Boolean Object Function Array Date RegExp Error var strPrimitive = "I am a string"; console.log(typeof strPrimitive); // "string" console.log(strPrimitive instanceof String); // false var strObject = new String("I am a string"); console.log(typeof strObject); // "object" console.log(strObject instanceof String); // true // 检查 sub-type 对象 console.log(Object.prototype.toString.call(strObject)); // [object String]
var myObject = { a:2 }; console.log(myObject.a); // 属性访问 console.log(myObject["a"]); // 键访问 // 在对象中,属性名永远都是字符串.如果你使用string(字面量)以外的其他值作为属性名,那它首先会被转换为一个字符串 var myObject1 = {}; myObject1[true] = "foo"; myObject1[3] = "bar"; myObject1[myObject1] = "baz"; console.log(myObject1["true"]); // "foo" console.log(myObject1["3"]); // "bar" console.log(myObject1["[object Object]"]); // "baz"
// 可计算属性名 var prefix = "foo"; var myObject = { [prefix + "bar"]:"hello", [prefix + "baz"]:"world" }; console.log(myObject["foobar"]); console.log(myObject["foobaz"]);
GO