标题:给各位狐友的马年大礼
只看楼主
taifu945
Rank: 12Rank: 12Rank: 12
等 级:贵宾
威 望:80
帖 子:1545
专家分:3298
注 册:2012-7-6
得分:0 
以下是引用liuxingang28在2014-2-14 17:00:48的发言:

闲暇之余,花了二天时间认真阅读了该书。总体来说,该书还不错,读后有不少收获,但也发现了几处不足或错误,如下:

1. 第15页,作者建议“字符型数据全部使用Varchar型,这样可避免再使用Alltrim()函数消除多余的尾部空格”。根据本人的开发体验,在VFP下Varchar型并不比Character有优势。一是Varchar并不比Character节省空间,二是Varchar仍然会存储手工录入的空格。VFP提供该字段类型只是为了兼容SQL Server。

2. 第22页,作者建议在命令窗口中输入较长命令时,在行尾输入分号号后按Ctrl_Enter来换行,不能直接按回车。但经上机验证,直接回车也可以。

3. 第38页,在介绍REPLACE命令时,作者认为“有 FOR/WHILE短语时,默认范围是ALL”,应该纠正为:有FOR短语时,默认范围是ALL,有WHILE短语时默认范围是REST,同时有FOR短语和WHILE短语时,默认范围是REST,即WHILE优先。

4. 第42页例3,“skip 记录号-RECNO()”有错误。比如,当前记录号是8,要切换到记录号3时,skip 3-8 则将记录切换到了第2条记录,显然错误。应该修改为:
    go 记录号
    if dele()
        skip
        if eof() and !bof()
            skip -1
        endif
    endif

5. 第74页,关于掩码“9”的作用,作者表述为“只允许输入数字和+-号”,应纠正为“对字符号字段只允许输入数字”,对于数值型字段只允许输入数字和+-号”。

6. 第111页例3,其中的“having avg(英语)>=85”改为“having 英语平均分>=85”更好一些

7. 第142页,其中的“where ... and 成绩=(select ...)”改为“where ... and 成绩 in (select ...)”更好一些,因为若子查询若回多条记录时,程序会出错,使用 in 则不会。

8. SQL的例子中,一个查询语句中出现多个子查询,甚至出现重复的子查询,使程序的可读性很差。其实,在编写SQL语句时,先将子查询输出到一个游标,再对游标进行操作,有时也很好,特别是需要重复引用子查询中的记录时。

9. 第168页第2行,在介绍Between函数时,作者表述为“该函数不区分大小写”,这个表述是错误的。只所以between('B','a','C')返回.f.,是因为Set Collate to "PinYin"引起的,输入Set Collate to "Machine"后,返回.t.。在PinYin排序方式下,不是按字符的Ascii码值进行比较的,而是按其拼音的先后比较,这可能与我们的习惯不同。

10.第196页倒数第6行,在介绍EndScan时,作者专门强调“在Endscan前切换到原工作区”。其实,根本没有必要,在执行Endscan时系统会自动切换到原工作区。

11.第208页倒数第7行中的“skip -reccount()”语句令人费解,应该改为“go top”更容易理解。

12.第247页,第1行,将“THISFORM.backcolor.value=255+255*256+0*65536”改为“thisform.backcolor.value=rgb(255,255,0)”更简单。

13.第268页,在记录导航条的代码中有大量的重复代码,使用算定义方法可能更好。

14.第283页,将Dakai.Click中使用低级文件函数读出文本文件内容的语句改为:THISFORM.源文本区.Value = FileToStr(文件名)。

15.第293页第4行开始的代码段有问题:若备选列表允许多选,则其中的“THISFORM.已选.additem(this.value)”应改为“THISFORM.已选.additem(this.list(i))”;若备选列表不允许多选,则仅需先清除已选列表,再执行“THISFORM.已选.additem(this.value)”一条语句即可。

