Skip to content

Commit

Permalink
Merge pull request #41 from ChaosAlphard/calculator
Browse files Browse the repository at this point in the history
V键计算器底层重构,计算函数解耦,新增功能
  • Loading branch information
gaboolic committed May 11, 2024
2 parents 8bb0aca + 4d051ce commit cd774c9
Showing 1 changed file with 208 additions and 33 deletions.
241 changes: 208 additions & 33 deletions lua/calculator.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,225 @@ local function truncateFromStart(str, truncateStr)
return string.sub(str, string.len(truncateStr) + 1)
end

local function replaceMathFunc(express)
-- 不使用string.gsub(str, "SIN%(([0-9]+)%)", "math.sin(%1)"), 因为无法处理SIN(SIN(x))这种情况
-- 转大写避免重复替换, 空格是为了给第一个[^A-z]留空间
local str = " " .. string.upper(express)
str = string.gsub(str, "([^A-z])ASIN%(", "%1math.asin(") -- 反正弦
str = string.gsub(str, "([^A-z])SINH%(", "%1math.sinh(") -- 双曲正弦
str = string.gsub(str, "([^A-z])SIN%(", "%1math.sin(") -- 正弦
str = string.gsub(str, "([^A-z])ACOS%(", "%1math.acos(") -- 反余弦
str = string.gsub(str, "([^A-z])COSH%(", "%1math.cosh(") -- 双曲余弦
str = string.gsub(str, "([^A-z])COS%(", "%1math.cos(") -- 余弦
str = string.gsub(str, "([^A-z])ATAN2%(", "%1math.atan2(") -- atan2(y,x). 返回y/x的反正切(以弧度为单位), 但使用两个参数的符号来查找结果的象限.
str = string.gsub(str, "([^A-z])ATAN%(", "%1math.atan(") -- 反正切
str = string.gsub(str, "([^A-z])TANH%(", "%1math.tanh(") -- 双曲正切
str = string.gsub(str, "([^A-z])TAN%(", "%1math.tan(") -- 正切
str = string.gsub(str, "([^A-z])DEG%(", "%1math.deg(") -- 以度为单位返回角度x
str = string.gsub(str, "([^A-z])RAD%(", "%1math.rad(") -- 以弧度为单位返回角度x
-- str = string.gsub(str, "([^A-z])FREXP%(", "%1math.frexp(") -- frexp(x). 返回m,e 使得x = m*2^e
-- str = string.gsub(str, "([^A-z])LDEXP%(", "%1math.ldexp(") -- ldexp(m, e). 返回m*2^e
str = string.gsub(str, "([^A-z])EXP%(", "%1math.exp(") -- exp(x). e的x次幂
str = string.gsub(str, "([^A-z])LOG10%(", "%1math.log10(") -- 以10为底的对数
str = string.gsub(str, "([^A-z])LOG%(", "%1math.log(") -- 自然对数(e为底)
str = string.gsub(str, "([^A-z])RANDOM%(", "%1math.random(") -- random([m[, n]]), m - n 之间的随机数
str = string.gsub(str, "([^A-z])SQRT%(", "%1math.sqrt(") -- x的平方根. 等于x^0.5
str = string.gsub(str, "([^A-z])PI", "%1math.pi") -- π
-- 去除第一个字符(手动添加的空格)
return string.sub(str, 2)
-- 函数表
local calcPlugin = {
-- e, exp(1) = e^1 = e
e = math.exp(1),
-- π
pi = math.pi
}

-- random([m [,n ]]) 返回m-n之间的随机数, n为空则返回1-m之间, 都为空则返回0-1之间的小数
local function random(...)
return math.random(...)
end
-- 注册到函数表中
calcPlugin["rdm"] = random

-- 正弦
local function sin(x)
return math.sin(x)
end
calcPlugin["sin"] = sin

-- 双曲正弦
local function sinh(x)
return math.sinh(x)
end
calcPlugin["sinh"] = sinh

-- 反正弦
local function asin(x)
return math.asin(x)
end
calcPlugin["asin"] = asin

-- 余弦
local function cos(x)
return math.cos(x)
end
calcPlugin["cos"] = cos

-- 双曲余弦
local function cosh(x)
return math.cosh(x)
end
calcPlugin["cosh"] = cosh

-- 反余弦
local function acos(x)
return math.acos(x)
end
calcPlugin["acos"] = acos

-- 正切
local function tan(x)
return math.tan(x)
end
calcPlugin["tan"] = tan

-- 双曲正切
local function tanh(x)
return math.tanh(x)
end
calcPlugin["tanh"] = tanh

