随想012:断言

2023-11-08

  • C 标准库提供了名为 assert 的断言宏;

  • C# 语言提供了名为 Debug.Assert 的断言方法;

  • Java 语言提供名为 assert 的断言关键字。

主流编程语言不约而同的在语言层面上提供了 断言 机制


  • David R. Jamson,编译器 Icc 的开发者之一,在他的《C 语言接口与实现——创建可重用软件的技术》一书中,教授如何实现断言(assert)接口,这是其它接口的基础;

  • Stephen A.Maguire,Excel 开发者和领导者,在他的《编程精粹:Microsoft 编写优质无错 C 程序秘诀》一书中,用一章来讲述,如何自己设计并使用断言(assert);

  • Andy Hunt,著名顾问,敏捷宣言成员之一,在他和 Dave Thomas 合著的《程序员修炼之道》一书中,介绍断言式编程,称赞这是注重时效的编程方法。

编程专家们不约而同的提倡使用 断言


  • ST 外设驱动固件库中随处可见 assert_param 断言宏;

  • 网络协议栈 lwip 中随处可见 LWIP_ASSERT 断言宏;

  • 操作系统 FreeRTOS 中随处可见 configASSERT 断言宏。

优秀的代码不约而同的已经使用了 断言


即使是第一次听说断言,你也应该意识到,这个东西应该挺重要。那么接下来的问题是,什么是断言(assert)?

断言,就是明确且坚定的指出某事是真的

to state clearly and firmly that sth is true 《牛津词典》

在 C 语言环境中,断言是一个宏,如果其参数的计算结果为假,就中止调用程序的执行
请添加图片描述

就这?

听上去好像很一般嘛,不就是用来做参数检查的嘛,就这也值得特意开个专题?

很值得!

随着技术人员见解的增长,他们都会使用断言,或早或晚,大势所趋,殊途同归。

与其说这是技术水平不断提升的结果,不如说这是编程思想转变的结果。这个转变是:从被动的调试 BUG,开始转变为主动的发现 BUG


检查不可能发生的情况

每一个程序员似乎都必须在其职业生涯的早期记住一句咒语。它是计算机技术的基本原则,也是我们所做的每一件事情的核心信仰。那就是:

这绝不会发生… 1

比如设计一个处理字符串的内部函数:

void StrDoSomething(char* str);
{
    ...
}

参数 str 绝不应该是 NULL,或许你会说,我绝不会让 str 为空

醒醒吧,我们不要这样自我欺骗,特别是在编码时。如果它不可能发生,用断言确保它不会发生!

If It Can’t Happen, Use Assertions to Ensure That It Won’t

就像这样:

void StrDoSomething(char* str);
{
    ASSERT(str != NULL);
    ...
}

如果你认为参数 str 不可能为空,就用断言 ASSERT(str != NULL) 来确保它不会为空;

如果你认为变量 count 不可能为负,就用断言 ASSERT(count >= 0) 来确保它不会为负;

如果你认为 switchdefault 分支不可能执行,就在 default 分支中用 ASSERT(false) 来确保它不会执行…

这些宏是无价的财富,你和使用该函数的人都将受益,如果将来某个程序员错误的使用了这个函数,函数自己会以明确的方式告知:嗨,你犯了个错误

请添加图片描述


对函数参数进行确认

设计一个将无符号数转为字符串的函数,转换后的字符串可以是二进制、十进制、十六进制样式,函数为:

void UnsignedToStr(unsigned u, char* str, unsigned base);
{
    ...
}

参数 str 指向转换后的结果,必须非空;

参数 base 指定何种进制样式,可能的值为 2、10、16,分别表示二进制、十进制、十六进制样式。

我们可以使用断言对参数进行确认,之后这个函数在每个调用点都会对参数进行检查,如果用户发生了错误,就可以很快的、自动的把它们检查出来

void UnsignedToStr(unsigned u, char* str, unsigned base);
{
    ASSERT(str != NULL);
 	ASSERT(base == 2 || base == 10 || base == 16);
    ...
}

