AutoIt教程:9. 问题、方案与实现

by xiaolai on 2010/05/03 · 19 comments

in Auto-It

第7节里提到过的问题,实际上在《把时间当作朋友》一书中出现过,作为反驳“成功学谬误”的例子

除了“以偏概全”、“单向成立”之外,“成功学”书籍里甚至不乏赤裸裸的、厚颜无耻逻辑混乱。最令我印象深刻的赤裸裸地逻辑混乱的“成功学”例子是关于“态度改变一切”的一个例子。我知道,也相信“有些时候,仅仅态度上的改变就真的可以带来不同的结果”。然而,我认为用以下的逻辑让我接受,或者让任何人接受,都不仅是不可能的,甚至是不道德的。

如果将字母 A 到 Z 分别编上 1 到 26 的分数,(A=1,B=2…,Z=26) ,那么:

  • 你的知识(KNOWLEDGE)得到 96 分(11+14+15+23+12+5+4+7+5=96)
  • 你的努力(HARDWORK)也只得到 98 分(8+1+18+4+23+15+18+11=98)
  • 你的态度(ATTITUDE)才是左右你生命的全部,因为1+20+20+9+20+21+4+5=100

拜托,有点基础逻辑好不好呢?事实上,用这种方法计算,结果等于100的单词多的去了:

alienation (疏远)
apoplectic (中风患者)
boycott (联合抵制)
cacophony (杂音、刺耳的音调)
chimpanzee (黑猩猩)
connivance (纵容)
coyness (羞怯)
flurry (慌张)
frisson (颤抖)
impotence (阳痿)
inflation (通货膨胀)
pussy (小猫<俚>女阴/阴户)
socialism (社会主义)
status (身份、地位)
stress (压力)
surcharge (超载, 追加罚款, 额外费)
syndicate (财团)
tuppence (微不足道的东西)
turkey (火鸡,无用的东西)
Wednesday (星期三)
wholesale (批发)

以上罗列的只是我在BNC英文词汇表中找到的一千多个中的一小部分名词而已。对了,形容词useless如果按照这种算法,结果也等于100。补记:刚刚写完,顺手在GOOGLE上搜索了一下 “KNOWLEDGE 11+14+15+23+12+5+4+7+5=96″。得到的结果特别值得回味:前150项几乎都来自中文网站。搜索结果的第16页才开始陆续有一些英文网站提到这个莫名其妙的算法,在GOOGLE搜索结果的第151项 看到这么一篇:指出 bullshit = 103,于是接着说,“So, it stands to reason that hard work and knowledge will get you close, attitude will get you there, but bullshit will put you over the top.” 还有更逗的,作者又接着指出另外一个词:asskissing=127

当时我就想,要是有很多莫名其妙的词按这种算法加起来也等于100呢?我猜肯定有的!只不过,用手工计算是(几乎)不可能的事情。还好,我有工具[1] 和技能,所以,就可以去验证,而后“精彩而又直接地”驳斥这种荒谬的说法。

有些时候我们非常依赖工具和技能。在心存狐疑之时,拥有工具掌握技能的人,可以去验证;而没有工具没有技能的人,只能“心有余力不足”。事实上,大多数“力不足”的人“心并无余”,他们甚至不会“心存狐疑”,而是直接“被震惊”、“被惊喜”,“被打动”……直接被这种说法带到坑里去了……

问题是:“有哪些词按这种算法也等于100?”──而解决方案倒也简单:

  • 找一个词表(如有必要,就把它转换为文本文件,便于程序阅读)
  • 逐一计算
  • 把那些按这种算法计算等于100的词挑出来

刚开始的时候,其实没必要一定要先去找个词表,我只新建了名称为一个“wordlist.txt”的文件,其中只有以下几行内容:

Hardwork
Attitude
Knowledge
Success
Anything

而后我们就可以写这样的代码处理这个文本文件中的每一行:

$file = FileOpen("wordlist.txt", 0)
While 1
	$line = FileReadLine($file)
	If @error = -1 Then ExitLoop
	; test if sum of character values in this word equals to 100, if so, print it to the output...
WEnd
FileClose($file)

注意,在AutoIt代码中,分号“;”后面的内容是“注释”,是给代码阅读者看的,AutoIt在执行代码的时候,忽略一切“;”和它后面的内容。注释的写法有若干种,请参阅帮助文档。

现在的问题是,如何才能计算出一个单词按这种算法等于多少呢?我到AutoIt的帮助文件里翻了翻与字符串(String)相关的函数,看看有没有能够用得上的……当然有,我找到了个StringSplit()。它会把一个字符串按照指定的“分隔符”拆分成若干段,而后把这些拆分后的片段按顺序保存在一个数组之中返回……如果分隔符是空字符(即,“”),那么它就把一个字符串拆分成一个字母一个片段……就是它了!

大致看了一下帮助文档,而后写了个代码片段测试这个函数:

#Include
_ArrayDisplay(StringSplit("Success", ""))

结果竟然出错了……仔细看了看出错提示,才发现_ArrayDisplay()这个函数第一个参数标明了是ByRef的,即,只接受变量作为参数,不接受表达式或函数调用。所以,改了改测试代码:

#Include
$a = StringSplit("Success", "")
_ArrayDisplay($a)

运行结果如下:

看懂了,原来StringSplit()返回的是一个这样的数组:$a[0]之中保存的是“分割后片段的个数”(即,Success这个字符串总计7个字母),而后的元素则为每一个字母……

现在的问题是,如何让a=1,b=2… c=26呢?

在AutoIt帮助文件的Variables and Conversion章节中,找到一个函数Asc()。这个函数返回一个字符对应的ASCII代码。例如:

Asc(“0″) == 48, Asc(“9″) == 57, Asc(“A”) == 65, Asc(“Z”) == 90, Asc(“a”) == 97, Asc(“z”) == 122

所以,Asc(“a”) == 97 、 Asc(“b”) == 98 … 于是,只要用这个函数的返回值再减去96就可以得到1、2、…26这些数值了。不过,还有个问题,大小写字母的值不一样,Asc(“A”) == 65,而Asc(“a”) == 97……再去看看跟String相关的函数,找到一个StringLower()──我们可以先用它把一个词中的字符全部转换为小写,而后再进行计算……

现在我们可以写出(或起码能够完全理解)以下的代码了:

$file = FileOpen("wordlist.txt", 0)
While 1
	$line = FileReadLine($file)
	If @error = -1 Then ExitLoop
	; test if sum of character values in this word equals to 100, if so, print it to the output...
	$wordLowerCase = StringLower($line)
	$Characters = StringSplit($wordLowerCase, "")
	$sum = 0
	For $i = 1 To $characters[0]
		$sum = $sum + Asc($characters[$i]) - 96
	Next
	MsgBox(0, $line, $sum)
WEnd

仔细看看,就会发现“计算总和”的过程是可以被独立出来的,那还不如把它写成一个函数,将来可以随意调用呢:

$file = FileOpen("wordlist.txt", 0)
While 1
	$line = FileReadLine($file)
	If @error = -1 Then ExitLoop
	; test if sum of character values in this word equals to 100, if so, print it to the output...
	$sum = AddUpCharactersInWordAsNumber($line)
	MsgBox(0, $line, $sum)
WEnd
 
Func AddUpCharactersInWordAsNumber($word)
	$wordLowerCase = StringLower($word)
	$Characters = StringSplit($wordLowerCase, "")
	$count = 0
	For $i = 1 To $Characters[0]
		$count = $count + Asc($Characters[$i]) - 96
	Next
	Return $count
EndFunc

如此这般之后,我们可以去找一个很全的英文词汇表去测试了。我到网上Google了一下“BNC Frequency Word List”,很快在kilgarriff.co.uk这个网站上找到一个词汇表,“lemma.al”。网站上有这个词表的格式说明:

The format for the list is:

        sort-order, frequency, word, word-class

and a sample from the top of the alphabetically-ordered list is:

	5 2186369 a det
	2107 4249 abandon v
	5204 1110 abbey n
	966 10468 ability n
	321 30454 able a

这个比较好办,就用我们刚刚用过的StringSplit()函数,用一个空格(” “)作为分隔符,就能把每一行中的“word”提取出来,然后在调用我们刚写过的AddUpCharactersInWordAsNumber()函数就可以算出数值……

然后,程序就变成了这个样子:

InetGet("http://www.kilgarriff.co.uk/BNClists/lemma.al", "lemma.al", 0)
 
$file = FileOpen("lemma.al", 0)
While 1
	$line = FileReadLine($file)
	If @error = -1 Then ExitLoop
	$lineElements = StringSplit($line, " ")
	If AddUpCharactersInWordAsNumber($lineElements[3]) == 100 Then
		ConsoleWrite($lineElements[3] & @CRLF)
	EndIf
WEnd
FileClose($file)
 
MsgBox(0, "", "Finished!")
 
Func AddUpCharactersInWordAsNumber($word)
	$wordLowerCase = StringLower($word)
	$Characters = StringSplit($wordLowerCase, "")
	$count = 0
	For $i = 1 To $Characters[0]
		$count = $count + Asc($Characters[$i]) - 96
	Next
	Return $count
EndFunc

如果你想把结果都写到一个文本文件之中,可以把第9行的

ConsoleWrite($lineElements[3] & @CRLF)

改成

FileWriteLine("result.txt", $lineElements[3])

FileWriteLine()这个函数的用法,当然要参见帮助文档。

作业

  • 把程序里的StringLower()函数换成StringUpper(),并修改其它相应的部分,使其得出正常结果。
  • 这里有个更长的词表:http://www.kilgarriff.co.uk/BNClists/all.al.gz,下载后解压缩,看看这个词表中有哪些词按这种算法等于100?
  • 这里还有一个词表:http://www.bckelk.ukfsn.org/words/wlist.zip,下载后解压缩,看看这个词表中有哪些词按这种算法等于100?
  • 回顾一下之前的所有文章,确保每一行代码都读得懂……

Footnotes:

  1. 当时我用的是Python,现在是在讲解AutoIt,所以当然得用AutoIt重写一遍。 []

{ 19 comments… read them below or add one }

smy20011 May 3, 2010 at 12:19

BEGIN {
for (i=0;i<255;++i) a[sprintf("%c",i)]=i
}

{
$3 = tolower($3)
c = 0
for (i = 1; i <= length($3) ; ++i) c+=a[substr($3,i,1)] – a["a"] + 1
if (c == 100) print $3
}
Awk代码,见效了

Reply

smy20011 May 3, 2010 at 12:22

TAT,应该是见笑了..输入法

Reply

lwzz May 3, 2010 at 12:47

这段代码让我想起了貌似是和菜头说的笑话,“这段代码1年前只有我和上帝能看懂,现在只有上帝能看懂。”
笑来的变量名起得通俗易懂。变量名不怕起长了,就怕乱起看起来特痛苦。

Reply

smy20011 May 3, 2010 at 12:57

嘛,我只写过OI的程序..so,悲剧

Reply

lwzz May 3, 2010 at 13:15

oi是什么?话说$3这种命名方式让我想起汇编里的地址“立即数”。最常见的就是入栈 subl $4 %esp
不过我是学越南语的文科生,不懂的……

smy20011 May 4, 2010 at 15:27

OI是指计算机奥林匹克竞赛,经常是大约100行左右的程序,因此没有什么变量命名的限制。至于$3这个变量名,是指文件按行读入,以空格分界的第三个域。awk中自动做Split 详细的见http://www.linux.gov.cn/shell/awk.htm

Reply

lwzz May 3, 2010 at 12:39

我说我猜了一个早上都看不懂那个 =100是代表什么类型的单词呢.原来有典故。

不过一次性读取效率应该更高些,I/O的操作是挺贵的。比如说用python的时候喜欢用readerlines代替readerline逐次读取,然后再遍历readerlines集合。输出的时候也是如此,喜欢组成个hashtable然后一次性输出。
看了下帮助好像autoit没有类似的函数库?

话说回来,脚本开发真的能提高很多开发效率。

Reply

李笑来 May 3, 2010 at 14:27

UDF里有个_FileReadToArray()

Reply

李笑来 May 3, 2010 at 14:28

另,我认识个高手,叫韩磊……韩总大学的时候好像也是文科生学越南语的……

Reply

lwzz May 3, 2010 at 22:36

恩,几年前在某篇孟岩和韩磊谈面向对象的文章就已知道有此高手。
我也foxpro入门的,毕业后干了一年多越南语翻译。后来辞职在家边学英语边学编程。开始学了些c#皮毛,接着很认真的研究了javascript一段时间,近来倒着回去夯实基础,由C语言作为媒介,离散数学,数据结构,x0x86汇编。现在再看其他语言已经有了不同理解,csapp真的是一本很好的教材。
外语和编程有很多的共通点。

Reply

AthensBird May 4, 2010 at 00:42

外语是文科,大学期间的外语专业应该不上数学课了吧,编程可是理工科的东西,你怎么应付的过来呢?

AthensBird May 4, 2010 at 00:46

我也是文科专业毕业的,可是对编程有兴趣,发现有人走过这条路,不禁发问,但愿能从你那里得到稍许经验。

风吹无声 May 3, 2010 at 20:01

笑来老师是怎么查字典的?我怀疑我会成为您说的一辈子不会查字典的人。我一般读完文章,记下不认识的,再一个个查,带入文中匹配词意,然后在小本子记下词义,有经典例句的话再记下,没事讼读。我还把词分成三类,第一类是考试词汇,第二类是我觉得词义很反常的词汇,第三类是平时少见词汇。

Reply

李笑来 May 3, 2010 at 21:48

你这样已经是很仔细的了,加油!

Reply

googya May 4, 2010 at 10:47

偶像,绝对的偶像!笑来!

Reply

brooklyn May 5, 2010 at 01:19

我一般都用python的,不过看了老师这个,感觉AutoIt也很强大的啊。

Reply

AF September 12, 2010 at 20:56

If AddUpCharactersInWordAsNumber($lineElements[3]) == 100
这个地方为什么传过去的参数是$lineElements[3]),而不是$lineElements[1]呢??

Reply

funyi October 9, 2011 at 11:12

看的时候也疑惑了一下,仔细看看,恍然。原来李老师程序中使用 Stringspilt时候 分隔参照是“ ”(空格) 即使用空格分隔网上得到的“单词词频表”。所以第三个分隔出来的元素就是单词。

Reply

free erotic movies March 3, 2011 at 20:41

oxjn drvaz lobstertube awjexu u vx c egi

jcqu axbvj [URL=http://www.zackspornlinks.com – porno[/URL – nysjes p pr b wei

Reply

Leave a Comment

Previous post:

Next post: