我可以使用 random 从某个范围中获取随机值。如何制作既可以执行相反操作又可以访问数学模块的数学评估器?

问题描述 投票:0回答:1

我想构建一个供私人使用的数学评估器(在进行评估时没有安全限制),它可以为我提供数学陈述的可能结果。假设这里的 random() 指的是 range() 并且内置的 python range 不适用于浮点值。因此,虽然它应该默认执行正常的评估,但返回值应该位于列表或集合中。 所以,它应该评估基础数学。

"3" -> [3]
"round(419.9)" -> [420]
"round(3.14, 1)" -> [3.1]

它还应该列出所有可能结果的评估。

"[1,2,3]" -> [1,2,3]

它还应该递归地评估多个列表

"[2,1] - 1" -> [1,0]
"[2,1] - [0,1]" -> [2, 1, 0] (with internal evalulation being combination of two list  recursive evaulation of expression "[2,1] - 0" and "[2,1] - 1")
"[2,1] * [1,2]" -> [4,2,1]

然后只需将随机函数编码为范围并返回列表,然后评估应该给出答案。

"random(3)" -> [0, 1, 2]
"random(3,4)" -> [3]
"round(random(0.1, 0.5), 1)" -> [0.1, 0.2, 0.3, 0.4]

最后,它还应该具有来自 stdlib 数学模块的变量和函数,例如

"log(e)" -> [1] # math.log(math.e)
"tau/pi" -> [2.0] # math.tau/math.pi

虽然这个测试用例超出了讨论范围,但如果可以编写这样的代码,那就太酷了。我确实看到一些评估者很好地处理了这段代码,而当我尝试使用 sympy、ast.eval 和正常 eval 时,出现了重大错误。

"2(3)" -> [6]
"2(3(3))" -> [18]

我设法编写了通过一些测试用例的代码,但很难使其全部正确。

import random

def evaluate_expression(expression):
    # Define safe functions and constants to use within eval
    def custom_function(expression, roundval=0):
        if isinstance(expression, list):
            return [round(item, roundval) for item in expression]
        else:
            return round(expression, roundval)
    safe_dict = {
        'random': lambda *args: list(range(int(args[0]), int(args[1]) + 1)),
        'round': custom_function,
    }
    
    # Add some common mathematical constants
    safe_dict.update({
        'pi': 3.141592653589793,
        'e': 2.718281828459045
    })

    # Try to evaluate the expression
    try:
        result = eval(expression, {"__builtins__": None}, safe_dict)
        
        # If the result is a single number, return it in a list
        if isinstance(result, (int, float)):
            return [result]
        
        # If the result is a list, return it as is
        elif isinstance(result, list):
            return result
        
        else:
            raise ValueError("Unsupported result type")

    except (SyntaxError, NameError, TypeError, ValueError) as e:
        return str(e)

这些是测试用例

# Test cases
assert evaluate_expression("3") == [3]
assert evaluate_expression("round(419.9)") == [420] 
assert evaluate_expression("round(3.14, 1)") == [3.1]
assert evaluate_expression("[1,2,3]") == [1,2,3]
assert evaluate_expression("[2,1] - 1") == [1,0]
assert evaluate_expression("[2,1] - [0,1]") == [2, 1, 0]
assert evaluate_expression("[2,1] * [1,2]") == [4,2,1]
assert evaluate_expression("random(3)") == [0,1,2]
assert evaluate_expression("random(3, 4)") == [3]
assert evaluate_expression("round(random(0.1, 0.5), 1)") == [0.1, 0.2, 0.3, 0.4]
assert evaluate_expression("log(e)") == [1]
assert evaluate_expression("tau/pi") == [2.0] 
#out of scope
assert evaluate_expression("2(3)") == [6]
assert evaluate_expression("2(3(3))") == [18]

我认为如果这个测试用例通过,像“random(1,9) * random(1,9)”这样的东西不应该出错并且应该产生“[1,2,3,4,5,6,7, 8]*[1,2,3,4,5,6,7,8]" 然后评估应该生成一个大列表。作为旁注,我还设法生成了一个自定义随机范围生成器。 (当输入是(0.01,0.05)时,我不太关心列表中的0.5,但如果可以改进这个功能,那就太酷了)

def custom_random(start, end):
    ten = 10**len(str(start).split('.')[-1])
    if isinstance(start, int):
        mul = 1
    elif isinstance(start, float):
        if len(str(start)) == 3:
            mul = 0.1
        elif len(str(start)) == 4:
            mul = 0.01
    if isinstance(start, int) and isinstance(end, int):
        return list(range(start, end))
    elif isinstance(start, float) and isinstance(end, float):
        return [round(i * mul, len(str(start).split('.')[1])) for i in range(int(start * ten), int(end * ten) + 1)]
    else:
        raise TypeError("Unsupported input type")

print(custom_random(1, 5))
print(custom_random(3, 4))
print(custom_random(10, 50))
print(custom_random(0.1, 0.5)) #prints also 0.5 but it should not print 0.5, but only upto 0.4? but not big of bug anyways
print(custom_random(0.01, 0.05)) #prints also 0.05 but it should not print 0.05, but only upto 0.4? but not big of bug anyways
print(custom_random(0.05, 0.09))
python math range eval
1个回答
0
投票

以下是

custom_random
函数的更简洁版本:

def custom_random(start, end):
    def round_to_precision(number, precision):
        return round(number * 10 ** precision) / 10 ** precision

    step = 1 if isinstance(start, int) else 10 ** -len(str(start).split('.')[-1])

    if isinstance(start, (int, float)) and isinstance(end, (int, float)):
        result = [round_to_precision(i, len(str(start).split('.')[-1])) for i in 
                  range(int(start * 10 ** len(str(start).split('.')[-1])), int(end * 10 ** len(str(start).split('.')[-1])) + 1)]
        if result[-1] > end:
            result.pop()
        return result
    else:
        raise TypeError("Unsupported input type")

此版本保留了

custom_random
函数的功能,同时使用辅助函数
round_to_precision
来实现更清晰的代码。

© www.soinside.com 2019 - 2024. All rights reserved.