……
还有很多,由于时间的关系我要下班回家了!
非常感谢你的意见。之所以回复你比较晚,是因为再次对你提出的质疑进行了一一验证,现答复如下:
1、Varchar对于Character的优势并不在于节省空间(这点我在书中也提到了,并介绍了该类型存储的原理),而在于编程时的便捷性,不用考虑字符型的后面是否填充有空格。我个人认为,用可变长字符串的优势在于编程时可少一层考虑,不用担心因为字符串后面无谓的空格而影响程序走向;
2、确实如你所说,直接按回车也可以;
3、经我再次验证:REPLACE...WHILE的默认范围确为REST;同时存在FOR,WHILE两个短语时,以WHILE优先;
4、这个例子经我再一次反复测试,结论与之前一样。你可以打开一个表文件,定位到第8条记录,再用SKIP 3-8,看看是否能回到第3条记录(第3-8条记录没有被删除时)。另外,我这个例子是为了展示如何让指针不误指到已被删除的记录上,不是为了展示SKIP可以比GO更精确地定位记录。所以,这个例子是放在删除记录的小节里介绍的;
5、正如你所说,对于C、V型字段,只允许输入数字;数值型或其兼容型字段允许输入数字和正负号;
6、两种方法是一样的,我个人认为习惯哪种就用哪种;
7、子查询的输出项用了聚集函数MAX(),在没有分组的情况下只可能输出一个数值,不可能有第二个。你可以再仔细斟酌一下,是不是这样;
8、你说得不错,我也是这样认为的。但在不影响可读性的情况下,适当使用子查询也不是不可以。这点,在我子查询介绍的第6点“关于子查询的小结”中已经有指出;
9、经你提醒,我又反复实验了一下:在SET COLLATE TO 'pinyin'/'Stroke'时,BETWEEN()函数不区分大小写,而在SET COLLATE TO 'machine'时区分大小写;
10、确如你所说,ENDSCAN会自动识别配套的SCAN属于哪个表文件;
11、与第6点一样,两种方法都可以的,看个人习惯,我个人习惯用SKIP。还有,这段程序可以用上面黄色背景的一条UPDATE-SQL搞定,写这段代码只是为了对比SQL命令和普通命令;
12、对于RGB()函数,后文已有介绍;
13、不知道你所说的“算定义方法”是什么。这个例子我在发布前也经多次测试,如果四个记录导航按钮不重复写这些代码,光靠一句表单刷新,是不会改变表单显示内容的。这一点,我刚刚又重新测试了一下,确实应该“重复”这些代码;
14、用FILETOSTR()函数读入文本文件确实比低级函数方便。由于以前没用过该函数,因此写本书的时候没想到它;
15、你说的那个例子是单选项,不多选,确实可以按你说的方式改进。
最后,再一次感谢你指出这么多问题,尽管有些地方值得再商榷,但还是能看出你这个论坛新人的实力。而且注册了ID后第一帖就给我,太感谢了,希望以后能在论坛中多多见到你的身影,更希望你在百忙之余继续为本书“找茬”。另外,我会根据你提出的建议修正书中一些不妥的内容,然后再次发布出来,让更多的人用好这款经典的桌面数据库软件。
2014-02-14 22:11
tlliqi
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:204
帖 子:15453
专家分:65956
注 册:2006-4-27
得分:0 
以下是引用liuxingang28在2014-2-14 17:00:48的发言:

闲暇之余,花了二天时间认真阅读了该书。总体来说,该书还不错,读后有不少收获,但也发现了几处不足或错误,如下:

1. 第15页,作者建议“字符型数据全部使用Varchar型,这样可避免再使用Alltrim()函数消除多余的尾部空格”。根据本人的开发体验,在VFP下Varchar型并不比Character有优势。一是Varchar并不比Character节省空间,二是Varchar仍然会存储手工录入的空格。VFP提供该字段类型只是为了兼容SQL Server。

