牛人与弱手的区别,不是线性的,而是指数的 及他们之间的道路
牛人与弱手的区别,不是线性的,而是指数的 及他们之间的道路1.误操作今天傍晚,我删除了整个下午的工作成果。误操作。执行的是那个著名的指令
"rm * -rf"。我发了会呆,再三确认是这个目录,然后手悬在键盘上不知道该敲些什么。我跑
到GOOGLE,没有解决方案。牛同学说,能不能恢复啊?我说:不能,十好几年前我就知道不能恢复。这命令太著名,且令人印象深刻了。上网查一下,只是安慰一下自己。恩,我并
不孤独,傻子真多啊。有多少人有这样的经历呢,先是对着心爱的人喊"我再也不想见到你",当如愿以
偿的时候,开始对着空虚哭泣。rm是remove,-f是不必再问一次,-r是递归。递归的意思是,把目录下的目录下
的目录下的...东西,都照此处理。类似于,我烧掉你的信,烧掉提到你的信的
日记,烧掉提到日记的作文,烧掉提到作文的...我想你明白了。大半夜的,刚又调了半宿,我可不是跑这伤感来了。当时,我对着屏幕发了一会
呆,然后对张宇同学说:你回家吧,今天的观摩到此结束。最重要的内容你刚刚
学到了,今天学不着啥了。然后,我开始键入,跟张健一边回忆下午的全部工作。我们花了大约1小时20分
钟,把整个下午的工作完成了。接下来的半个晚上,我证明了这1小时20分中,
大部分时间是在试图解决一个暴露出的新问题,而不是恢复下午的工作。一整个下午 对比 1小时20分,其实这正是令人悲哀的差距。1小时20分 说明,我们,具体地说就是我,一整个下午,大部分工作是在发现错
误和改正错误,而真正的工作,1小时20分钟足以完成。这就是高手与我们这样的低手的差别:我们引以为豪的一整个下午的工作,对于
不犯那些愚蠢错误的高手而言,只值1小时20分。2.效率之差高手通过避免错误节省时间,从突显出他的效率。即使犯了错误,他也能更快地
发现错误的原因,也更快地修正错误。想起ACM比赛,牛人和面人之间的差距,岂可以道里计。相信有过比赛经验的同
学深有体会。在工程中,面对牛人深感绝望的各位也一定深有同感。更可悲哀的是有一些同学尚无法看到自己与牛人的差距,动辄认为:我也能整出
来。他所看到的,是现实或者电影里牛人几乎不犯错误的流程,并投射到自己,却不
知道 不犯错误本身正是至难的事情;犯了错误能够以别人几乎无法发现的速度
发现和更正,也并非人人能够。我们把大部分时间都花费在犯错误上了。骄傲的同学认为:其实再给我一点时间,我也可以。时间。这个世界最吝惜给予我们的,恰恰就是时间。如果我们有足够的时间,我们就可以在战场上迂回到敌人的后方;如果我们有足
够的时间,我们就可以比对手更早地把刀切在某个部位;如果我们有足够的时
间,我们就能把这些兵那些兵都调到敌人的基地,把所有的矿全占了……所有这些愿望,只需要一个前提,那就是当我们拥有时间的时候,别人没有同样
拥有。你这为这个前提可能实现么?时间。这个世界最吝惜给予我们的,恰恰就是时间。所以,我们在战场以下用小
时计算以天计算以年月计算的时间,却换取战场上的几秒钟。我们用这些时间训练自己避免错误,也训练自己迅速改正错误。这样,在战场
上,我们就可以有更多的时间做正确的事。经常有年轻人抱怨,失声痛哭,为什么结果会是这么悲惨。为了让你自己好受一
些,请假设我在讨论的是中国男足。其实结果如此悲惨的原因非常简单而直接,
在过去所有的岁月中,你做出了这样的选择。我们与牛人能有多大的差距,在甚至不足一生的努力之后?想想你和你的小学同学现在的差别,和你的高中同学,和你的大学同学。有一些
人,如偶像YMH,对够达到令我认为 对比是没有意义的,因为永远不可能达到,
更遑论超越。据说巨牛的程序员的效率是面手程序员的10倍。我想,做出这个统计数字的人,
一定没有参观我们的教室和实验室。3.图书馆案例正确的方法与糟烂的方法效率之差能有多大?一个真实的案例,但是今天不细说。当年在图书馆导数据出来。预定的方案导出全部书目所需的时间是:200个工作日左右,每天8小时,需要一
位工作人员陪着机器。我们经过1个月还是一周的研究,最终的方案导出数据花费了:半小时,人类按
键花费了不到1分钟,然后就离开了。如果我们更牛,避免错误技术路线的尝试,这两个方案的对比就是 200天
vs. 30分钟。4.于同学的问题周三李记者讲座以后,于同学留言问到:"其实老师,我还有一个问题没有问(我怕老师们回答不了):如果一个人很热爱
代码,很喜欢计算机,但是他学的就是不好,编程就是不如别人,他还有存在的
价值吗,他如何生存(可以译为小人物的生存之道)。习编程。"其实这个问题一点也不难回答。初步的回答是:"我唱歌跑调挺严重的,但是我学习弹琴;我体质挺差的,但是我坚持做俯卧撑--我
一直在进步。結果并非总是重要的,过程也是我们所追求的;试想,人生如果忽
略过程,只看結果,还有什么意思。"只要努力总会进步,而且进步是成指数,而不是线性的。牛人,一般都是从面人
过来的,当年也都面得可笑。很难想像热爱却不能精通,如果你用一生的时候去追求。是的,由于天份所限--我们不得不承认--直到生命最后一刻,我们也没有成为巨
牛的牛人,但是你的高度已经可以被很多人仰视了。你三两岁的时候,如果像现
在这样畏难,一定会被自己当时走路和语言的能力吓得每天哇哇大哭。我知道,你当时确实是那样做的,每天哇哇大哭。现在,请别再以另一种形式继
续哭了。而且,我们的人生并非用来与人比较,这一进步的过程,也正是我们所享受的。我们终无可能踏入神的队列,但是我们从未停止努力的脚步。正是持久的努力之
路,而并非结果,令我们不同于凡俗。
2011年11月9日,我的理想
2011年11月9日,我的理想2011年11月9日,历史从这一天改写了。李记者下午讲座,问同学们,以后想干啥。王同学说:想做像杨老师那样的人。我非常激动。极其激动。当年去丹麦和Lars闲扯。他问我,你想你的学生们么?我说:想。他给我看大家
的照片,我愈加想念。Lars说你的学生都挺喜欢你的。我说:啊。L又经过了若干谈话,Lars说:做一名教师,应该能够让学生希望将来成为自己这
样的人。今天,当我听到王同学这样说的时候,我想起了Lars的话,想起了几次中丹合作
项目,回想起我们成功和失败的项目。人生不如意十有八九,今天这算是人生的一点亮色。偶而,我做教师也有成就感
的时候啦。领导和上级的评价不足为凭,学生喜欢老师扯淡吹牛讲笑话也不足为据,学生N
多年以后后悔或感谢你当年的教诲也算不了什么。但是,我的学生想成为像我一
样的人。谢谢,我很自豪。----晚上吃饭,提到风投啊,就业形势越来越好啊,什么的。好象我们已经整到了很
多钱,可以大把地花了。我说:我要把那些钱都发了。每个学生每个月四十万。每个人再发条狗,有男朋
友女朋友的都带着,每天就是去大连海边遛狗。没对象的,一人发一个对象。男
生发个女朋友,女生发个男朋友。恩,如果有男生要求男朋友的,也可以满足。考虑到中国的男女比例,我在女生要求女朋友这犹豫了一下。后来想想,也行。
轻轨的尽头
包师弟的放大镜
pics
lambda过程作为返回值的例子-分析,及一个作用域的例子
lambda过程作为返回值的例子-分析,及一个作用域的例子1. 昨天的例子的分析昨天整完lambda过程作为返回值的例子就被二猫叫走了,没来得及分析.以下是分析及另一个的例子(似乎出自SICP,记不大清了).以下混用过程和函数.: guile> (define (foo x)
: (cond ((= x 1) (lambda (y) (+ y 10)))
: ((= x 2) (lambda (y) (+ y 20)))
: (else (lambda (y) (+ y 0)))))这段代码可以理解为(define (foo x) (啥啥东西))这意味着,定义了一个过程名为foo,有一个参数.我们再看 (啥啥东西) 的里面: (cond ((= x 1) (lambda (y) (+ y 10)))
: ((= x 2) (lambda (y) (+ y 20)))
: (else (lambda (y) (+ y 0))))一个条件判断,有三种可能,每种可能都形如: ((= x 1) (lambda (y) (+ y 10)))其中 (= x 1) 是 判断条件部分,后面的 (lambda (y) (+ y 10)) 是整个表达式
的值,即foo这个过程的返回值.这个返回值是什么呢?: (lambda (y) (+ y 10))这个返回值是一个函数,匿名,有一个参数,函数体是 (+ y 10),即把这个参数
加10以后返回.整理一下.
1.foo这个函数返回值是一个函数;
2.这个作为返回值的函数,函数体是 (+ y 10).我们再测试一下.: guile> (foo 1)
: #<procedure #f (y)>意思是:foo这个函数传参数值为1,返回值是个函数,有一个参数.: guile> ((foo 1) 23)
: 33当foo的参数为1的时候,cond求值为
: ((= x 1) (lambda (y) (+ y 10)))(lambda (y) (+ y 10)) 的参数是
: guile> ((foo 1) 23)
中的23.即,把(foo 1)代换为(lambda (y) (+ y 10)),这正是对(foo 1)求值的结果.那么 ((foo 1) 23) 就成了 ((lambda (y) (+ y 10)) 23).在一个参数的函数上,传参(apply)值为23,所以值为(+ 23 10),即33.以下例子请作为练习分析.: guile> ((foo 2) 23)
: 43
: guile> ((foo 3) 23)
: 23
: guile> ((foo 2) 33)
: 532. 新的例子.2.1: guile>(define (f x y)
: ((lambda (a b)
: (+ (* x (* a a)) (* y b) (* a b)))
: (+ 1 (* x y))
: (- 1 y)))即 (define (f x y) (啥啥东西) )定义函数f,有两个参数.啥啥东西是:: ((lambda (a b)
: (+ (* x (* a a)) (* y b) (* a b)))
: (+ 1 (* x y))
: (- 1 y))这是什么呢?这是 ((lambda (a b) (又一个啥啥东西))
(东西1)
(东西2))这是什么意思呢?这是 (一个匿名函数 (参数1) (参数2)).其中 参数1 = 东西1; 参数2 = 东西2.请先回顾一下,然后我们继续.所以这是什么呢?这是给有两个参数的一个匿名函数传参,求值.这个匿名函数是什么呢?(lambda (a b) (又一个啥啥东西)原文是 (+ (* x (* a a)) (* y b) (* a b)).2.2所以,我们退回去一些,下式得到了解释.: ((lambda (a b)
: (+ (* x (* a a)) (* y b) (* a b)))
: (+ 1 (* x y))
: (- 1 y))它的意思是"这是给有两个参数的一个匿名函数传参,求值."再往回退一点,第2节提到的式子,如下,得到了解释.: (define (f x y)
: ((lambda (a b)
: (+ (* x (* a a)) (* y b) (* a b)))
: (+ 1 (* x y))
: (- 1 y)))我们定义了一个函数名为f,两个参数,这个函数f的作用是 给两个参数的一个匿
名函数传参求值.我们测试一下.: guile>(f 1 2)
: 42.3这个例子的求值,一个关键因素是那些 x、y、a、b 的值,或者说,它们的作用域。在没有被覆盖的情况下,lambda里面的x、y就是f里面的x、y。请看下面的等价测
试。: guile> (define x 1) (define y 2) (define a 3) (define b -1)
:
: guile> (+ (* x (* a a)) (* y b) (* a b))
: 4
lambda过程作为返回值的例子
pics
当你说不行时,你应该说些什么:以Android播放midi为例
中国好几年前都对能对美国说"不",软件工程师也经常要对用户和同事说"不"。但是别人经常还要在你说不以后继续喋喋不休,为什么呢?
据说男生说不的时候,就是不的意思;据说女生说不的时候,是请再继续表现的意思。这坑害了很多猥琐男。因为,只有她希望你继续表现的时候,那时"不"的意思才是请继续表现,否则,"不"的意思就是"不"。一些女生假设对方能够理解这一显然的事实:即,你是否是被欢迎继续表现的。
而这个世界上没有显然的事情。继续表现的,多是猥琐男,被希望继续表现的,通常很快就引退了。
关于如何说是,咱们今天不讨论;今天单讨论一下如何明确地说不。
会发生上述误会,盖因东方民族隐晦含蓄的表达方式传统。看过一个日本片子。某个提了个什么主意,女主角坐在那里,低头着头,很坚毅地说:"虽说很好。"
你不要以为还有下文,她说到这里就戛然而止了。日本人就是这样说话的。她发出的声音是"虽说很好",但实际想传达的是后面所有没说的内容。
在工程中,用户和同事也经常会误解。那么如何准确地传达"不"的信息呢?
1. 对用户
这个简单。
除了告诉他不以外,还要告诉他 技术上完全可以,但是...然后开始罗列代价:时间(工期)、经费等等。
然后他就自动引退了。这在女生说不的场景中,类似于,女生说完不以后,再补充,"其实呢,也不是不行,但是需要加些条件。我想要的很简单:一座大大的露台,上面有个秋千……"
明确的回绝,就是 不行 + 你不具备"行"的理由。
2. 对同事
明确告诉同事,尤其是分配给你任务的那个人,"你的想法很猪头" 的方法是,告诉他,他的想法不可行的原因。
并且,为了交流简洁起见,把他所有可能的提问,所有可能修正的主意都罗列出来,然后一一否定。写文档或报告的时候,不要期待交互,不要期待他问你答,用答案堵住他所有可能下嘴的地方。
不恃敌所不攻也,恃吾有以待也。
以下是一个实例故事。这也我们为什么要训练写实验报告的原因,它记录了过程和数据,以此支持你的看法。没有人会像科幻电影里的总统啥的那样,因为你琼瑶般喊"你们相信我吧,地球真的要毁灭啦"就相信你。
我们应该做的是:告诉小伙不行,然后对他淡定地笑笑,然后准确清晰完整地说出他不行的原因。
背景:
我请牛同学做Android下的一个程序原理,播放音乐合弦,比如大三度。意思是,同时把do,re,mi的声音发出来。
技术方案之一是用PCM混音,之二是在MIDI中同时播放这几个音。牛同学分别试了。
牛同学试了一段时间以后,说终于找到一个生成midi文件的方法。
我提出新的要求,不要把midi文件写到文件系统(internal
storage)中,而是用java的stream,在内存(RAM)中直接发给MediaPlayer播放。
牛同学说:不行。
故事开始了。
你说不行,我当然不能因为是你说的,然后就信了。要有事实。
所以牛同学发来他的代码,我学习了一段,发现真的不行。然后写了下面这封信。略有修改。
----信的正文由此开始----
下面提到的"应该"是指 建议给你的
报告的写法.有了这些你"应该"提供的资料,我才能了解你研究了哪些必要的方法.这是用以证明你研究的路线是正确的方法.或者换种说法,如果以下报告中的"应该"部分你已经告诉我,我就不必看你的代码了.
1. 你应该提供我MidiFile的出处,以便我参考,因为MidiFile不是你写的,且不是java/android的一部分;
2. 你是否参考了此邮件后面网址的实现?[http://kevinboone.net/javamidi.html]
3. 我提到的不必向文件系统中写出midi文件,原计划的路线是这样的:通过重写 writeToFile
实现,把这个方法中的写到文件系统中转为对Stream赋值,也可以把midi的内容放在 vector<byte>中;
4. 然后设置MediaPlayer.SetDataSource()的参数为这个vector或者Stream.
下面是我寻找解决方案的路线。
4.1 MediaPlayer需要FileDescriptor
[http://developer.android.com/reference/android/media/MediaPlayer.html#pubmethods]MediaPlayer.setDataSource(FileDescriptor
fd)Sets the data source (FileDescriptor) to use.
4.2 FileDescriptor可以由FileInputStream/FileOutputStream建立
[http://developer.android.com/reference/java/io/FileDescriptor.html]It's
possible to get the file descriptor used by some classes (such
asFileInputStream, FileOutputStream, and RandomAccessFile), and
thencreate new streams that point to the same file descriptor.
[http://developer.android.com/reference/java/io/FileOutputStream.html]FileInputStream.getFD()public
final FileDescriptor getFD ()Returns the underlying file descriptor.
4.4 到这里,我的思路走不下去了。因为FileInputStream的构造函数要求存储在FLASH中的文件。
你应该列出以下方法,并说明MediaPlayer没有其他的方法 setDataSource.
void setDataSource(String path)Sets the data source (file-path or
http/rtsp URL) to use.void setDataSource(FileDescriptor fd, long
offset, long length)Sets the data source (FileDescriptor) to use.void
setDataSource(FileDescriptor fd)Sets the data source (FileDescriptor)
to use.void setDataSource(Context context, Uri uri, Map<String,
String> headers)Sets the data source as a content Uri.void
setDataSource(Context context, Uri uri)Sets the data source as a
content Uri.4.5 然后我开始查是否有其他的办法。因为利用stream而不通过文件系统,这实在应该是个非常通用的方案,android sdk没有理由不支持啊。
[http://developer.android.com/reference/android/content/Context.html#getCacheDir()]讨论了这一问题,播放内存中,而不是flash(internal
storage)中的midi文件。
4.6 下面这个方法我同意[http://code.google.com/p/android/issues/detail?id=739]
: Comment 7 by [email protected], Aug 19, 2008: From the ReadMe
included with 0.9 SDK.: : Unfortunately, the ability to play audio
streams from memory (such as via an : InputStream or Reader) will not
be possible in Android 1.0. As a workaround, we : recommend that
developers save media content to SD card and use MediaPlayer to play :
from a file URI, or embed a small HTTP server and play from a URI on
localhost (such : as http://127.0.0.1:4242/something
杨注:但是第二天一早,我又意识到这是不行的。因为读http必然要求那个midi文件已经存在了。这与我原计划不希望写入文件系统相违背。
同一贴子有人提到
: Comment 12 by [email protected], May 19, 2009: Issue now
resolved with AudioTrack in Android 1.5 (Cupcake)
这是我们以前提到过的方法,在[http://developer.android.com/reference/android/media/AudioTrack.html]
里提到,不过需要PCM作为buffer而不能用midi作为buffer.
正如同一贴子的人回答道:
: Comment 22 by sam.clegg, Sep 13, 2011: Is there really still no
streaming audio support in android?: : AudioTrack doesn't help as it
only works for PCM samples, not encoded files.
4.7 至此,我提出的从stream中写入midi,然后从stream中构造MediaPlayer,这一方案可以得出结论 不行.
我们不能因为谁说不行就认定不行,而只能根据以上的分析.
4.8 我们仍然可以用PCM的方法合成和弦.我建议还是试回这个方法.
杨注:然后第二天一早,我又反悔了,因为PCM的尺寸会比MIDI大很多,这不是我们希望的。
请用MATLAB做这样的试验:
杨注:所以下面的实验我就在此省略了。