-- 反正切
local function atan(x)
return math.atan(x)
end
calcPlugin["atan"] = atan

-- 返回以弧度为单位的点(x,y)相对于x轴的逆时针角度。y是点的纵坐标,x是点的横坐标
-- 返回范围从−π到π (以弧度为单位),其中负角度表示向下旋转,正角度表示向上旋转
-- 它与传统的 math.atan(y/x) 函数相比,具有更好的数学定义,因为它能够正确处理边界情况(例如x=0)
local function atan2(y, x)
return math.atan2(y, x)
end
calcPlugin["atan2"] = atan2

-- 将角度从弧度转换为度 e.g. deg(π) = 180
local function deg(x)
return math.deg(x)
end
calcPlugin["deg"] = deg

-- 将角度从度转换为弧度 e.g. rad(180) = π
local function rad(x)
return math.rad(x)
end
calcPlugin["rad"] = rad

-- 返回两个值, 无法参与运算后续
-- 返回m,e 使得x = m*2^e
-- local function frexp(x)
-- return math.frexp(x)
-- end
-- calcPlugin["frexp"] = frexp

-- 返回 x*2^y
local function ldexp(x, y)
return math.ldexp(x, y)
end
calcPlugin["ldexp"] = ldexp

-- 返回 e^x
local function exp(x)
return math.exp(x)
end
calcPlugin["exp"] = exp

-- 返回x的平方根 e.g. sqrt(x) = x^0.5
local function sqrt(x)
return math.sqrt(x)
end
calcPlugin["sqrt"] = sqrt

-- x为底的对数, log(10, 100) = log(100) / log(10) = 2
local function log(x, y)
-- 不能为负数或0
if x <= 0 or y <= 0 then return nil end

return math.log(y) / math.log(x)
end
calcPlugin["log"] = log

-- 自然数e为底的对数
local function loge(x)
-- 不能为负数或0
if x <= 0 then return nil end

return math.log(x)
end
calcPlugin["loge"] = loge

-- 10为底的对数
local function log10(x)
-- 不能为负数或0
if x <= 0 then return nil end

return math.log10(x)
end
calcPlugin["log10"] = log10

-- 平均值
local function avg(...)
local data = {...}
local n = select("#", ...)
-- 样本数量不能为0
if n == 0 then return nil end

-- 计算总和
local sum = 0
for _, value in ipairs(data) do
sum = sum + value
end

return sum / n
end
calcPlugin["avg"] = avg

-- 方差
local function variance(...)
local data = {...}
local n = select("#", ...)
-- 样本数量不能为0
if n == 0 then return nil end

-- 计算均值
local sum = 0
for _, value in ipairs(data) do
sum = sum + value
end
local mean = sum / n

-- 计算方差
local sum_squared_diff = 0
for _, value in ipairs(data) do
sum_squared_diff = sum_squared_diff + (value - mean)^2
end

return sum_squared_diff / n
end
calcPlugin["var"] = variance

-- 阶乘
local function factorial(x)
-- 不能为负数
if x < 0 then return nil end
if x == 0 or x == 1 then return 1 end

local result = 1
for i = 1, x do
result = result * i
end

return result
end
calcPlugin["fact"] = factorial

-- 实现阶乘计算(!)
local function replaceToFactorial(str)
-- 替换[0-9]!字符为fact([0-9])以实现阶乘
return str:gsub("([0-9]+)!", "fact(%1)")
end

-- 简单计算器
-- BUG: 不支持小键盘 + - * /
function M.func(input, seg, env)
if not startsWith(input, M.prefix) then return end
-- 提取算式
local express = truncateFromStart(input, M.prefix)
-- 算式长度 < 2 直接终止(没有计算意义)
if (string.len(express) < 2) then return end
-- pcall()的原因需要控制一下 . 符号的位置
if (string.match(express, "[^0-9]%.")) then
yield(Candidate(input, seg.start, seg._end, express, "小数点不能在非数字字符后面"))
return
end
-- 现在不需要了
-- if (string.match(express, "[^0-9]%.")) then
-- yield(Candidate(input, seg.start, seg._end, express, "小数点不能在非数字字符后面"))
-- return
-- end
local code = replaceToFactorial(express)

local code = replaceMathFunc(express)
local success, result = pcall(load("return " .. code))
local success, result = pcall(load("return " .. code, "calculate", "t", calcPlugin))
if success then
yield(Candidate(input, seg.start, seg._end, result, ""))
yield(Candidate(input, seg.start, seg._end, express .. "=" .. result, ""))
Expand Down

0 comments on commit cd774c9

Please sign in to comment.