……
还有很多,由于时间的关系我要下班回家了!
谢谢你的建议
2014-02-14 22:24
qingfameng
Rank: 12Rank: 12Rank: 12
等 级:贵宾
威 望:35
帖 子:964
专家分:3019
注 册:2010-2-6
得分:0 
...
2014-02-15 10:33
b土木丁口
Rank: 3Rank: 3
等 级:论坛游侠
帖 子:264
专家分:189
注 册:2013-9-12
得分:0 
赞同!
2014-02-15 10:57
liuxingang28
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:山东济南
等 级:贵宾
威 望:47
帖 子:649
专家分:2156
注 册:2014-2-7
得分:0 
回复 21楼 taifu945
1. 关于第4个问题,文中的代码确实有问题,之所以我在举例中将当前记录指定为8,目标记录指定为3,是因为按照书中的例子,第5条记录被删除了,并且Set dele on,所以书中的代码肯定是有问题的。

2. 关于第6个问题,我要说明的是:为计算字段指定别名是为了方便引用,既然为 AVG(英语)指定了别名,那么在HAVING语句中使用别名应该更好一些。

3. 关于第7个问题,我要表述的意思是:用 IN 代替 = 也可以,且适用范围更宽。在我看过的资料中,很少见到用等号连接子查询的用法。

4. 关于第13个问题,我要表述的意思是:将按钮 Click 事件代码中的:“THISFORM.XH_Label.Caption = ……”放到表单的自定义方法中,如:THISFORM.ShowData 代码更简洁,原文并无错误。

泉城飞狐
2014-02-18 11:24
taifu945
Rank: 12Rank: 12Rank: 12
等 级:贵宾
威 望:80
帖 子:1545
专家分:3298
注 册:2012-7-6
得分:0 
以下是引用liuxingang28在2014-2-18 11:24:46的发言:

1. 关于第4个问题,文中的代码确实有问题,之所以我在举例中将当前记录指定为8,目标记录指定为3,是因为按照书中的例子,第5条记录被删除了,并且Set dele on,所以书中的代码肯定是有问题的。

2. 关于第6个问题,我要说明的是:为计算字段指定别名是为了方便引用,既然为 AVG(英语)指定了别名,那么在HAVING语句中使用别名应该更好一些。

3. 关于第7个问题,我要表述的意思是:用 IN 代替 = 也可以,且适用范围更宽。在我看过的资料中,很少见到用等号连接子查询的用法。

4. 关于第13个问题,我要表述的意思是:将按钮 Click 事件代码中的:“THISFORM.XH_Label.Caption = ……”放到表单的自定义方法中,如:THISFORM.ShowData 代码更简洁,原文并无错误。

1、你说的情况确实存在,但请注意一点:该例子是为了说明当指针指向被删除记录时的处理情形。你说的跳转到3号记录是未被删除的记录,这种情况当然要另外处理。之所以本例代码不作处理,是因为这段代码与本例要说明的主题无关;
2、我说了,这是一个个人习惯问题,无所谓哪个更好。为“AVG(英语)”指定别名,在我看来,只是为了输出显示时栏目名称更一目了然。我并没有强调大家必须用这种方式,每个人可以根据自己的爱好和习惯来写代码;就象我在书中强调写代码的规范性,但你看本论坛上很多人贴上来的代码不也是五花八门的么?
3、既然子查询只可能输出一个数值,那用等号和用IN有什么区别呢?对于“=ANY”这种情况,我向来是用IN的,因为IN比=ANY更好理解。=表示等于,IN表示包含,等于通常用在一对一、多对一比较,而包含通常用在一对多、多对多比较。对于本例来说,属于多对一比较,更强调“等于”,而不是“包含”。我见过很多范例中,类似的情况(与一个定值进行比较相等)都用等号的;
4、本书面向入门者(书名就定义为《入门手册》),所以我采用的代码都力求新手能尽快理解,自定义方法显然对刚入门的人来说有一定难度。已经达到一定水平的用户,可以按自己的方法去重新改写代码。案例只是一种参考,并不是一种模板。
2014-02-18 14:07
liuxingang28
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:山东济南
等 级:贵宾
威 望:47
帖 子:649
专家分:2156
注 册:2014-2-7
得分:0 
《Foxpro入门手册》中的几处不足或错误 (二)
今日有空闲,因此将上次未指出的错误或不足列出如下:

