豆豆到了晚上开始找妈妈了

原本谁哄都可以哄豆豆睡觉的,但最近一个星期,他开始赖上妈妈了。每天晚上睡觉必须要躺在妈妈身边才行,否则就大哭大闹。

不过豆豆白天还是更喜欢听着故事睡觉。今天上午,豆豆本来在吃奶,可是他有点困了,于是吃了几口就开始哼唧。后来我把他抱过来趴在我肩膀上,一边在屋里散布,一边给他讲小蝌蚪找妈妈的故事,他于是就安静了,趴在我肩膀上一动不动。一会就闭上眼睛睡着了。

DSC01240

DSC01227

豆豆练杂耍:
DSC01271

小和尚:
DSC01249

广告

LabVIEW中实现链表、树等数据结构

https://labview.qizhen.xyz/

LabVIEW自带的数据结构只有数组和队列。多数情况下,这两种数据结构足够开发者使用了。但是,我平时使用C++和C#语言更多一些,所以编写程序时常常会想到使用其它编程语言中常见的数据结构比如链表(List)、树(Tree)等。

LabVIEW中也可以编程实现这些数据结构,一个比较直观易懂的编程方法是基于LabVIEW中的类和引用来实现各类数据结构。我在《我和LabVIEW》一书的第13.3.5节中介绍了一个简单的链表容器的实现方法,它是基于LvClass编写的,数据流驱动的一种容器。但是正如我在书中提到的,它虽然和有一些和文本编程语言中的链表相类似的地方,但本质并不相同。文本编程语言中的链表,树等数据结构离不开引用(或指针),节点之间是通过引用来相互关联的。LabVIEW可以为数据创建引用,因此也可以方便的实现与文本语言中功能相同的数据结构。

这里插一段,介绍一下数据结构和数据容器的关系,我自己理解是这样的:数据结构侧重于数据的存储方式,比如如何排序;数据结构在加上与此结构相关的操作方法,比如添加删除数据等方法,就构成了一个数据容器。脱离了操作方法,单纯的数据结构用处非常有限。因此,我文章中在提到数据结构或者数据容器时,指的都是同一回事:数据结构和相关的方法。

为了介绍如何在LabVIEW中实现一个数据结构,我打算以双向链表为例,讲解一下如何编写它。

image

双向链表中每个节点都会记录上一个节点和下一个节点的位置。因此,在双向链表中,可以从一个节点直接跳转到它的上一个或下一个节点上去,也就是正向或反向遍历整个链表。可以直观的想到,使用LvClass实现这样的节点,只要为这个节点创建一个类ListNode,并且这个类有两个成员变量,它们的类型都是ListNode的引用,分别用于指向前一个和后一个节点就可以了:

image

这样的设计在文本编程语言中是没有问题的,但在LabVIEW中行不通。其它编程语言中,程序运行时,才会对类的对象进行初始化。LabVIEW中,VI一打开,它上面的控件和常量就需要被初始化了。某个对象在初始化时,它的成员变量也要被初始化,若它的成员变量的类型还是这个类,这以初始化的过程就陷入了死锁:类需要它的成员变量先初始化;它成员变量需要这个类先初始化。

基于同样的原因,一个类的成员变量的数据类型也不可以是这个类的子类:子类初始化需要先对它的父类进行初始化。但是,一个类的成员变量的数据类型可以是这个类的父类:父类在初始化的时候,不需要理会它的任何子类。

既然父类初始化时,不依赖于子类的初始化;而子类的对象又可以被当做父类的类型来保存,咱们就可以利用这一特性在LabVIEW中实现可以数据结构的节点了。只不过LabVIEW实现链表的节点要多一个步骤:我们需要为ListNode类再定义一个父类ListNodeVirtual。这个父类不做任何实质性的工作,它仅用于保存相邻节点的引用。

image

以上两个类是针对链表节点的双向链表本身也需要做成一个类:DoubleLinkedList类,这个类中封装有链表的属性和方法。比如它需要一个指向链表表头的引用,需要有为链表添加删除数据的方法,为遍历链表中的数据,还需要有一个迭代器……

作为演示,我只实现了链表的几个简单功能。演示程序工程结构如下:

image

ListNode的成员变量包括一个数据,和两个指向前后节点的引用:

image

DoubleLinkedList类的成员变量包括指向链表头节点的引用,迭代器指向的节点的引用,并记录了链表长度

image

下面看一下链表中几个主要方法是如何实现的。

首先是Append after Enumerator.vi这个方法,它是链表里最复杂的一个方法。它的输入是链表中一个新的节点,它把这个新节点添加在链表迭代器指向的那个节点的后面。

在给链表添加数据时,会遇到两种情况。首先,这个链表是一个空链表,那么被添加的节点就是这个链表的首节点,链表的迭代器也应当指向这一唯一的节点。

我设计的这个链表是一个环状链表。当链表中只有一个节点的时候,这个链表的上一个和下一个节点都是它自己。

image

如果链表不是空的,就把新节点插在迭代器指向的节点的后面。因此:

新节点的前一节点指向的应当是迭代器指向的那个节点;新节点的后一节点是迭代器指向节点原来的后一节点。迭代器指向节点的新的后一节点应当是这个新节点;原来迭代器的后一节点的前一节点也应当换成这个新的节点。最后,我把迭代器也指向了这个新的节点,这样连续添加新节点时,它们会按照先后顺序插入链表。

image

我的演示程序还用到了其它几个方法。

Reset Enumerator.vi 负责把迭代器复位,也就是指向链表的头节点:

image

Enumerator go Next.vi 用于让迭代器向后移动一个节点:

image

Enumerator Value.vi 返回迭代器指向的那个节点:

image

使用这几个方法就可以搭建出一个简单的演示程序来看一下链表如何工作了。下面这个演示程序中,分两部分:第一部分是左面那个循环,每次循环迭代就会创建出一个新的ListNode对象,它的数值是当前迭代的次数;右半部分使用链表的迭代器遍历链表中的节点。在这个演示程序中,迭代器移动次数比链表长度多了两次,因为链表是环状的,转着圈访问,链表中的头两个元素会被读出两遍。

image

程序运行后,data显示了迭代器每一步所指向的节点的值:

image

示例程序下载:https://decibel.ni.com/content/docs/DOC-16236

豆豆的小脾气

豆豆现在不是很爱哭了,他表现不高兴的方式很有些个性。

豆豆平时很喜欢跟人说话,让人陪他玩。奶奶给他扮鬼脸的时候,他可以疯的嘎嘎嘎笑。可是一旦他困了或者饿了,就马上换了一个人似的,非常安静。这时候,通常需要一个人比如爷爷抱着豆豆来回在屋里溜达。豆豆就趴在爷爷肩膀上,表情严肃,谁都不许跟他说话,更别提做鬼脸了。若是这时候有人继续试图跟他交流一下,他马上就酸起脸,准备发作。直到他吃上奶或者睡上一觉,情绪才能好过来。

昨天中午,豆豆妈喂完奶去上班,一直到六点半才下班回来。豆豆从来没饿过这么长时间,有点挺不住了。下午五点半的时候,就开始不高兴了,趴在爷爷肩膀上忍啊忍啊。但是,他一见到妈妈进门,就终于忍不住了,开始委屈的嚎啕大哭。

nextpad

https://labview.qizhen.xyz/

受LabVIEW一哥所托,宣传一下他的新产品。一哥是目前最活跃的LabVIEW中文论坛“http://bbs.gsdzone.net/”的创始人,因其高超的编程技术,而被网友亲切称为“一哥”。(不过我后来听说论坛其它创始人的称号都是三姑、二姨之类高一辈的)

一哥除了维护网站之外,也做一些商业产品。虽然索取广告费未果,但还是帮他宣传一下他的新产品吧:http://nextpad.wordpress.com/
他的产品正在推过,所以常搞有奖活动,大家就算对产品没兴趣也可以去骗个奖品哈:)

梦游了

昨晚不到10点上床睡觉,凌晨3点多的时候被豆豆的哭声吵醒,起来帮老婆给豆豆喂奶。我看了一下表,跟老婆说:“豆豆今天真乖啊,一觉睡了这么长时间。”老婆说:“豆豆12点的时候醒过一次啊,是你起来把豆豆抱给我的,你忘了?”我还真是完完全全想不起来我曾经起来过了,难道我刚才是梦游起来抱豆豆的?

豆豆这两天又学会了不少新本领。

豆豆会自己抱着奶瓶啦:
DSC01136

豆豆学会了翻身,今天自己一使劲,就趴过来了:
DSC01173

豆豆还很爱“看书”呢:
DSC01176

我昨天剃头了,结果一回家就被豆豆发现了。他一看见我的“光头”就哈哈大笑,他不知道,他自己才是真正的光头呢:D
视频:http://v.youku.com/v_playlist/f5520621o1p8.html

当当网上查看书的销量

我还是比较关心自己的书的销量的,所以是不是会跟编辑联系一下,问他卖了多少了。不过这个绝对的销量数字对于我来说意义不大,我更希望看到这本书与同类型书籍的比较结果。也许LabVIEW读者的总数并不多,但是如果我的书比其它的LabVIEW书更受欢迎我就心满意足了。

查看印刷数是一个方法,比如打开我的书能够看到它是第二次印刷,印数5000~10000,销售数量肯定也就介于这个数字之间。

更精确一点的可以去看网上书店的用户留言,对同一类书来说,买书者和留言者的比例可能差不多。但也不精确啦,如果一本书特别好或者特别不好,肯定会比那些中规中矩的书吸引到更多的留言数量。

前一阵子,发现当当网给每本书还多设置了一个参数:“喜欢人数”。我一直在琢磨,当当凭什么判定有多少用户喜欢一本书呢?我跟踪观察了一段时间,觉得最有可能的是所谓“喜欢人数”就是这本书的买家数量。这大概是最能体现销量的参数了。

image