在 Javascript 中有切片的替代方法吗?
Is there an alternative to slice in Javascript?
我正在开发一个使用数组的计算器项目。我想让用户在找到答案之前编写多个函数,类似于 Casio fx-300ES Plus。现在我正在研究乘法,然后再转向其他运算符。为此,我认为最好的方法是使用 for 循环找到所有 'x' 所在的索引,然后 运行 两个其他 for 循环,一个在运算符的左侧,另一个看着它的右边。一旦找到另一个操作员,它就会崩溃。然后我可以使用 slice().
将信息存储在 'x' 旁边
我遇到的问题 运行 是当运算符之间的数字为 1 时。如果我使用 slice() 它不起作用,因为索引之间没有信息。还有其他方法可以将这些数字存储到数组中吗?
非常感谢任何有关此的信息。
var array = ['7', '3', '+', '6', 'x', '8', '+', '5', '4', 'x', '2'];
//for loop checking for 'x' symbols
for (var i = 0; i < array.length; i++){
console.log("i " + array[i]);
//if there is an 'x'
if (array[i] == 'x') {
console.log('index is at ' + i);
//create an array to eventually store the values
var newArray = new Array();
//checks for the index where j is NaN on the LEFT side
for (j = i - 1; j > 0; --j){
if (isNaN(array[j])){
console.log('j is ' + j);
break;
}
}
//checks for the index where e is NaN on the RIGHT side
for (e = i + 1; e < array.length; e++)
{
if (isNaN(array[e])){
console.log('e is at ' + e);
break;
} else if (e == array.length - 1) {
console.log('e is at array length of ' + e);
break;
}
}
//add the numbers between j and i to newArray
newArray = array.slice(j + 1, i);
console.log(newArray);
//add the numbers between i and e to newArray
newArray = array.slice(i + 1, e);
console.log(newArray);
console.log("array of slice is " + newArray);
}
}
您可以编写 tokenize
来消耗您的文本输入并生成标记流 -
const input =
[ '7', '3', '+', '6', 'x', '8', '+', '5', '4', 'x', '2' ]
function* tokenize (es) {
let r = 0, n
for (const e of es) {
switch (e) {
case "+":
case "x":
yield { number: r }
yield { operation: e }
r = 0
break
default:
n = Number.parseInt(e, 10)
if (Number.isNaN(n))
throw Error(`unexpected input: ${e}`)
else
r = r * 10 + n
break
}
}
if (r > 0) yield { number: r }
}
for (const t of tokenize(input))
console.log(t)
{"number":73}
{"operation":"+"}
{"number":6}
{"operation":"x"}
{"number":8}
{"operation":"+"}
{"number":54}
{"operation":"x"}
{"number":2}
之后是 parse
,它使用标记流并生成抽象语法树 -
function parse (ts) {
let s = e => e
for (const t of ts) {
if (t?.number) {
let r = s(t)
s = _ => r
}
else if (t?.operation) {
let left = s()
s = right => ({ operation: t.operation, left, right })
}
else {
throw Error(`unexpected token: ${JSON.stringify(t)}`)
}
}
return s()
}
console.log(parse(tokenize(input)))
{
operation: "x",
left: {
operation: "+",
left: {
operation: "x",
left: {
operation: "+",
left: { number: 73 },
right: { number: 6 }
},
right: { number: 8 }
},
right: { number: 54 }
},
right: { number: 2 }
}
最后eval
将语法树作为一个程序进行评估-
function eval (e) {
if (e?.number)
return e.number
else if (e?.operation)
return evalOp(e)
else
throw Error(`unexpected expression: ${JSON.stringify(e)}`)
}
我们通过单独写evalOp
来简化eval
。这种技术称为相互递归,对于遍历树状结构非常有效 -
function evalOp (e) {
switch (e?.operation) {
case "+": return eval(e.left) + eval(e.right)
case "x": return eval(e.left) * eval(e.right)
default: throw Error(`unexpected operation: ${e.operation}`)
}
}
将tokenize
、parse
和eval
放在一起计算结果-
const input =
[ '7', '3', '+', '6', 'x', '8', '+', '5', '4', 'x', '2' ]
console.log(eval(parse(tokenize(input))))
1372
使用袖珍计算器运算顺序计算时这是正确的 -
73
... + 6 = 79
... * 8 = 632
... + 54 = 686
... * 2 = 1372
如果您想使用不同的操作顺序进行评估,即 PEMDAS,您将不得不重写 parse
以将令牌流解释为不同的程序。
展开下面的代码片段以在浏览器中验证结果 -
const input =
[ '7', '3', '+', '6', 'x', '8', '+', '5', '4', 'x', '2' ]
function* tokenize (es) {
let r = 0, n
for (const e of es) {
switch (e) {
case "+":
case "x":
yield { number: r }
yield { operation: e }
r = 0
break
default:
n = Number.parseInt(e, 10)
if (Number.isNaN(n))
throw Error(`unexpected input: ${e}`)
else
r = r * 10 + n
break
}
}
if (r > 0) yield { number: r }
}
function parse (ts) {
let s = e => e
for (const t of ts) {
if (t?.number) {
let r = s(t)
s = _ => r
}
else if (t?.operation) {
let left = s()
s = right => ({ operation: t.operation, left, right })
}
else {
throw Error(`unexpected token: ${JSON.stringify(t)}`)
}
}
return s()
}
function eval (e) {
if (e?.number)
return e.number
else if (e?.operation)
return evalOp(e)
else
throw Error(`unexpected expression: ${JSON.stringify(e)}`)
}
function evalOp (e) {
switch (e?.operation) {
case "+": return eval(e.left) + eval(e.right)
case "x": return eval(e.left) * eval(e.right)
default: throw Error(`unexpected operation: ${e.operation}`)
}
}
console.log(eval(parse(tokenize(input))))
我正在开发一个使用数组的计算器项目。我想让用户在找到答案之前编写多个函数,类似于 Casio fx-300ES Plus。现在我正在研究乘法,然后再转向其他运算符。为此,我认为最好的方法是使用 for 循环找到所有 'x' 所在的索引,然后 运行 两个其他 for 循环,一个在运算符的左侧,另一个看着它的右边。一旦找到另一个操作员,它就会崩溃。然后我可以使用 slice().
将信息存储在 'x' 旁边我遇到的问题 运行 是当运算符之间的数字为 1 时。如果我使用 slice() 它不起作用,因为索引之间没有信息。还有其他方法可以将这些数字存储到数组中吗?
非常感谢任何有关此的信息。
var array = ['7', '3', '+', '6', 'x', '8', '+', '5', '4', 'x', '2'];
//for loop checking for 'x' symbols
for (var i = 0; i < array.length; i++){
console.log("i " + array[i]);
//if there is an 'x'
if (array[i] == 'x') {
console.log('index is at ' + i);
//create an array to eventually store the values
var newArray = new Array();
//checks for the index where j is NaN on the LEFT side
for (j = i - 1; j > 0; --j){
if (isNaN(array[j])){
console.log('j is ' + j);
break;
}
}
//checks for the index where e is NaN on the RIGHT side
for (e = i + 1; e < array.length; e++)
{
if (isNaN(array[e])){
console.log('e is at ' + e);
break;
} else if (e == array.length - 1) {
console.log('e is at array length of ' + e);
break;
}
}
//add the numbers between j and i to newArray
newArray = array.slice(j + 1, i);
console.log(newArray);
//add the numbers between i and e to newArray
newArray = array.slice(i + 1, e);
console.log(newArray);
console.log("array of slice is " + newArray);
}
}
您可以编写 tokenize
来消耗您的文本输入并生成标记流 -
const input =
[ '7', '3', '+', '6', 'x', '8', '+', '5', '4', 'x', '2' ]
function* tokenize (es) {
let r = 0, n
for (const e of es) {
switch (e) {
case "+":
case "x":
yield { number: r }
yield { operation: e }
r = 0
break
default:
n = Number.parseInt(e, 10)
if (Number.isNaN(n))
throw Error(`unexpected input: ${e}`)
else
r = r * 10 + n
break
}
}
if (r > 0) yield { number: r }
}
for (const t of tokenize(input))
console.log(t)
{"number":73}
{"operation":"+"}
{"number":6}
{"operation":"x"}
{"number":8}
{"operation":"+"}
{"number":54}
{"operation":"x"}
{"number":2}
之后是 parse
,它使用标记流并生成抽象语法树 -
function parse (ts) {
let s = e => e
for (const t of ts) {
if (t?.number) {
let r = s(t)
s = _ => r
}
else if (t?.operation) {
let left = s()
s = right => ({ operation: t.operation, left, right })
}
else {
throw Error(`unexpected token: ${JSON.stringify(t)}`)
}
}
return s()
}
console.log(parse(tokenize(input)))
{
operation: "x",
left: {
operation: "+",
left: {
operation: "x",
left: {
operation: "+",
left: { number: 73 },
right: { number: 6 }
},
right: { number: 8 }
},
right: { number: 54 }
},
right: { number: 2 }
}
最后eval
将语法树作为一个程序进行评估-
function eval (e) {
if (e?.number)
return e.number
else if (e?.operation)
return evalOp(e)
else
throw Error(`unexpected expression: ${JSON.stringify(e)}`)
}
我们通过单独写evalOp
来简化eval
。这种技术称为相互递归,对于遍历树状结构非常有效 -
function evalOp (e) {
switch (e?.operation) {
case "+": return eval(e.left) + eval(e.right)
case "x": return eval(e.left) * eval(e.right)
default: throw Error(`unexpected operation: ${e.operation}`)
}
}
将tokenize
、parse
和eval
放在一起计算结果-
const input =
[ '7', '3', '+', '6', 'x', '8', '+', '5', '4', 'x', '2' ]
console.log(eval(parse(tokenize(input))))
1372
使用袖珍计算器运算顺序计算时这是正确的 -
73
... + 6 = 79
... * 8 = 632
... + 54 = 686
... * 2 = 1372
如果您想使用不同的操作顺序进行评估,即 PEMDAS,您将不得不重写 parse
以将令牌流解释为不同的程序。
展开下面的代码片段以在浏览器中验证结果 -
const input =
[ '7', '3', '+', '6', 'x', '8', '+', '5', '4', 'x', '2' ]
function* tokenize (es) {
let r = 0, n
for (const e of es) {
switch (e) {
case "+":
case "x":
yield { number: r }
yield { operation: e }
r = 0
break
default:
n = Number.parseInt(e, 10)
if (Number.isNaN(n))
throw Error(`unexpected input: ${e}`)
else
r = r * 10 + n
break
}
}
if (r > 0) yield { number: r }
}
function parse (ts) {
let s = e => e
for (const t of ts) {
if (t?.number) {
let r = s(t)
s = _ => r
}
else if (t?.operation) {
let left = s()
s = right => ({ operation: t.operation, left, right })
}
else {
throw Error(`unexpected token: ${JSON.stringify(t)}`)
}
}
return s()
}
function eval (e) {
if (e?.number)
return e.number
else if (e?.operation)
return evalOp(e)
else
throw Error(`unexpected expression: ${JSON.stringify(e)}`)
}
function evalOp (e) {
switch (e?.operation) {
case "+": return eval(e.left) + eval(e.right)
case "x": return eval(e.left) * eval(e.right)
default: throw Error(`unexpected operation: ${e.operation}`)
}
}
console.log(eval(parse(tokenize(input))))