有一次我的程序错误的键入了循环次数,这会导致调用 gpio 库函数时,传入不存在的 GPIO 端口参数。由于我使能了 ST 外设驱动固件库的断言宏,当我把新改动的代码下载运行时,断言立即报告了这个问题:
请添加图片描述

根据断言提示,结合新增加的代码,很快找到了问题的原因。如果没有使能断言宏,这次非法访问将触发总线 Fault,免不了一顿调试和定位。
这里值得注意的是,断言确认并不能代替异常判断,如果 UnsignedToStr 是一个外部使用的函数,并且外部调用时 str 有可能为空,则必须对这种异常情况做明确的处理:

void UnsignedToStr(unsigned u, char* str, unsigned base);
{
 	ASSERT(base == 2 || base == 10 || base == 16);
    
    if(str == NULL)
    {
        //卫语句,对异常做处理
        ...
    }
    ...
}

使用断言来消除未定义行为

memcpy 是定义在 string.h 中的一个库函数,函数原型为:

void *memcpy(void *s1, const void *s2, size_t n)

函数 memcpy 从 s2 指向的对象中复制 n 个字符到 s1 指向的对象中。如果复制发生在两个重叠的对象中,这种行为未定义2

行为未定义 在 C/C++ 中很常见,某些行为,如果在标准中没有明确规定、也不限制编译器的具体实现,那么这些行为就是未定义的。因此,未定义行为的执行结果取决于编译器,可能各家编译器都不相同,理论上,即使执行结果把你的硬盘格式掉责任都在你方。未定义行为就相当于非法行为3,我们可以用断言来消除未定义行为。

对于 memcpy 函数,可以使用断言来进行重叠检查:

/*封装内存拷贝函数*/
void *s_memcpy(void *s1, const void *s2, size_t n)
{
    ASSERT(s1 != NULL && s2 != NULL);
    /*检查内存重叠*/
    ASSERT((char *)s1 >= (char *)s2 + n || (char *)s2 >= (char *)s1 + n);
    
    memcpy(s1, s2, n);		//调用库函数
}

利用断言检查隐式假设的正确性

如果假设 long 占用 4 个字节,可以使用以下断言来检查假设是否正确:

ASSERT(sizeof(long) == 4);

这里困难的不是理解这句话的意思,而是如何意识到自己的代码是基于了某个假设!

罗伯特 B.西奥迪尼(Robert B.Cialdini)在他的《影响力》一书中指出:如果你是个售货员,那么当顾客准备购买毛衣和套装时,你应该总是先给顾客看套装然后再给顾客看毛衣。这样做的理由是可以增加销售额,因为在顾客买了一件 $500 元的套装之后,相比之下,一件 $80 元的毛衣就显得不那么贵了。但是如果你先给顾客看毛衣,那么 $80 元一件的价格可能会使其无法接受,最后也许你只能卖出一件 $30 元的毛衣。

任何人只要花 30 秒的时间想一想,就会明白这个道理。可是,又有多少人花时间想过这一问题呢

在编写函数时,要进行反复的思考,并且自问:“我打算做哪些假设?”

一旦确定了相应的假设,就要使用断言对所做的假设进行检验,或者重新编写代码去掉相应的假设。


在进行防御性编程设计时,利用断言进行报警

防御性编程设计通常是一种很好的编码风格,他可以让程序更加健壮,当出现异常时,能以一种优雅的方式退出或者降级运行。比如在恶劣电磁环境中,从存储器中读出的数据通常认为是不可靠的,所以我们会对数据进行校验,然后将数据和校验值一起写入存储器。

使用数据时,把数据和校验一起读取出来,然后再次计算数据的校验值,将新的校验值与读出的校验值做对比,如果不相等,表示数据遭到了破坏,则进行相应的异常处理:

/*从存储器读出数据*/
lwnvrb_peek(&resume_nvrb_s, RESUME_LEN_BYTES, len, resume_read_buf);

