`
NeuronR
  • 浏览: 59166 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

[开发过程]运算节点的测试

阅读更多

准备

在语法分析完结篇中我给出了一个压缩包, 里面包含了词法和语法分析的各个方面. 其中有一个叫做 jerry-ui.h 的文件中定义了一些 ui 以及 io 相关函数. 作为指令生成测试这样局部性质的测试, 也需要实现这些接口以免发生编译错误, 但没有必要使用 jerry-ui.c 中实现, 因此先在名为 test-ui.c 的文件中给出伪实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "jerry-ui.h"

FILE* treeout;
struct Stack analyserStack;
struct Token* firstToken(void) { return NULL; }
struct Token* nextToken(void) { return NULL; }
void eofToken(void) {}
int isFailed(void) { return 0; }
unsigned int getLineNumber(void) { return 0; }
void lexicalError(struct Token* token) {}
void syntaxError(ErrMsg e, struct Token* t) {}
void semanticErr(ErrMsg err, int line) {}
void symError(ErrMsg err, struct VariableNode* var) {}
int nextChar(void) { return 0; }

而在测试中, 还需要给出这些实现

struct Stack loopStack;

struct AbstractInstruction* loopOutlet(void)
{
    return (struct AbstractInstruction*)(loopStack.peek(&loopStack));
}

void enterLoop(void* outlet)
{
    loopStack.push(&loopStack, outlet);
}

void leaveLoop(void)
{
    loopStack.pop(&loopStack);
}

名称包含 loop 的是循环栈及对应的数据访问接口, 虽然这一次并不测试循环语句, 但是先写上备用; analyserStack 是一个没有用到的栈, 定义在这里. 将这些它们放入新文件 test-common.c 中, 并且还得弄一个 test-common.h 存放对应的声明. 此外, 好要写一些小工具

 

// 判定两个指令是相同的, 并且结束后释放它们
void assertInsEqual_Del(void* i0, void* i1)
{
    assert(((struct AbstractInstruction*)i0)->code ==
           ((struct AbstractInstruction*)i1)->code);
    if (isIntParamIns(((struct AbstractInstruction*)i0)->code)) {
        assert(((struct IntParamInstruction*)i0)->param ==
               ((struct IntParamInstruction*)i1)->param);
    } else if (isRealParamIns(((struct AbstractInstruction*)i0)->code)) {
        assert(doubleEqual(((struct IntParamInstruction*)i0)->param,
                           ((struct IntParamInstruction*)i1)->param));
    }
    revert(i0);
    revert(i1);
}

// 双精度浮点判同
int doubleEqual(double a, double b)
{
    return 1e-9 > fabs(a - b);
} 

也放在这一块.

接下来是数据预备. 这次进行测试的是

 

IntegerNode

RealNode

UnaryOpNode

BinaryOpNode

VariableNode

这些节点类型的指令生成, 下面是我准备的数据, 你也可以准备你自己的数据进行测试

#define NR_TEST_CASE (4)
#define MAX_ARRAY_SIZE (5)

// 数组维度
int DIM[NR_TEST_CASE][MAX_ARRAY_SIZE] = {
    { 2, 3, 4, 5, 0 },
    { 2, 4, 0 },
    { 0 },
    { 2, 3, 4, 0 }
};

// 变量, 包括从标识符到符号表信息等一系列域
struct {
    char* ident;
    AcceptType type;
    int nrDim;
    int offsets[MAX_ARRAY_SIZE];
    int size;
    int base;
} data[NR_TEST_CASE] = {
    { "testee", INTEGER },
    { "tom", INTEGER },
    { "jerry", REAL },
    { "mANDg", REAL }
};

// 纯标识符
char* ident = "param";
char* ident1 = "param1";

因为测试不依赖任何框架 (啊啊, 我是个无聊喜欢做轮子的人~), 因此有些测前准备和测后清场工作也得自己来弄

 

void prepare(void)
{
    initialSymTabManager(); // 初始化符号表
    int i, j;
    struct VariableNode* var;
    for (i = 0; i < NR_TEST_CASE; ++i) {
        var = newVariableNode(data[i].ident);
        for (j = 0; 0 != DIM[i][j]; ++j) {
            var->dimInfor->enqueue(var->dimInfor, newIntegerNode(DIM[i][j])); // 添加数组信息
        }
        declare(var, data[i].type); // 在符号表中注册所有的变量
        var->delNode(var);
    }
}

void clear(void)
{
    finalizeSymTabManager(); // 清理符号表
}

其实还有一项初始化工作, 那就是根据数组维度大小计算每一维的偏移, 当然这计算并不是被测试的, 而是因为这些需要在变量节点测试中用到, 这个放在 main 中进行好了, 因为只需要进行一次.

 

 

IntegerNode 及 RealNode

 

 

先来两个简单的来阐述我的测试思路

void testIntegerNode(void)
{
    struct IntegerNode* i; // 节点
    struct List* ins; // 节点产生的指令列表
    prepare(); // 准备

    i = newIntegerNode(14); // 构造一个节点
    ins = i->createInstruction(i);
    assert(1 == ins->count(ins)); // 测试1: 验证指令数量
    assert(CONST_INT == ((struct IntParamInstruction*)
                         (ins->elementAt(ins, 0)))->code); // 测试2: 验证指令码
    assert(14 == ((struct IntParamInstruction*)
                  (ins->elementAt(ins, 0)))->param); // 测试3: 验证指令参数
    revert(ins->elementAt(ins, 0)); // 销毁指令
    i->delNode(i); // 析构节点
    ins->finalize(ins); // 析构列表
    clear(); // 测试结束, 清理现场
    showAlloc; // 看看有没有内存泄漏
    puts("    Integer node test done.\n"); // 啦啦啦, 结束
}

为了讲述方便, 这里只给一个案例. 因为 IntegerNode (以及 RealNode) 是不依赖任何其它节点的, 所以测它们的指令只需要验证这些即可. 下面是 RealNode 的, 几乎一样.

void testRealNode(void)
{
    struct RealNode* r;
    struct List* ins;
    prepare();

    r = newRealNode(3.14159);
    ins = r->createInstruction(r);
    assert(1 == ins->count(ins));
    assert(CONST_REAL == ((struct RealParamInstruction*)
                          (ins->elementAt(ins, 0)))->code);
    assert(doubleEqual(3.14159, ((struct RealParamInstruction*)
                                 (ins->elementAt(ins, 0)))->param));
    revert(ins->elementAt(ins, 0));
    r->delNode(r);
    ins->finalize(ins);
    clear();
    showAlloc;
    puts("    Real node test done.\n");
}

变量

先假定我们需要的那一项初始化 (就是之前提到要放在 main 中的那个) 已经完成, 那么 data 数组中每一个元素的 baseoffsets 数组都存放着该变量的基地址和每一维偏移.

测试包括这么几个方面

void testVarNode(void)
{
    struct VariableNode* var;
    struct List* ins;
    int i, j, paramAddr;
    struct VariableNode* param;
    prepare();

    // 1: addressOf
    for (i = 0; i < NR_TEST_CASE; ++i) {
        var = newVariableNode(data[i].ident);
        for (j = 0; 0 != DIM[i][j]; ++j) {
            var->dimInfor->enqueue(var->dimInfor, newIntegerNode(0)); // 每一维都是静态给定的, 而且是 0
        }
        ins = var->addressOf(var);
        // 那么加载变量地址只需一个指令
        assert(1 == ins->count(ins));
        // 指令就是加载其数组基地址
        assert(CONST_INT == ((struct IntParamInstruction*)
                                         (ins->elementAt(ins, 0)))->code);
        assert(data[i].base == ((struct IntParamInstruction*)
                                (ins->elementAt(ins, 0)))->param);
        revert(ins->elementAt(ins, 0));
        ins->finalize(ins);
        var->delNode(var);
    }

    // 2: addressOf 每一维是变量
    param = newVariableNode(ident);
    paramAddr = declare(param, INTEGER); // 注册变量
    assert(0 != paramAddr);
    var = newVariableNode(data[0].ident);
    for (j = 0; 0 != DIM[0][j]; ++j) {
        var->dimInfor->enqueue(var->dimInfor, newVariableNode(ident)); // 灌入数组
    }
    ins = var->addressOf(var);
    assert(1 + j * 5 == ins->count(ins)); // 指令数量增加很多个~
    // 第一条指令仍旧是加载基地址
    assert(CONST_INT == ((struct IntParamInstruction*)
                                         (ins->elementAt(ins, 0)))->code);
    assert(data[0].base == ((struct IntParamInstruction*)
                                         (ins->elementAt(ins, 0)))->param);

    for (i = 0; i < j; ++i) {
        // 加载偏移量的指令
        assert(CONST_INT == ((struct IntParamInstruction*)
                                       (ins->elementAt(ins, 1 + i * 5)))->code);
        assert(data[0].offsets[i] == ((struct IntParamInstruction*)
                                      (ins->elementAt(ins, 1 + i * 5)))->param);
        // 加载变量 (ident) 的地址
        assert(CONST_INT == ((struct IntParamInstruction*)
                                       (ins->elementAt(ins, 2 + i * 5)))->code);
        assert(paramAddr == ((struct IntParamInstruction*)
                                      (ins->elementAt(ins, 2 + i * 5)))->param);
        // 加载变量 (ident) 的值
        assert(LOAD_INT == ((struct NoParamInstruction*)
                                       (ins->elementAt(ins, 3 + i * 5)))->code);
        // 计算 (ident) 与偏移量之积
        assert(INT_MULTIPLY == ((struct NoParamInstruction*)
                                       (ins->elementAt(ins, 4 + i * 5)))->code);
        // 累加地址
        assert(INT_PLUS == ((struct NoParamInstruction*)
                                       (ins->elementAt(ins, 5 + i * 5)))->code);
    }

    while (0 != ins->count(ins)) {
        revert(ins->popElementAt(ins, 0)); // 销毁指令啦~
    }
    ins->finalize(ins);
    var->delNode(var);
    param->delNode(param);
    clear();

    prepare(); // 重置符号表 (主要目的是干掉 ident), 准备 3: 每一维是实型变量
    param = newVariableNode(ident);
    paramAddr = declare(param, REAL);
    var = newVariableNode(data[1].ident);
    for (j = 0; 0 != DIM[1][j]; ++j) {
        var->dimInfor->enqueue(var->dimInfor, newVariableNode(ident));
    }
    ins = var->addressOf(var);
    assert(1 + j * 6 == ins->count(ins)); // 每一维多 1 指令
    assert(CONST_INT == ((struct IntParamInstruction*)
                                         (ins->elementAt(ins, 0)))->code);
    assert(data[1].base == ((struct IntParamInstruction*)
                                         (ins->elementAt(ins, 0)))->param);

    for (i = 0; i < j; ++i) {
        assert(CONST_INT == ((struct IntParamInstruction*)
                                       (ins->elementAt(ins, 1 + i * 6)))->code);
        assert(data[1].offsets[i] == ((struct IntParamInstruction*)
                                      (ins->elementAt(ins, 1 + i * 6)))->param);

        assert(CONST_INT == ((struct IntParamInstruction*)
                                       (ins->elementAt(ins, 2 + i * 6)))->code);
        assert(paramAddr == ((struct IntParamInstruction*)
                                      (ins->elementAt(ins, 2 + i * 6)))->param);

        assert(LOAD_REAL == ((struct NoParamInstruction*)
                                       (ins->elementAt(ins, 3 + i * 6)))->code);
        // 多的这条指令就是类型转换指令
        assert(REAL_2_INT == ((struct NoParamInstruction*)
                                       (ins->elementAt(ins, 4 + i * 6)))->code);

        assert(INT_MULTIPLY == ((struct NoParamInstruction*)
                                       (ins->elementAt(ins, 5 + i * 6)))->code);

        assert(INT_PLUS == ((struct NoParamInstruction*)
                                       (ins->elementAt(ins, 6 + i * 6)))->code);
    }

    while (0 != ins->count(ins)) {
        revert(ins->popElementAt(ins, 0));
    }
    ins->finalize(ins);
    var->delNode(var);
    param->delNode(param);
    clear();
    
    showAlloc;
    puts("    Variable node test done.\n"); // 结束 :-)
}

单目运算

运算测试需要涵盖两个方面, 一是一般情况, 二是当这个节点实际上是一个编译时可以确定值的节点. 对于编译时可以确定值的节点的测试, 这里给出一个示例

    struct IntegerNode* i;
    struct UnaryOperationNode* u;
    struct List* ins;
    struct List* subIns;
    i = newIntegerNode(0x7fffffff); // 常量
    u = newUnaryOperationNode(MINUS, (struct AbstractValueNode*)i);
    ins = u->createInstruction(u); // 这是单目运算节点指令
    assert(1 == ins->count(ins)); // 常量, 所以个数是 1
    assert(CONST_INT == ((struct IntParamInstruction*)
                         (ins->elementAt(ins, 0)))->code);
    assert(-0x7fffffff == ((struct IntParamInstruction*)
                          (ins->elementAt(ins, 0)))->param); // 是负数哦
    revert(ins->elementAt(ins, 0));
    u->delNode(u);
    ins->finalize(ins); // 结束

此外还可以弄些很挫的, 比如级联式单目运算

    struct UnaryOperationNode* cascade;
    i = newIntegerNode(0x7fffffff);
    cascade = newUnaryOperationNode(NOT, (struct AbstractValueNode*)i);
    u = newUnaryOperationNode(NOT, (struct AbstractValueNode*)cascade); // 两个套在一起了
    ins = u->createInstruction(u);
    assert(1 == ins->count(ins));
    assert(CONST_INT == ((struct IntParamInstruction*)
                         (ins->elementAt(ins, 0)))->code);
    assert((!0) == ((struct IntParamInstruction*)
                          (ins->elementAt(ins, 0)))->param);
    revert(ins->elementAt(ins, 0));
    u->delNode(u);
    ins->finalize(ins);

对于非常量的运算节点, 验证其下一级节点产生的指令是否正确的工作可以丢给 void assertInsEqual_Del(void* a, void* b) 去完成, 而不必写在运算节点内部. 比如对于逻辑非运算, 已知其指令生成是这样的

> 产生子节点指令

> 在栈顶放入整数 0

> 比较栈顶和次栈顶

那么测试可以写成这样

    struct VariableNode* var;
    struct UnaryOperationNode* u;
    struct List* ins,* subIns;

    prepare();
    var = newVariableNode(ident);
    declare(var, INTEGER);
    u = newUnaryOperationNode(NOT,
                      (struct AbstractValueNode*)newVariableNode(ident));
    ins = u->createInstruction(u);
    subIns = var->createInstruction(var);
    // 指令数量验证: 运算比子节点应该多两条指令
    assert(subIns->count(subIns) + 2 == ins->count(ins));

    // 前面的指令应该都是一样的
    while (0 != subIns->count(subIns)) {
        assertInsEqual_Del(ins->popElementAt(ins, 0),
                           subIns->popElementAt(subIns, 0));
    }
    // 多出的第一条指令: 载入 0
    assert(CONST_INT ==
                ((struct IntParamInstruction*)(ins->elementAt(ins, 0)))->code);
    assert(0 == ((struct IntParamInstruction*)(ins->elementAt(ins, 0)))->param);
    revert(ins->popElementAt(ins, 0));
    // 第二条指令: 比较栈顶和次栈顶
    assert(INT_EQ ==
                ((struct NoParamInstruction*)(ins->elementAt(ins, 0)))->code);
    revert(ins->popElementAt(ins, 0));

    u->delNode(u);
    var->delNode(var);
    ins->finalize(ins);
    subIns->finalize(subIns);
    clear();

双目运算

双目运算与单目运算的测试思路大致是一样的. 在非常量的情况下, 验证节点时需要验证两个子节点的指令; 此外, 当两个子节点类型不同时, 要验证是否生成了类型转换指令. 一个典型的例子如下

    struct BinaryOperationNode* b;
    struct VariableNode* var0,* var1;
    struct List* ins,* subIns0,* subIns1;

    prepare();
    var0 = newVariableNode(ident);
    var1 = newVariableNode(ident1);
    declare(var0, INTEGER);
    declare(var1, REAL); // Oops 类型不一样

    b = newBinaryOperationNode(ASSIGN, /* 赋值运算, 类型转换一定是将右值类型转成左值类型 */
                            (struct AbstractValueNode*)newVariableNode(ident),
                            (struct AbstractValueNode*)newVariableNode(ident1));
    ins = b->createInstruction(b);
    subIns0 = var0->addressOf(var0);
    subIns1 = var1->createInstruction(var1);
    assert(ins->count(ins) - 2 == // 多出两条指令
           subIns0->count(subIns0) + subIns1->count(subIns1));
    while (0 != subIns0->count(subIns0)) {
        assertInsEqual_Del(ins->popElementAt(ins, 0),
                           subIns0->popElementAt(subIns0, 0));
    }
    while (0 != subIns1->count(subIns1)) {
        assertInsEqual_Del(ins->popElementAt(ins, 0),
                           subIns1->popElementAt(subIns1, 0));
    }
    // +1: 类型转换
    assert(REAL_2_INT ==
           ((struct AbstractInstruction*)(ins->elementAt(ins, 0)))->code);
    revert(ins->popElementAt(ins, 0));
    // +2: 整型赋值
    assert(INT_ASSIGN ==
           ((struct NoParamInstruction*)(ins->elementAt(ins, 0)))->code);
    revert(ins->popElementAt(ins, 0));

    var0->delNode(var0);
    var1->delNode(var1);
    b->delNode(b);
    subIns0->finalize(subIns0);
    subIns1->finalize(subIns1);
    ins->finalize(ins);
    clear();

而双目运算还有一个职责, 那就是条件短路 (这篇文章中最后的部分描述了条件短路双目运算的指令的模型). 这一点的测试给出一个逻辑与运算的测试例子

    struct BinaryOperationNode* b;
    struct VariableNode* var0,* var1;
    struct List* ins,* subIns0,* subIns1;

    prepare();
    var0 = newVariableNode(ident);
    var1 = newVariableNode(ident1);
    declare(var0, INTEGER);
    declare(var1, INTEGER);
    b = newBinaryOperationNode(AND,
                            (struct AbstractValueNode*)newVariableNode(ident),
                            (struct AbstractValueNode*)newVariableNode(ident1));
    ins = b->createInstruction(b);
    subIns0 = var0->createInstruction(var0);
    subIns1 = var0->createInstruction(var1);
    assert(ins->count(ins) - 4 == // 指令 +4
           subIns0->count(subIns0) + subIns1->count(subIns1));
    while (0 != subIns0->count(subIns0)) { // 条件 1
        assertInsEqual_Del(ins->popElementAt(ins, 0),
                           subIns0->popElementAt(subIns0, 0));
    }
    // 短路跳出
    assert(JMP_NOT_TOP ==
           ((struct AbstractInstruction*)(ins->elementAt(ins, 0)))->code);
    assert(ins->elementAt(ins, ins->count(ins) - 2) ==
           ((struct JumpInstruction*)(ins->elementAt(ins, 0)))->targetIns);
    revert(ins->popElementAt(ins, 0));

    while (0 != subIns1->count(subIns1)) { // 条件 2
        assertInsEqual_Del(ins->popElementAt(ins, 0),
                           subIns1->popElementAt(subIns1, 0));
    }
    // 跳到出口
    assert(JMP ==
           ((struct AbstractInstruction*)(ins->elementAt(ins, 0)))->code);
    assert(ins->elementAt(ins, ins->count(ins) - 1) ==
           ((struct JumpInstruction*)(ins->elementAt(ins, 0)))->targetIns);
    revert(ins->popElementAt(ins, 0));
    // 加载 0
    assert(CONST_INT ==
           ((struct AbstractInstruction*)(ins->elementAt(ins, 0)))->code);
    assert(0 ==
           ((struct IntParamInstruction*)(ins->elementAt(ins, 0)))->param);
    revert(ins->popElementAt(ins, 0));
    // 出口 伪指令
    assert(NOP ==
           ((struct AbstractInstruction*)(ins->elementAt(ins, 0)))->code);
    revert(ins->popElementAt(ins, 0));

    var0->delNode(var0);
    var1->delNode(var1);
    b->delNode(b);
    subIns0->finalize(subIns0);
    subIns1->finalize(subIns1);
    ins->finalize(ins);
    clear();

文件

目前可以在 svn 上弄到一个最近版本. 使用

 

svn checkout http://bitgarden.googlecode.com/svn/trunk/Jerry Jerry

svn checkout http://cobjorntlib.googlecode.com/svn/trunk/ Jerry/COOL

以获取全部代码.

在 Jerry 目录下有 Makefile 一枚, 重要的 make 项目包括

 

semantic:Jerry.out JerryVM.out # 生成编译器及虚拟机

syntax:Syntax.out              # 仅语法分析

lexical:Lexical.out            # 仅词法分析


alltest:test-symbol-table.out test-st-manager.out test-constant-fold-type-of.out test-instruction-arith.out # 产生所有测试

runtest:alltest       # 运行所有测试

test-symbol-table.out

test-st-manager.out

test-constant-fold-type-of.out

test-instruction-arith.out

 

这里也给出全部代码下载 jerry-compiler.zip

分享到:
评论
1 楼 lwwin 2009-10-24  
注意到这次COOL的更新,总算把所有的.C代码变更为无需直接INCLUDE了
这些工作量就少了好多=x=

慢慢比对中……

相关推荐

    IEEE30节点测试系统matlab M文件,包含各节点信息.rar

    IEEE30节点测试系统是电力系统分析中常用的一个标准模型,它被广泛用于验证和比较不同的潮流计算算法、稳定性分析以及动态模拟等研究。MATLAB作为一款强大的数值计算和编程环境,非常适合进行这类复杂的工程计算。这...

    使用multisim和matlab模拟节点电压法.rar

    Multisim是一款由National Instruments公司开发的电路仿真软件,它允许用户在虚拟环境中构建、测试和分析电子电路。在Multisim中,我们可以方便地绘制电路图,添加各种元件,并通过虚拟仪器如万用表、示波器等测量...

    three.bsp体布尔运算方法

    体布尔运算是一种在3D几何体之间进行操作的技术,例如合并、相交、差集等,这些操作在游戏开发、建筑设计和可视化应用中非常常见。 BSP树是一种数据结构,用于将三维空间分割成互不重叠的区域,每个区域要么完全...

    基于QT+C++开发的数据计算和图像处理的小工具+程序控制+数据计算+逻辑运算+图像处理+可视化蓝图编辑等(毕设&课设&项目开发

    基于QT+C++开发的数据计算和图像处理的小工具+程序控制+数据计算+逻辑运算+图像处理+可视化蓝图编辑等,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于QT+C++...

    潮流计算入门学习(IEEE6节点)(Matlab实现)

    - **简介**:IEEE 6节点系统是一个简化的电力网络模型,常用于教学和测试算法,它包含6个节点和若干支路,能体现基本的电力系统特性。 - **特点**:虽然简单,但足以展示潮流计算的基本原理和方法。 3. **MATLAB...

    Android字符串运算的计算器

    在Android平台上,开发一个能处理字符串运算的计算器是一项挑战性的任务。这个计算器应用程序允许用户输入一个完整的数学表达式,如 "2 + 3 * (4 - 5)",然后通过字符串解析和运算来得出结果。以下是一些关于实现...

    二叉树及其运算.docx

    【二叉树及其运算】 二叉树是一种在计算机科学中常用的数据结构,它由节点构成,每个节点最多有两个子节点,通常称为左子节点和右子节点。在本实验报告中,我们将探讨如何以二叉链表的形式存储二叉树,并实现一系列...

    大整数运算包的设计与实现

    在实现过程中,考虑到效率,可以利用C++的模板元编程技术进行编译时计算,或者使用多线程技术来加速并行运算。同时,对于某些常用操作,如加法和乘法,可以通过内联函数或宏来减少函数调用开销。 总之,设计和实现...

    数据结构—集合运算实现 实现报告(含代码)

    在实现过程中可能遇到的问题包括:如何有效地遍历链表、如何处理重复元素、如何在保持时间复杂度较低的同时正确实现集合运算等。解决这些问题需要深入理解和熟练运用链表数据结构。 七、运行结果 7.1、实现集合 ...

    IEEE33配电网潮流计算,ieee33节点潮流计算,C/C++

    IEEE(电气与电子工程师协会)为研究和测试目的制定了一系列标准的电力系统模型,其中,IEEE33节点系统是小型配电网的一个标准模型。这个模型由33个节点(包括负荷节点和电源节点)组成,反映了实际配电网的复杂性,...

    浙江大学软件开发课堂测试

    - **构造过程**: 按照给定的序列构建二叉排序树时,每个节点的左子树中所有节点的值都小于它,右子树中所有节点的值都大于它。 #### 三、简答题知识点解析 **1. 散列表** - **散列函数**: 散列函数用于将关键字...

    cadence运算放大器仿真

    设计复杂混合信号芯片时,面临市场窗口缩小、开发周期短、低电源电压、更小工艺节点、更高性能和更低功耗的挑战。为了满足这些需求,一次成功的设计和设计重用变得至关重要。Cadence的仿真策略基于Spectre模拟器、...

    运算表达式计算库

    - "MathParserTest.exe":这可能是一个测试应用程序,用于验证库的正确性和性能,通过运行各种测试用例来检查表达式的解析和计算是否符合预期。 - "COPYING.TXT"、"README.txt"、"ALTERNATIVE.TXT"、"BUGS.TXT":...

    swift-Expressions-以可视化算术和逻辑表达式运算过程

    总结来说,"swift-Expressions-以可视化算术和逻辑表达式运算过程"项目是一个结合了Swift语言特性、数据结构、UI设计和交互的实践示例,对于学习和提升Swift开发技能,特别是理解和应用表达式及其可视化有着积极的...

    数据结构一元多项式的加法运算

    在实际的开发过程中,可能还需要考虑其他因素,如错误处理、输入/输出格式、以及可能的扩展功能,如减法、乘法等。但核心的实现是构建一个有效且灵活的数据结构,以便于处理一元多项式的运算。通过这样的实践,我们...

    Labview应用技术 四则运算-完善(课堂实训).docx

    LabVIEW,全称Laboratory Virtual Instrument Engineering Workbench(实验室虚拟仪器工程工作台),是一款由美国国家仪器(NI)公司开发的图形化编程环境,广泛应用于数据采集、测试测量、控制系统设计等多个领域。...

Global site tag (gtag.js) - Google Analytics