该楼层疑似违规已被系统折叠 隐藏此楼查看此楼

宏观思路:

人脑和计算机处理的对象都是信息,我在这里讨论的信息专门指思维处理的对象。信息的表达方式(数据结构)具有灵活性和多样性,但是为了使计算机能够模拟出人类思维,能对信息进行推理、分析、归纳、分类等操作,我们就必须对信息的表示方式进行规范(约束),即定义一套专门的数据结构以表达思维信息。一旦建立了这样一套数据结构,我们便能够在此基础上建立思维的基本函数。

从逻辑角度探讨知识:

什么是知识?知识就是事物与事物之间的逻辑关系。知识的基本单元是概念。概念可以是名词,也可以是动词或谓词。名词的作用,就是表达物质的名称与具体物质形态(属性)的唯一对应关系,相当于程序设计语言中变量名与变量值的对应关系。动词的作用,则是表达动作的名称与具体行动步骤之间的唯一对应关系,相当于编程语言中函数名与函数体内部指令之间的对应关系。还有谓词,谓词容易与动词相混淆,因为自然语言语法中没有明确地区分开两者,但两者还是有区别的——动词(函数)一旦被执行,就会立刻产生一个效果,比如我调用printf函数,程序就会马上输出我指定的文本;而谓词被执行却不会立即产生执行效果,学过逻辑编程语言(如Prolog)的人都知道,谓词用于表达两个名词对象(变量)之间的逻辑关系,比如我定义一个谓词Father,并写一个语句Father(A,B)来表达A是B的父亲,这个语句被解释器执行后不会立即产生可见的效果,但是它的的确确地在A和B两对象之间建立了逻辑联系即“父子关系”,以后比如我写一段代码要求程序输出A——H这八个人中谁和谁是父子关系,程序检索到这一逻辑关系就会输出:A是B的父亲。这时候谓词的执行效果就体现出来了。由此我们可以看出动词与谓词之间的区别:①动词的执行效果会立即显现(虽然有些对用户而言是不可见的),谓词的被执行则不一定马上有效果显现。② 谓词可以表达名词对象之间的逻辑关系,而动词却做不到。

名词和动词组合形成命令,比如动词“打开”与名词“点名册文件”组合,形成命令“打开点名册文件”。有时候我也把命令称作祈使句(imperative)。

名词与谓词组合形成命题(Proposition),例如名词“南京市”、名词“江苏省”与谓词“属于”组合,形成命题“南京市属于江苏省”。

命题还有一种更复杂的“如果~则”形式,它由两个命题(前件和后件)组成,形如:如果命题A成立,则命题B成立。

命令也有一种更复杂的“如果~则”形式,我把它叫做条件祈使句,它由一个命题和一个命令组成,形如:如果命题成立,则执行命令。

我把人类之知识基本概念分为名词(Noun)、动词(Verb)、谓词(Predicate)三大类。所以我给我的这套知识表达方法取名为“NVP知识表示法”。

编程实践:

名词、动词、谓词的数据结构定义如下(构造函数省略):

classnoun//名词
{
public:
string name;//名称
string data;//具体内容
};
classverb//动词
{
public:
string name;//名称
funcp exe_func;//要执行的函数
};
classpredicate//谓词
{
public:
string name;//名称
boolpositive = true;//若要对逻辑取反,则赋值为false
boolfuncp judge;//判断标准
};

命令(祈使句)和命题数据结构定义如下:

classimperative//命令句,祈使句
{
public:
string name;
intverb;//必须要有一个动词
vectorobjects;//这些名词是动词的作用对象
voidexecute();//执行整句命令
};
classproposition//命题
{
public:
string name;
inttype;//0:单一命题;1:否定命题;2:与连接;3:或连接
vectorsub_pro;//子命题
vectorobjects;//如果type==0,则要求有一系列名词对象
intpredicate;//如果type==0,则要求有一个谓词
booljudge();//判断命题是否成立
};

If~then形式的命题(P_P)、命令(P_I)数据结构定义如下:

【备注:P代表命题,I代表祈使句。条件命题形式为:如果P1则P2,所以简写为P_P。条件祈使句P_I同理】

classP_P//ifpro1 then pro2 如果命题1成立,则命题2成立
{
public:
string name;
proposition antecedent;//前件
proposition consequent;//后件
};
classP_I//ifpro then imp 如果命题成立,则执行祈使句命令
{
public:
string name;
proposition condition;//条件
imperative command;//要执行的命令
voidexecute()
{
if(condition.judge())
{
//cout<
command.execute();
}
}
};

在程序中我使用vector数组来存储所有的名词、动词、谓词、祈使句、命题、事实(已经被证明为真的命题)、条件祈使句、条件事实命题。

程序的重点是命题的判定过程。让我们一起看一下这段代码:

boolproposition::judge()
{
if(type == 0)//单一的命题
{
if(predicates[predicate].judge(objects))returntrue;
else
{
if(GetFactCode(this->name)) returntrue;
}
returnfalse;
}
elseif(type == 1)//否定的命题
{
return!sub_pro[0].judge();
}
elseif(type == 2)//用“与”连接的命题
{
boolright = true;//先假设命题成立
for(inti = 0; i < sub_pro.size(); i++)
{
if(!sub_pro[i].judge())right = false;//只要有一个子命题不成立,则整个命题不成立
}
returnright;
}
elseif(type == 3)//用“或”连接的命题
{
bool_right = false;//先假定命题不成立
for(inti = 0; i < sub_pro.size(); i++)
{
if(sub_pro[i].judge())_right = true;//只要有一个子命题成立,则整个命题成立
}
return_right;
}
}

命题判定部分最关键的一句是if(predicates[predicate].judge(objects))

这句代码中的predicates[predicate]表示一句命题中的谓词部分,objects是谓词作用的对象,而judge是谓词的方法,它是一个指向bool型函数的指针。比如我定义了一个函数LessThan,它接受的参数是一个int数组(实际上只有前两个元素是有用的),如果第一个元素 < 第二个元素,则返回真,否则返回假。命题在进行判定的时候,它就会调用我预先定义过的judge函数,并把实参代入进去。

再看下这句代码:GetFactCode(this->name)。意思是:程序会在“事实库”里面查找同名的命题,如果找到,则该命题被判定为真。我来解释一下“事实库”的作用:它与“命题库”不同,命题库存储的是用户自定义的命题,里面的命题可能是真命题也可能是假命题;而事实库中存储的是已经被证明为真,或者被强行设定为真命题的命题。

命题的属性type用来标志这个命题的类型——0表示单一的命题,1表示否定命题(子命题只有一个,子命题成立则返回false,不成立则返回true),2表示用“与”连接的命题,3表示用“或”连接的命题。

一个复合命题由多个子命题构成。例如 a>b 且 cb 和 c

前面我介绍过命题还有一种更复杂的形式:如果 命题1成立 则 命题2成立。命题1被称为前件(antecedent),命题2被称为后件(consequent)。在我的程序里面,这种形式的命题一旦建立就立刻被设定为真,即存入事实库中。(目前程序功能还不完善,以后要增加对if~then命题的判定过程)。下面再提一个新功能:刷新。大家应该可以看出,if~then命题由前件和后件组成,如果前件成立则后件也成立。那么依照这一原则,我们让程序判定前件命题是否成立,如果发现前件命题成立,则让后件命题也成立(现在事实库中搜索后件命题,如果没有找到,就把后件命题加入到事实库中),这个过程被我称为“刷新”,它的作用是让程序发现更多的“事实”。刷新过程的代码如下:

void refresh(vectorv)
{
if(if_facts.size() > 0)
{
for(inti = 0; i < if_facts.size(); i++)
{
if(if_facts[i].antecedent.judge()&&!GetFactCode(if_facts[i].consequent.name))
{
facts.push_back(if_facts[i].consequent);
cout<
}
}
}
cout <
}