crc16 = to_uint16_low_first(&resume_read_buf[len - 2]);
/*防御性编码*/
if(crc16 != cal_sensor_crc16(resume_read_buf, len - 2))
{
    clear_resume();		//错误处理
    return 0;
}

这是一种典型的防御性编程设计,你能看出这样的代码隐含着什么问题吗?

安静的处理了异常!那些本该在设计阶段就应该规避的异常,比如硬件设计失误、软件设计失误,被隐瞒了

看上去风平浪静,实则暗涛汹涌,代码撒了谎!

想象你正在设计一个温度传感器,温度不会突变。所以你对 ADC 采集的数据做了防御性编程设计:在计算真实温度前,忽略了突变的 ADC 数据。这是一个常规的操作,称为滤波(滤除干扰)。

但正是这种操作,掩盖了设计缺陷,由于信号调理链路的设计问题,会周期的产生尖脉冲,但是防御性编程的存在,这个问题被隐瞒了。其结果就是,信号调理硬件问题一直没能解决,温度传感器的精度始终差强人意。

那我们还要不要防御性编程设计?

当然要,不过要做一点改动,在进行防御性编程设计的同时,用断言对错误进行报警

回到存储器读取数据的例子。在开发阶段,办公室环境中,我们判断电磁环境良好,因此认为存储器是可靠的,我们在保留防御性编程的基础上,增加了一条断言,如果进入防御性代码,则让程序“死”在这里:

/*从存储器读出数据*/
lwnvrb_peek(&resume_nvrb_s, RESUME_LEN_BYTES, len, resume_read_buf);

crc16 = to_uint16_low_first(&resume_read_buf[len - 2]);
/*防御性编码*/
if(crc16 != cal_sensor_crc16(resume_read_buf, len - 2))
{
    ASSERT(0);		//如果进入防御代码,则触发断言,通常程序会停在这句代码中
    clear_resume();	
    return 0;
}

死掉的代码不会撒谎

这段代码在联调阶段,触发了断言,给出了触发断言的文件名和位置(第几行)。经过调试,很快锁定了触发断言的原因,不是存储器不可靠,而是软件逻辑的问题。如果没有断言,这个问题将会被隐瞒,并以其它的形式困扰我很久,且难以查找。
请添加图片描述


利用断言检查契约

想象一下这种场景:你的设备通过一套协议和上位机软件通讯,协议规定了数据的格式。我们以 设置报警 为例,协议规定:报警类型占 1 个字节,可能的值为 0 (无报警)、1 (上限报警)和 2 (下限报警)。传统编程这样解析上位机发来的数据:

switch(alarm_type)
{
    case ALARM_NONE:		//无报警
        alarm_value = ...;
        break;
    case ALARM_UPPER::		//上限报警
       	alarm_value = ...;
   		break;
    case ALARM_LOWER:		//下限报警
        alarm_value = ...;
        break;
    default:
        break;
}

但是,第一次联调时,你用上位机软件设置了报警值,随后发现你的设备并没有按照预期报警。这种事情在联调阶段很常见,问题在哪里?是发送的协议不正确还是你的报警逻辑不正确,不得而知。你只好抓取上位机下发的数据,然后对照协议分析,费时费力。

让我们换一种思路。

这种使用协议进行通讯的场景,是典型的契约式编程。协议即契约,契约作用于双方。作为契约的一方,你必须履行契约,而且有责任检查对方是否遵守契约,如果契约被破坏,必须以合适的方式处理。触发断言,就是一种很好的处理方式。

还是以上面的设置报警为例,使用断言检查契约,代码为:

switch(alarm_type)
{
    case ALARM_NONE:		//无报警
        alarm_value = ...;
        break;
    case ALARM_UPPER::		//上限报警
       	alarm_value = ...;
   		break;
    case ALARM_LOWER:		//下限报警
        alarm_value = ...;
        break;
    default:
        ASSERT(false);		//如果数据不合法,触发断言
        break;
}