1. 第24页,作者使用了“Replace A.ACC WITH '新值2',试验.AAC WITH 3000”语句,在本例中由于当前工作区就是别名“A”和“试验”所在的工作区,因此该语句可正常执行。但是,这样的用法非常危险,极易出错,不建议采用该格式。若要更新其他工作区中的字段,应采用“Replace 字段 with 新值 in 别名”的形式。若采用“replace 别名.字段 with 新值”的格式,需保证当前工作区有表打开,且EOF()=.f.,否则报错或不执行替换。

2. 第37页,作者认为Browse命令无 Title 和 Valid 选项。经验证 Browse 也有 Title 和 Valid 选项。

3. 第79页,用命令行方式设置表的长表名和注释时,作者建议读者参考第20页 Create Table -SQL 用法。需要指出的是,Create Table 只有在新建表时可设置长表名,且无法修改已有表的长表名,更无法设置表注释。修改长表名的命令是:RENAME TABLE 原长表名 TO 新长表名,修改表注释的命令是:DBSETPROP('表名','TABLE','COMMENT',新注释)

4. 第208-209页,不使用SQL查找单价>=100的最低价的程序代码令人费解。使用下面一条语句即可搞定:Calculate min(单价) to 最低单价 For 单价>=100。再次指出,从209页的解释中可以看出,作者宁愿使用 Skip -reccount(),而不使用 Go top 的原因可能是认为 go top是记录指针的绝对移动,有可能会移动到被删除的记录上。这种想法是多余的,go top 时仍会考虑set delete 以及 set filter 等系统状态。

5. 第253页,在Form1上添加Shape时,先将Form1上的按钮移到Form2,等添加完Shape后再从Form2移回。其实根本没有必要这么做,只需选定Shape后,单击布局工具栏上的“Send to back”按钮即可。

6. 第295页,在扩充数组元素前,作者先将原数组复制到一个临时数组,重新定义数组后再复制回来。在301页,作者认为“数组一经重新定义,所有数据都会初始化为.f.”。这种想法是错误的。重新定义数组后(扩大),原数据仍会保留,新元素初始值为.f.。因此,第294-295页的代码可精简为:

* 保留ShowItem一个数组
itemselect = 0
for i = 1 to this.listcount
    if this.selected[i]
        itemselect = itemselect + 1
        public showitem[itemselect]
        showitem[itemselect] = this.list(3)
    endif
next
with thisform.已选
    .rowsourcetype = 5
    .rowsource = 'showitem'
endwith

7. 第323-324页,作者认为命令按钮组和选项按钮组的常用事件是Click,而本人认为其常用事件应该是InteractiveChange。当单击命令按钮组的边界或空白时,仍会触发命令按钮组的Click事件,其Value值是上一次单击的按钮值,这与我们的习惯不符。一般认为,单击非按钮区域时,应该不执行任何操作。因此,应将事件代码放在InteractiveChange。

8. 第458页,VFP 9.0 SP2 的最新版本是“7423”而不是“7432”

特别声明:给作者的书挑毛病,并不代表本人的水平比作者高。可以看出,书中的大部分内容为原创,且作者能够将自己的原创作品放在论坛上供大家免费下载令人感动,值得尊敬。

[ 本帖最后由 liuxingang28 于 2014-2-20 15:03 编辑 ]

