Bir dizedeki matematiksel bir ifadeyi sayısal değerini elde etmek için '1+1'
çağırmadan nasıl ayrıştırabilir ve değerlendirebilirim (örneğin ) eval(string)
?
Bu örnekle, işlevin kabul etmesini '1+1'
ve geri dönmesini istiyorum 2
.
Yanıtlar:
Aşağıdakiler gibi şeyler yapmanıza izin veren JavaScript İfade Değerlendiricisi kitaplığını kullanabilirsiniz :
Parser.evaluate("2 ^ x", { x: 3 });
Veya aşağıdaki gibi şeylere izin veren mathjs :
math.eval('sin(45 deg) ^ 2');
Projelerimden biri için mathjs seçmeye başladım.
+ Veya - işlemini kolayca yapabilirsiniz:
function addbits(s) {
var total = 0,
s = s.match(/[+\-]*(\.\d+|\d+(\.\d+)?)/g) || [];
while (s.length) {
total += parseFloat(s.shift());
}
return total;
}
var string = '1+23+4+5-30';
console.log(
addbits(string)
)
Daha karmaşık matematik, değerlendirmeyi daha çekici ve kesinlikle yazmayı daha kolay hale getirir.
Birinin bu dizeyi çözümlemesi gerekiyor. Eğer yorumlayıcı (aracılığıyla eval
) değilse, o zaman sizin olmanız, sayıları, operatörleri ve matematiksel bir ifadede desteklemek istediğiniz her şeyi ayıklamak için bir ayrıştırma rutini yazmanız gerekecektir.
Yani hayır, olmadan (basit) bir yol yoktur eval
. (Giriş Sen ayrıştırma kontrol bir kaynaktan değil çünkü) Güvenlik konusunda endişe ediyorsanız, belki geçirmeden önce (beyaz liste regex filtresi aracılığıyla) girişin formatını kontrol edebilirsiniz eval
?
Daha kısa bir düzenli ifade kullanarak ve operatörler arasında boşluk bırakarak @kennebec'in mükemmel cevabına bir alternatif
function addbits(s) {
var total = 0;
s = s.replace(/\s/g, '').match(/[+\-]?([0-9\.\s]+)/g) || [];
while(s.length) total += parseFloat(s.shift());
return total;
}
Gibi kullan
addbits('5 + 30 - 25.1 + 11');
Güncelleme
İşte daha optimize bir sürüm
function addbits(s) {
return (s.replace(/\s/g, '').match(/[+\-]?([0-9\.]+)/g) || [])
.reduce(function(sum, value) {
return parseFloat(sum) + parseFloat(value);
});
}
Oluşturduğum BigEval aynı amaçla.
İfadeleri çözerken, Eval()
%, ^, &, ** (güç) ve! Gibi operatörlerle tam olarak aynı performansı gösterir ve bunları destekler. (faktöryel). İfadenin içinde fonksiyonları ve sabitleri (veya değişkenleri) kullanmanıza da izin verilir. İfade, JavaScript dahil programlama dillerinde yaygın olan PEMDAS sırasına göre çözülür .
var Obj = new BigEval();
var result = Obj.exec("5! + 6.6e3 * (PI + E)"); // 38795.17158152233
var result2 = Obj.exec("sin(45 * deg)**2 + cos(pi / 4)**2"); // 1
var result3 = Obj.exec("0 & -7 ^ -7 - 0%1 + 6%2"); //-7
Rastgele hassasiyetle sayılarla uğraşmanız durumunda, aritmetik için bu Büyük Sayı kitaplıklarını kullanmak da yapılabilir.
Matematiksel ifadeleri değerlendirmek için JavaScript kitaplıkları aramaya gittim ve şu iki umut verici adayı buldum:
JavaScript İfade Değerlendiricisi : Daha küçük ve umarız daha hafiftir. Cebirsel ifadelere, ikamelere ve bir dizi fonksiyona izin verir.
mathjs : Karmaşık sayılara, matrislere ve birimlere de izin verir. Hem tarayıcı içi JavaScript hem de Node.js tarafından kullanılmak üzere oluşturulmuştur.
Bunu yakın zamanda C # 'da ( Eval()
bizim için hayır ...) İfadeyi Reverse Polish Notation'da (bu kolay bit) değerlendirerek yaptım . İşin zor kısmı aslında dizgiyi ayrıştırıp Reverse Polish Notation'a çevirmektir. Wikipedia ve sözde kodda harika bir örnek olduğu için Shunting Yard algoritmasını kullandım . Her ikisini de uygulamayı gerçekten kolay buldum ve henüz bir çözüm bulamadıysanız veya alternatifler arıyorsanız bunu tavsiye ederim.
Bu, şu anda bu sorunu çözmek için bir araya getirdiğim küçük bir işlev - dizgeyi bir seferde bir karakter analiz ederek ifadeyi oluşturur (aslında oldukça hızlıdır). Bu, herhangi bir matematiksel ifadeyi alır (yalnızca +, -, *, / operatörlerle sınırlıdır) ve sonucu döndürür. Negatif değerleri ve sınırsız sayıda işlemi de halledebilir.
Yapılması gereken tek şey, + & --'dan önce * & / hesapladığından emin olmaktır. Bu işlevi daha sonra ekleyeceğim, ancak şimdilik ihtiyacım olan şeyi yapıyor ...
/**
* Evaluate a mathematical expression (as a string) and return the result
* @param {String} expr A mathematical expression
* @returns {Decimal} Result of the mathematical expression
* @example
* // Returns -81.4600
* expr("10.04+9.5-1+-100");
*/
function expr (expr) {
var chars = expr.split("");
var n = [], op = [], index = 0, oplast = true;
n[index] = "";
// Parse the expression
for (var c = 0; c < chars.length; c++) {
if (isNaN(parseInt(chars[c])) && chars[c] !== "." && !oplast) {
op[index] = chars[c];
index++;
n[index] = "";
oplast = true;
} else {
n[index] += chars[c];
oplast = false;
}
}
// Calculate the expression
expr = parseFloat(n[0]);
for (var o = 0; o < op.length; o++) {
var num = parseFloat(n[o + 1]);
switch (op[o]) {
case "+":
expr = expr + num;
break;
case "-":
expr = expr - num;
break;
case "*":
expr = expr * num;
break;
case "/":
expr = expr / num;
break;
}
}
return expr;
}
İle sade ve zarif Function()
function parse(str) {
return Function(`'use strict'; return (${str})`)()
}
parse("1+2+3");
) -----
() `bu köşeli parantez nedir?
parse('process.exit()')
.
Dizenin geçersiz karakter içerip içermediğini kontrol etmek için bir for döngüsü kullanabilir ve daha sonra, hesaplamanın eval("2++")
yapacağı gibi bir hata verip vermediğini kontrol etmek için bir try ... catch kullanabilirsiniz .
function evaluateMath(str) {
for (var i = 0; i < str.length; i++) {
if (isNaN(str[i]) && !['+', '-', '/', '*', '%', '**'].includes(str[i])) {
return NaN;
}
}
try {
return eval(str)
} catch (e) {
if (e.name !== 'SyntaxError') throw e
return NaN;
}
}
console.log(evaluateMath('2 + 6'))
veya bir işlev yerine, Math.eval
Math.eval = function(str) {
for (var i = 0; i < str.length; i++) {
if (isNaN(str[i]) && !['+', '-', '/', '*', '%', '**'].includes(str[i])) {
return NaN;
}
}
try {
return eval(str)
} catch (e) {
if (e.name !== 'SyntaxError') throw e
return NaN;
}
}
console.log(Math.eval('2 + 6'))
Sonunda pozitif ve negatif tam sayıları toplamak için çalışan bu çözüme gittim (ve normal ifadede küçük bir değişiklik ile ondalık sayılar için de işe yarayacak):
function sum(string) {
return (string.match(/^(-?\d+)(\+-?\d+)*$/)) ? string.split('+').stringSum() : NaN;
}
Array.prototype.stringSum = function() {
var sum = 0;
for(var k=0, kl=this.length;k<kl;k++)
{
sum += +this[k];
}
return sum;
}
Eval () 'den daha hızlı mı emin değilim, ancak işlemi birçok kez gerçekleştirmem gerektiğinden, bu betiği çalıştırırken çok sayıda javascript derleyicisi oluşturmaktan çok daha rahatım
return
bir ifadenin içine kullanılamaz, sum("+1")
döner NaN .
deneyin nerdamer
var result = nerdamer('12+2+PI').evaluate();
document.getElementById('text').innerHTML = result.text();
<script src="http://nerdamer.com/js/nerdamer.core.js"></script>
<div id="text"></div>
Buna inanıyorum parseInt
ve ES6 bu durumda yardımcı olabilir
let func = (str) => {
let arr = str.split("");
return `${Number(arr[0]) + parseInt(arr[1] + Number(arr[2]))}`
};
console.log(func("1+1"));
Buradaki en önemli şey parseInt
, numarayı operatörle ayrıştırmasıdır. Kod, ilgili ihtiyaçlara göre değiştirilebilir.
AutoCalculator'ı deneyin https://github.com/JavscriptLab/autocalculate değerini ve Çıkışı Hesapla Seçici ifadelerini kullanarak
Çıktı girdiniz için data-ac = "(# firstinput + # secondinput)" gibi bir özellik eklemeniz yeterlidir.
Herhangi bir başlatmaya gerek yok, sadece data-ac özelliğini ekleyin. Dinamik olarak eklenen öğeleri otomatik olarak bulacaktır
Veya Çıktı ile 'Rs' ekleyin, sadece küme parantez içine ekleyin data-ac = "{Rs} (# firstinput + # secondinput)"
const operatorToFunction = {
"+": (num1, num2) => +num1 + +num2,
"-": (num1, num2) => +num1 - +num2,
"*": (num1, num2) => +num1 * +num2,
"/": (num1, num2) => +num1 / +num2
}
const findOperator = (str) => {
const [operator] = str.split("").filter((ch) => ["+", "-", "*", "/"].includes(ch))
return operator;
}
const executeOperation = (str) => {
const operationStr = str.replace(/[ ]/g, "");
const operator = findOperator(operationStr);
const [num1, num2] = operationStr.split(operator)
return operatorToFunction[operator](num1, num2);
};
const addition = executeOperation('1 + 1'); // ans is 2
const subtraction = executeOperation('4 - 1'); // ans is 3
const multiplication = executeOperation('2 * 5'); // ans is 10
const division = executeOperation('16 / 4'); // ans is 4
num
1 ile çarpılır ?
Burada jMichael'inkine benzer, karakter karakter ifade boyunca döngü yapan ve aşamalı olarak sol / operatör / sağı izleyen algoritmik bir çözüm var. İşlev, bir operatör karakteri bulduğu her dönüşten sonra sonucu toplar. Bu sürüm yalnızca '+' ve '-' operatörlerini destekler ancak diğer operatörlerle genişletilmek üzere yazılmıştır. Not: İfadenin pozitif bir float ile başladığını varsaydığımız için döngü yapmadan önce 'currOp'u' + 'olarak ayarlıyoruz. Aslında, genel olarak girdinin bir hesap makinesinden gelene benzer olduğunu varsayıyorum.
function calculate(exp) {
const opMap = {
'+': (a, b) => { return parseFloat(a) + parseFloat(b) },
'-': (a, b) => { return parseFloat(a) - parseFloat(b) },
};
const opList = Object.keys(opMap);
let acc = 0;
let next = '';
let currOp = '+';
for (let char of exp) {
if (opList.includes(char)) {
acc = opMap[currOp](acc, next);
currOp = char;
next = '';
} else {
next += char;
}
}
return currOp === '+' ? acc + parseFloat(next) : acc - parseFloat(next);
}
(Function("return 1+1;"))()
.