修改后的代码仅仅多了一行断言(ASSERT(false)),但意义迥然不同。这句断言可以证明对方遵守了契约,或者在对方违反契约时主动报告错误

还是第一次联调,你在上位机软件上刚点击了设置报警值的下发按钮,就发现你的设备显示屏上输出了一行断言:

请添加图片描述

你按照文件名和行号,找到这句断言,立刻明白,上位机程序员出错了,因为数据格式违反了契约。


如何实现断言

认识到断言的好处后,我们自然想知道如何在项目中实现断言。这里给出一个断言接口的实现。

assert.h 提供对外接口,也就是 ASSERT 宏,代码如下所示:

#ifndef __assert_h__
#define __assert_h__
#include "app_assert_cfg.h"

#define __STR(x)  __VAL(x)
#define __VAL(x)  #x

#ifdef  ASSERTS
    extern void _Assert(char *str_file, char *str_line);
#define ASSERT(e)  ((e) ? (void)0 : _Assert(__FILE__ ":", __STR(__LINE__)))
#else
#define ASSERT(e)
#endif      //#ifdef ASSERTS

#endif      //#ifndef __assert_h__

在设计上,这个宏需要一些技巧:

  1. 宏依赖一个外部函数 _Assert,用于输出断言信息。这个函数需要你自己实现,因为信息的显示依赖特定硬件。嵌入式设备没有标准输出,有的设备使用显示屏,有的使用 UART,各不相同,根据硬件而定。
  2. 代码要紧凑。通常编译器提供的符号 __LINE__ 是一个十进制常量,这里用 __STR 宏在编译阶段转换成字符串类型,避免在代码中做格式转换,这是一个小技巧。
  3. 为了生成的代码最小,这里没有输出测试条件,对于断言 ASSERT(str != NULL) ,其中 str != NULL 称为测试条件。C 标准提供的断言会输出这个测试条件,但是嵌入式系统通常存储容量不大,并且根据文件和行号能容易的找到测试条件,因此本接口不输出测试条件
  4. 为了生成的代码最小,没有把函数 _Assert 的参数 str_filestr_line 粘连在一起,而是分成了两个参数。C 标准实现是将这两个参数在编译阶段粘连成一个 mesg 参数。这是因为如果在同一个文件中定义了多个断言,那么本接口的实现方法生产的代码更小(文件名 __FILE__ 只会存储一份)。
  5. 接口需要一个外部头文件 app_assert_cfg.h,在这个头文件中可以使能或者禁用断言。在开发、维护阶段使能断言,则在这个头文件中定义宏 ASSERTS,在正式销售版本中禁止断言,则在这个文件中取消定义宏 ASSERTS。为什么不在 assert.h 文件中定义或取消宏 ASSERTS 呢?这涉及到模块化代码的一个原则:模块化代码应该是只读的。具体参见 随想007:模块化代码

函数 _Assert 的实现比较简单,给出一个在 LCD 屏上显示断言信息的例子:

#include "lcd_func.h"

/*用于LCD打印输出的接口*/
#define ASSERT_LCD_BUF_NUM          65
void _Assert(char *str_file, char *str_line)
{    
    char  lcd_buf[ASSERT_LCD_BUF_NUM];
    
    snprintf(lcd_buf, ASSERT_LCD_BUF_NUM, "%s%s", str_file, str_line);
    disp_txt_by_specify_location(LCD_ROW_1, LCD_COLUMN_1, lcd_buf);
    
    while(1)
    {
        WWDT_Feed();
    }
}






读后有收获,资助博主养娃 - 千金难买知识,但可以买好多奶粉 (〃‘▽’〃)
千金难买知识,但可以买好多奶粉


  1. 《程序员修炼之道》 ↩︎

  2. 《C 标准库》 ↩︎

  3. 《编程精粹》 ↩︎

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

随想012:断言 的相关文章

  • 如果没有断言,pytest 会失败吗?

    今天我的测试失败了 但很高兴成功了 因为我忘记了最后一行相当重要的一行 assert actual expected 我希望机器将来能够发现这个错误 有没有办法让 pytest 检测测试函数是否未断言任何内容 并将其视为测试失败 当然 这需
  • 如何编写具有不同数量的信息参数的 C++ 断言宏?

    我正在尝试写一个宏dbgassert与标准相似assert 除了什么assert是吗 我想要dbgassert打印任意数量的附加参数 包含调试信息 到目前为止我所拥有的内容如下所列 改编自这个答案 但我的代码中的可变参数模板或宏存在问题 如
  • 如何在 Visual Studio 2012 中查看断言失败的 C++ 堆栈跟踪?

    当我的断言在 Visual Studio 2012 中失败时 如何查看堆栈跟踪 如果我附加到一个进程 它会按预期工作 但是当我使用 F5 调试 构建时 我的断言会发生 给我 中止 重试 忽略 提示 但我在调试器 有什么方法可以在调试构建期间
  • 使用 LifecycleCallbacks 时找不到该文件

    我有问题form validation in symfony2 就我而言 form gt isValid 命令结果为The file could not be found 即使我在填写表格时提供了文件 另外调试setFile函数于docum
  • Python 中断言的使用示例?

    我已经读过有关何时使用断言与异常的信息 但我仍然没有 明白 似乎每当我认为我处于应该使用断言的情况时 在后来的开发中我发现我 三思而后行 以确保当我调用函数时断言不会失败 由于还有另一个 Python 习惯用法 即更喜欢使用 try exc
  • cucumber.js 并且不是一个函数

    我正在练习使用 cucumber js 通过 BDD 编写一些单元测试 当我尝试使用 And 语句时 该错误表明 TypeError And is not a function 这是我的代码 feature Feature dataTabl
  • Python:断言变量是实例方法?

    如何检查变量是否是实例方法 我正在使用Python 2 5 像这样的事情 class Test def method self pass assert is instance method Test method inspect ismet
  • 打字稿检查类型 A === 类型 B | C型

    在一个文件中我有这样的内容 export const all a b c d e f type AllKeysType typeof all export type AllKey keyof AllKeysType 在另一个文件中我有这样的
  • 在 Boost Test 框架中测试断言

    我使用 Boost Test 框架对我的 C 代码进行单元测试 并想知道是否可以测试函数是否会断言 是的 听起来有点奇怪 但请耐心听我说 我的许多函数在输入时检查输入参数 断言它们是否无效 对此进行测试将很有用 例如 void MyFunc
  • 何时在客户端和通用 GWT 代码中使用断言

    StackOverflow 上有几个问题讨论何时应该使用断言语句而不是抛出异常 例子here https stackoverflow com questions 4333371 when to use assert and when to
  • 为什么 Assert.AreEqual(1.0, double.NaN, 1.0) 通过?

    简短的问题 为什么Assert AreEqual 1 0 double NaN 1 0 经过 然而Assert AreEqual 1 0 double NaN fails 这是 MSTest Microsoft VisualStudio Q
  • Fortran 中的断言

    Fortran 是否有与 C 等效的标准函数 关键字assert 我找不到assert我在 Fortran 2003 标准中提到过 我发现了几种使用预处理器的方法 但是在这个answer https stackoverflow com a
  • 2 个 JUnit Assert 类之间的差异

    JUnit 框架包含 2Assert类 显然在不同的包中 和每个类的方法看起来非常相似 有人能解释这是为什么吗 我指的课程是 junit framework Assert http junit org junit javadoc 4 5 j
  • 使用断言的最佳实践?

    使用是否存在性能或代码维护问题assert作为标准代码的一部分而不是仅将其用于调试目的 Is assert x gt 0 x is less than zero 比更好或更差 if x lt 0 raise Exception x is l
  • 有什么理由使用运行时断言而不是编译时断言?

    在审查 Visual C 代码库时 我发现了以下奇怪的事情 运行时断言 即检查条件 如果违反条件则抛出异常 用于可以在编译时评估条件的情况 assert sizeof SomeType sizeof SomeOtherType 显然 编译器
  • 如何在运行时检测某些编译器选项(如断言)是否设置为 ON?

    Delphi 中检查断言是否有效的条件是什么 当断言在代码中不活动时 我希望能够做一些事情来抑制有关未使用变量的提示 例如 procedure Whatever var v Integer begin v DoSomething Asser
  • Try..Catch 块中的断言被捕获

    刚刚遇到一些有趣的行为 Assert被抓住Catch block List
  • 正确使用断言

    您能否帮助我更好地理解 断言 与 引发异常 的正确用法是什么 每种场景什么时候合适 场景1 CODE public Context Algorythm algo if algo null throw new IllegalArgumentE
  • C# 编译器如何删除发布版本中的 Debug.Assert?

    我最近正在浏览一些代码 并考虑是否需要小心放置在其中的表达式Debug Assert语句 例如昂贵的操作或具有副作用的操作 然而 编译器似乎非常聪明地完全删除了Assert陈述和内在表达 例如 以下内容只会在调试版本上打印 static v
  • 如何在 Rails 测试中测试 params 哈希?

    以下内容会生成错误 未定义的局部变量或方法 params assert equal params recipient id users one id 如何测试参数哈希 另外 你如何测试assert redirect当存在参数时 参数附加到

随机推荐

  • C 修改文件或文件夹的权限,为指定用户 用户组添加完全控制权限

    分享一下我老师大神的人工智能教程 零基础 通俗易懂 http blog csdn net jiangjunshow 也欢迎大家转载本篇文章 分享知识 造福人民 实现我们中华民族伟大复兴 C 修改文件或文件夹的权限 为指定用户 用户组添加完全
  • 阿里一道Java并发面试题 (详细分析篇)

    说明 前天分享了一篇关于阿里的 Java常见疑惑和陷阱 的文章 有人说这个很早就有了 可能我才注意到 看完之后发现内容非常不错 有几个我也是需要停顿下想想 题目 我个人一直认为 网络 并发相关的知识 相对其他一些编程知识点更难一些 主要是不
  • Dingo + Laravel + JWT + Entrust + memcache 实现API设计

    Dingo Laravel JWT Entrust memcache 实现API设计 Dingo Api 是一个为laravel设计的用于API开发的开源包 规范和简化了Api的设计 JWT 即 Json Web Token 是一种新的用于
  • 将类模板做为参数(类模板中使用)

    将类模板做为参数 类模板中使用 这种方式只在类模板中使用 函数模板不能使用这种方法 将模板名做为一种特殊的数据类型 实例化对象的时候 使用模板名做为参数 传递给模板 下面例子中 数组和链表都是线性表 数组和链表自身为了适用不同的数据类型参数
  • 代理IP与Socks5代理:跨界电商智能爬虫与出海之道

    跨界电商正成为全球市场的关键驱动力 而代理IP和Socks5代理则为其提供了技术支持 本文将深入研究这两种代理技术在跨界电商 爬虫和出海战略中的重要作用 引言 介绍跨界电商的崛起和全球化趋势 引出代理IP和Socks5代理的重要性以及本文的
  • 魔兽争霸3宽屏显示器适配

    魔兽争霸只提供 4 3 的分辨率选择 800 600 1024 768 等等 如果显示器是 16 9 或 16 10 的话就有点儿蛋疼了 两边都会留出黑边来 可以在 windows 下面双击执行如下的文件来解决这个问题 wide scree
  • Atcoder Beginner Contest 291

    A camel Case AC代码 include
  • 屏蔽预训练模型的权重。 只训练最后一层的全连接的权重。_Soft-Masked BERT 一种新的中文纠错模型...

    今年 字节AI Lab与复旦大学合作提出了一种中文文本纠错模型 Soft Masked BERT 1 这项工作发表在了ACL 2020上 由于论文并没有开源代码 所以我将对这篇论文进行解读与复现 问题提出 中文文本纠错是一项挑战性的任务 因
  • Python pandas 排序出现'DataFrame' object has no attribute 'sort'错误

    目标 对dataframe一列时间字符串做升序排列 时间字符串格式 2020 03 23 06 20 00 00 00 df sort MTS ascending True 运行提示 AttributeError DataFrame obj
  • CNN训练细节:卷积核分解

    背景 最近看到一些分解卷积层的方法 比如三个3 3的卷积层替代一个7 7的卷积层 或者两个3乘3的卷积层替代一个5 5的卷积层 本文主要是个人粗浅的分析下原因 一 两个3乘3的卷积层替代一个5 5的卷积层 经典原理网图 如图所示 对于两层3
  • 蓝桥杯校内模拟赛题解

    蓝桥杯校内模拟赛题解 引言 本题解非官方满分题解 因此 可能存在下列问题 题意理解错误 导致答案错误 代码中存在一些问题 导致答案错误 算法复杂度的分析有误 导致不能在规定时间内得出结果 由于水平受限 本篇题解全部由 C 语言完成 题解中的
  • 自我管理的重要模型

    文章目录 前言 一 精力管理 自我管理的新旧理念 二 人类精力金字塔 精力管理四个层次 体能 情绪 思维 精神 三 运动 人类为什么喜欢躺平 每天怎么简单高效的完成20分钟的运动量 四 钟摆运动 钟摆运动对工作最大的指导意义 刻意休息 在这
  • Java 到 Go 过渡:基于 Go 开发分布式配置中心的实践

    目录 一 简介 二 Java 实现 三 Go 实现 四 从 Java 过渡到 Go 五 总结 在今天的技术世界中 从一种编程语言转向另一种是很常见的 特别是对于在企业级应用中具有广泛应用的语言如 Java 转向轻量级 效率更高的 Go 语言
  • 【因果学习】贝叶斯网络结构学习方法

    随机对照试验是发现因果关系的黄金准则 然而现实世界中很多问题往往由于道德伦理的原因不允许我们设置干预进行试验 这就引发了在观测数据上学习因果关系的需求 贝叶斯网络是概率论与图论相结合的产物 它用图论的方式直观地表达各变量之间的因果关系 为多
  • 面向对象编程(OOP):理解类、封装性的关键概念

    文章目录 对象 Object 什么是对象 面向对象 OOP 面向过程的编程语言 面向对象的编程语言 类 class 使用类创建对象的流程 类的定义 代码演示 初始化方法和实例属性 类属性和类方法 继承和多态 魔术方法 小结 类的封装性 属性
  • JSR303使用说明文档

    1 引言 参数校验是我们程序开发中必不可少的过程 用户在前端页面上填写表单时 前端js程序会校验参数的合法性 当数据到了后端 为了防止恶意操作 保持程序的健壮性 后端同样需要对数据进行校验 后端参数校验最简单的做法是直接在业务方法里面进行判
  • 大数据单机学习环境搭建(1)Hadoop本地单节点安装

    专题 大数据单机学习环境搭建和使用 1 资源获取 免费下载 2 Hadoop 本地模式 安装及文件配置 2 1安装java 2 2Hadoop安装与配置 2 3设置ssh免密登录 2 4开启hadoop 2 6访问应用 大数据单机学习环境搭
  • mysql 中使用 update 同时更新多个字段

    update 表名 set 字段1 新值1 字段2 新值2 字段3 新值3 where 条件 mysql gt select from tb person id name phone age sex description create t
  • 多机docker部署fisco-bcos区块链

    0 首先每台机器安装docker sudo yum install docker 展示一下机器环境 一共5台机器 111 203 104 97 111 203 104 113 111 203 104 114 111 203 104 116
  • 随想012:断言

    C 标准库提供了名为 assert 的断言宏 C 语言提供了名为 Debug Assert 的断言方法 Java 语言提供名为 assert 的断言关键字 主流编程语言不约而同的在语言层面上提供了 断言 机制 David R Jamson