如何在Jsonnet中用函数式方式实现字符串型有符号整数的加减乘除?
在仅支持64位double的Jsonnet中实现64位有符号大整数基础运算
核心思路
由于Jsonnet仅支持64位double类型,超过2^53的整数无法精确存储,因此采用字符串+安全数字混合处理策略:
- 对输入字符串先判断是否可安全转为double(数值范围
[-2^53, 2^53]),能转则用数字运算,否则保留字符串模拟大整数运算 - 所有运算函数均为纯函数,避免状态变更,符合Jsonnet函数式特性
- 运算算法确保时间复杂度为O(m+n)级别(m、n为操作数的位数),避免低效的O(m*n)实现
基础工具函数
1. 安全数字判断与转换
// 判断字符串是否可安全转为double(无精度丢失) local isSafeNumberStr(s) = ( local num = std.parseJson(s); std.isNumber(num) && std.toString(num) == s ); // 将输入转为安全数字或保留字符串 local toSafeOperand(x) = ( if std.isString(x) && isSafeNumberStr(x) then std.parseJson(x) else x ); // 取反操作(处理数字和字符串) local negate(x) = ( if std.isNumber(x) then -x else if std.isString(x) then ( if std.startsWith(x, "-") then std.stringSlice(x, 1) else "-" + x ) else error "Unsupported operand type" );
2. 字符串大整数辅助函数
// 移除字符串前导零(处理正数) local trimLeadingZeros(s) = ( local idx = std.findSubstr(s, "0", 0); if idx == 0 then trimLeadingZeros(std.stringSlice(s, 1)) else s ); // 比较两个正数字符串的大小(返回1: a>b, 0:相等, -1:a<b) local comparePositiveStr(a, b) = ( if std.length(a) != std.length(b) then std.sign(std.length(a) - std.length(b)) else ( if a == b then 0 else std.sign(std.cmp(a, b)) ) );
核心运算函数实现
1. 加法(add)
支持数字、字符串混合运算,同号相加、异号转减法:
local add(a, b) = ( local opA = toSafeOperand(a); local opB = toSafeOperand(b); // 均为安全数字时直接运算 if std.isNumber(opA) && std.isNumber(opB) then opA + opB // 混合类型:转字符串处理 else if std.isNumber(opA) then add(std.toString(opA), opB) else if std.isNumber(opB) then add(opA, std.toString(opB)) // 双字符串处理:分正负情况 else ( local isNegA = std.startsWith(opA, "-"); local isNegB = std.startsWith(opB, "-"); local absA = if isNegA then std.stringSlice(opA, 1) else opA; local absB = if isNegB then std.stringSlice(opB, 1) else opB; // 同号相加 if isNegA == isNegB then ( local recAdd = (x, y, carry)::( if x == "" && y == "" then (if carry > 0 then std.toString(carry) else "") else ( local digitX = if x == "" then 0 else std.parseInt(std.stringSlice(x, -1)); local digitY = if y == "" then 0 else std.parseInt(std.stringSlice(y, -1)); local sum = digitX + digitY + carry; recAdd(std.stringSlice(x, 0, -1), std.stringSlice(y, 0, -1), std.floor(sum / 10)) + std.toString(sum % 10) ) ); local result = trimLeadingZeros(recAdd(absA, absB, 0)); if isNegA then "-" + result else result ) // 异号:转为减法(a + b = a - (-b)) else ( local cmp = comparePositiveStr(absA, absB); if cmp == 0 then "0" else if cmp > 0 then sub(opA, negate(opB)) else sub(opB, negate(opA)) ) ) );
2. 减法(sub)
基于加法实现,a - b = a + (-b):
local sub(a, b) = add(a, negate(b));
3. 乘法(mult)
采用Karatsuba算法(时间复杂度O(n^1.58),优于O(m*n)的逐位乘法),支持混合类型:
local mult(a, b) = ( local opA = toSafeOperand(a); local opB = toSafeOperand(b); if std.isNumber(opA) && std.isNumber(opB) then std.floor(opA * opB) // 确保整数结果 else if std.isNumber(opA) then mult(std.toString(opA), opB) else if std.isNumber(opB) then mult(opA, std.toString(opB)) else ( local isNegA = std.startsWith(opA, "-"); local isNegB = std.startsWith(opB, "-"); local absA = trimLeadingZeros(if isNegA then std.stringSlice(opA, 1) else opA); local absB = trimLeadingZeros(if isNegB then std.stringSlice(opB, 1) else opB); // 基础情况:其中一个为0或1 if absA == "0" || absB == "0" then "0" else if absA == "1" then absB else if absB == "1" then absA else ( local lenA = std.length(absA); local lenB = std.length(absB); local half = std.max(lenA, lenB) // 2; // 分割字符串为高位和低位 local splitStr(s, pos) = ( if std.length(s) <= pos then ("0", s) else (std.stringSlice(s, 0, -pos), std.stringSlice(s, -pos)) ); local (aHigh, aLow) = splitStr(absA, half); local (bHigh, bLow) = splitStr(absB, half); // Karatsuba核心步骤 local z0 = mult(aLow, bLow); local z1 = mult(add(aLow, aHigh), add(bLow, bHigh)); local z2 = mult(aHigh, bHigh); local z1Sub = sub(sub(z1, z2), z0); local shiftZ2 = z2 + std.repeat("0", 2*half); local shiftZ1 = z1Sub + std.repeat("0", half); local result = trimLeadingZeros(add(add(shiftZ2, shiftZ1), z0)); if isNegA != isNegB then "-" + result else result ) ) );
4. 除法(div)
实现整数除法(向零取整),时间复杂度O(m)(m为被除数位数):
local div(a, b) = ( local opA = toSafeOperand(a); local opB = toSafeOperand(b); if std.isNumber(opA) && std.isNumber(opB) then std.floor(opA / opB) // 向零取整 else if std.isNumber(opA) then div(std.toString(opA), opB) else if std.isNumber(opB) then div(opA, std.toString(opB)) else ( local isNegA = std.startsWith(opA, "-"); local isNegB = std.startsWith(opB, "-"); local absA = trimLeadingZeros(if isNegA then std.stringSlice(opA, 1) else opA); local absB = trimLeadingZeros(if isNegB then std.stringSlice(opB, 1) else opB); if absB == "0" then error "Division by zero" else if comparePositiveStr(absA, absB) < 0 then "0" else ( local recDiv = (dividend, divisor, quotient)::( local cmp = comparePositiveStr(dividend, divisor); if cmp < 0 then quotient else ( // 快速计算除数左移的位数(避免逐次减) local shiftDivisor = (d, count)::( if comparePositiveStr(add(d, d), dividend) <= 0 then shiftDivisor(add(d, d), count + 1) else (d, count) ); local (shiftedD, shiftCount) = shiftDivisor(divisor, 0); recDiv(sub(dividend, shiftedD), divisor, add(quotient, "1" + std.repeat("0", shiftCount))) ) ); local result = trimLeadingZeros(recDiv(absA, absB, "0")); if isNegA != isNegB then "-" + result else result ) ) );
5. 模运算(modulo)
基于除法实现,遵循向零取整后的模规则:
local modulo(a, b) = ( local opA = toSafeOperand(a); local opB = toSafeOperand(b); if std.isNumber(opA) && std.isNumber(opB) then opA - (std.floor(opA / opB) * opB) else sub(opA, mult(div(opA, opB), opB)) );
比较操作实现
local eq(a, b) = ( local opA = toSafeOperand(a); local opB = toSafeOperand(b); if std.isNumber(opA) && std.isNumber(opB) then opA == opB else if std.isString(opA) && std.isString(opB) then opA == opB else eq(std.toString(opA), std.toString(opB)) ); local gt(a, b) = ( local opA = toSafeOperand(a); local opB = toSafeOperand(b); if std.isNumber(opA) && std.isNumber(opB) then opA > opB else if std.isString(opA) && std.isString(opB) then ( local isNegA = std.startsWith(opA, "-"); local isNegB = std.startsWith(opB, "-"); if isNegA != isNegB then !isNegA else ( local cmp = comparePositiveStr( if isNegA then std.stringSlice(opA, 1) else opA, if isNegB then std.stringSlice(opB, 1) else opB ); if isNegA then cmp < 0 else cmp > 0 ) ) else gt(std.toString(opA), std.toString(opB)) ); // 其余比较操作(lt, ge, le)可基于gt/eq实现 local lt(a, b) = !ge(a, b); local ge(a, b) = gt(a, b) || eq(a, b); local le(a, b) = lt(a, b) || eq(a, b);
使用示例
// 安全数字运算 add(123, 456) // 输出579 mult(999, 999) // 输出998001 // 大整数运算 add("9223372036854775807", "1") // 输出"9223372036854775808"(超过2^53,用字符串存储) div("1234567890123456789", "123") // 输出"10037137318076884" modulo("-12345", "7") // 输出"-4"(向零取整规则)
内容的提问来源于stack exchange,提问作者Sebastien Diot
相关产品推荐
相关产品推荐