泉城飞狐
2014-02-20 14:14
taifu945
Rank: 12Rank: 12Rank: 12
等 级:贵宾
威 望:80
帖 子:1545
专家分:3298
注 册:2012-7-6
得分:0 
1、那条命令只是为了说明别名的用法,不是建议大家这么用。我在后面的举例中从未有这么用的;
2、我在介绍很多命令时只是挑比较常用的选项进行介绍,并不是每条命令都介绍所有的选项。TITLE和VALID选项在EDIT中我也作了介绍;
3、你也说了,是“设置”表文件的长名,不是修改,我在本书中没涉及到如何修改表的长名和注释。参考手册不是字典,不可能介绍全面。事实上,教材都不会把一样东西从头到尾都详细地讲述完整;
4、我说了,这是个人习惯问题,请你不要把你的理念强加给别人,我也从未在书里说过只能这样用。再次说明,例子是一种参考,不是一种模板。你可以用GO TOP,但不要去阻止别人用SKIP,除非代码执行的结果有错误。还有,我当初写这本书时有一个宗旨,就是力争在保持代码简洁有效的情况下,多展示几种代码的实现方式,让读者根据自身情况去选择。于是,就有了GO TOP用SKIP -RECCOUNT()代替的写法;
5、第256-257页添加形状确实可以点“布局”工具箱中的“置后”钮进行排列;
6、对于你提出的数组扩容可以保留原数据的说法,我又做了一遍实验(其实写本例时我已多次实验)。依次执行以下命令:
public abc(3)
abc(1)=[row1]
abc(2)=[row2]
abc(3)=[row3]
list memory like abc
public abc(4)
list memory like abc
然后你看截图吧(当然,这也可能是我们所用版本不同造成的),因此,至少在我机器上运行这个例子是一定要借用临时数组的;

7、对于这个问题,我觉得是你对命令按钮及命令按钮组的CLICK事件执行关系没理解透彻。首先,命令按钮组和其中的每个按钮都有CLICK事件;其次,两种CLICK事件的执行关系是:如果用户点击的按钮有自己的Click事件代码,则执行按钮本身的;若没有,就执行按钮组的Click事件代码。根据以上两点,如果你用鼠标点击命令按钮中的空档或边界,触发的是组的CLICK事件,而不是按钮的。而组的CLICK事件是空的,所以,即便执行也不会做任何事,对程序一点影响也没有。若你有兴趣的话,可以建一个表单,用调试程序一步步跟进,看看是不是如此;
8、第461页中,确实手误,把7423写成了7432。

感谢你的批评和指正,虽然有些地方我们观点不同,但还是很钦佩你仔细入微地提出意见,让本书得以有质的飞越。感谢钦佩之余,给你一个提醒:看案例代码需要结合上下文来看,并理解好案例所反映的主题。最后,再次由衷感谢,希望能在论坛中常常见到你的身影。
2014-02-20 16:56
liuxingang28
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:山东济南
等 级:贵宾
威 望:47
帖 子:649
专家分:2156
注 册:2014-2-7
得分:0 
以下是引用taifu945在2014-2-20 16:56:57的发言:

……
6、对于你提出的数组扩容可以保留原数据的说法,我又做了一遍实验(其实写本例时我已多次实验)。依次执行以下命令:
public abc(3)
abc(1)=[row1]
abc(2)=[row2]
abc(3)=[row3]
list memory like abc
public abc(4)
list memory like abc
然后你看截图吧(当然,这也可能是我们所用版本不同造成的),因此,至少在我机器上运行这个例子是一定要借用临时数组的;
……

我用的版本的VFP 9.0 SP2 7423 英文版。不建议用户采用汉化版,汉化版有很多问题。

以下是帮助文件中关于 DIMENSION 命令的解释:

If the number of elements in an array is increased, the contents of all the elements in the original array are copied to the newly redimensioned array. The additional array elements are initialized to False (.F.).
译文:如果数组中元素的数目增加了,就将原数组中所有元素内容复制到维数重新调整过的数组中,增加的数组元素初始化为“假”(.F.)。



[ 本帖最后由 liuxingang28 于 2014-2-21 10:01 编辑 ]

泉城飞狐
2014-02-21 09:01
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
得分:0 
哪來的7423,微軟官方下載的就是5815。

授人以渔,不授人以鱼。
2014-02-21 09:29



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-427048-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.016346 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved