浏览 20852 次
锁定老帖子 主题:Ruby Quiz
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2006-04-12  
刚刚在看Ruby Quiz,发现有些解法真的很巧妙

Quiz 1: MadLibs

简单的模板,假设有一句话

I had a ((an adjective)) sandwich for lunch today. It dripped all
over my ((a body part)) and ((a noun)).

程序应该拿这些((..))中间的词组问你:

Give me an adjective?
Give me a body part?
Give me a noun?

你回答以后,程序用你的答案代替相应的问题

譬如你分别回答smelly、big toe 、 bathtub

程序打印:

I had a smelly sandwich for lunch today. It dripped all
over my big toe and bathtub.

有时候你可能要重用问题,则可以给问题一个名字。例如:

Our favorite language is ((gem:a gemstone)). We think ((gem)) is
better than ((a gemstone)).

这里第一个((gem:a gemstone))给出了一个名字gem,而第二个((gem))直接用gem这个名字就可以复用第一个问题的答案了。

因此,程序只需要问你两个问题:
Give me a gemstone?
Give me a gemstone?

如果你回答
Ruby、Emerald

结果就是
Our favorite language is Ruby. We think Ruby is better than
Emerald.
   发表时间:2006-04-12  
我自己做的答案按部就班,和他书上的第二个差不多,但是看起来还是比较Java。书上给的第三个答案比较有意思,一共6行代码



1 keys=Hash.new { |h, k|
     2 puts "Give me #{k.sub(/\A([^:]+);:/, "");}:"
     3 h[$1]=$stdin.gets.chomp
4 }
5 puts "", $*[0].split(".");[0].gsub("_", " ");,
6 IO.read($*[0]);.gsub(/\(\(([^);]+);\);\);/); { keys[$1] }


实际上,第4行的}不能算有效代码,而第5行是打印一个题头,真正有效的代码是4行
0 请登录后投票
   发表时间:2006-04-12  
第5行开始的puts就是打印,它可以带n个参数,第一个参数""没啥用,第二个参数


 $*[0].split(".");[0].gsub("_", " ");

中的$*实际上就是ARGV,$*[0]就是第0个参数split(".")把文件名和后缀名分开,然后gsub替换"_"为"",例如你的文件名为read_me.txt,就变成了read me.这句话就是打印一个题头。


IO.read($*[0]);.gsub(/\(\(([^);]+);\);\);/); { keys[$1] }


首先read($*[0])就是把命令行的第0个参数作为文件名,并把它的内容读入到一个字符串。gsub是全局替换,这里有两个参数

/\(\(([^);]+);\);\);/
是它的正则表达式参数
keys[$1]
是它的block参数

意思就是用正则表达式匹配,并每次用block返回的值进行替换。

/\(\(([^);]+);\);\);/
的左边\(\(和右边\)\),分别匹配题意中的((..)),而([^)]+)则是一个group,匹配任何非)的字符,每次匹配成功((...))中间的内容变成$1,就是第一个group。

引用
keys[$1]


把$1作为关键字,去取它的值

现在看看

1keys=Hash.new { |h, k|
2      puts "Give me #{k.sub(/\A([^:]+);:/, "");}:"
3      h[$1]=$stdin.gets.chomp
4} 


Ruby的Hash构造函数有三种形式,这种方式是Hash.new block。此时,如果你用一个已经在Hash中存在的关键字去取值,那么就直接返回值,如果这个关键字不存在,那么就用hash本身和你需要的关键字调用这个block.

现在可以看看,如果没有答案的重用,例如

引用
I had a ((an adjective)) sandwich for lunch today. It dripped all
over my ((a body part)) and ((a noun)).



第一次用an adjective变成$1到keys去取值,没有找到,调用这个block,而且第2行的/\A([^:]+):/ 没有匹配,因此$1还是等于an adjective,结果就是屏幕提问
引用
Give me an adjective?

keys["an adjective"] = 你回答的答案



如果有可以重用的答案,例如:
引用
Our favorite language is ((gem:a gemstone)). We think ((gem)) is
better than ((a gemstone)).


第一次,$1="gem:a gemstone",显然无法在keys里面找到,但是第2行的/\A([^:]+):/匹配gem:,并且把$1="gem",因此,屏幕提示

引用
Give me a gemstone?
keys["gem"] = 你回答的答案


第二次,$1="gem",我们上一次已经在keys这个hashmap里面有了这个值,因此整个block就不执行,屏幕不需要提示,答案也可以重用了
0 请登录后投票
   发表时间:2006-04-12  
这本书确实很不错
很多问题只需跳一跳自己也可以解
然后再看看解答
真的爽快

有时候读源码相对高度就会比较高
这个就比较好
0 请登录后投票
   发表时间:2006-04-12  
我现在也在翻这本书,感觉真的不错。相对而言,算是比较高阶的了!很多的Quiz,在自己实际的implement以后,才发现别人的更高,实现的更加优雅!
也知道了自己的差距又多远...
0 请登录后投票
   发表时间:2006-04-12  
怎么感觉又想起了perl.
问题是,代码写成这样,简洁是简洁了,但是一般人猛一看,还真不知道它是干嘛的.想改个东西也不好下手.
0 请登录后投票
   发表时间:2006-04-12  
可能是$*和正则表达式给搞的吧


这个解答从某些方面来说并不是很好,譬如可读性方面,可重用性方面,只不过设计巧妙,充分利用了Block的优势
0 请登录后投票
   发表时间:2006-04-12  
我同事的Javascript实现

<script>
var str = "I had a ((gem:an adjective);); sandwich for lunch today. It dripped all over my ((gem);); and ((a noun););. ";
/**** Start ****/
var h = {};// it's a Hash
document.write(str.replace(/\(\(([^);]+);\);\);/ig,function($1,$2);{
	var k = $2.split(":");[0],o=$2.split(":");[1]||k;
	return "<b style='color:red'>"+ (h[k]||(h[k]=prompt("Give me "+o,"");););+"</b>";
}););;
</script>
0 请登录后投票
   发表时间:2006-04-13  
有没有类似的quiz书,在关于好的OOP方面的书推荐一下?
0 请登录后投票
   发表时间:2006-04-13  
脚本语言可以写出十分灵活的代码。可它比较难懂
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics