SegmentFault 旧文章合集最新的文章
2015-04-09T18:21:44+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
为什么用 Make 构建网站绝对不是一个好主意
https://segmentfault.com/a/1190000002661458
2015-04-09T18:21:44+08:00
2015-04-09T18:21:44+08:00
沙渺
https://segmentfault.com/u/shamiao
10
<p>本文直接联动阮一峰blog的文章《<a rel="nofollow" href="http://www.ruanyifeng.com/blog/2015/03/build-website-with-make.html">使用 Make 构建网站</a>》。</p>
<p>这篇文章在描述javascript构建工具的弱点上,是牵强附会和夸大的。grunt和gulp的问题远远没有文章所述那么严重。</p>
<p>而文中对make工具,仅有教程式的讲述,而缺乏对弱点和缺陷的讨论。但偏偏原文的make教程就直接暴露了make的若干设计问题。此时如果没有其他观点对make的弱点加以分析,无疑是不全面的。</p>
<p>我觉得这篇文章从本质上来讲,是错误和误导性的,尤其是对新手开发者而言。<strong>Make,包括标准的GNU make及其他任何仿品,都不应当作为Web项目的构建工具</strong>。</p>
<h2>make:看似简单实则简陋</h2>
<h3>隐喻胜于明确</h3>
<p><code>Makefile</code>使用大量的<strong>隐喻</strong>来表述<strong>实在</strong>的语法意义。</p>
<p>举一个最简单的例子:无参数的<code>make</code>命令,执行Makefile定义的第一个任务。这就是一个非常不好的语法。这造成了我们修改Makefile时必须自行记忆:“任务是否排在首位是不一样的”。</p>
<p>而这一点在大多数语言中都不存在——例如类的方法写成什么顺序都可以。在grunt和gulp构建系统中,也都使用<code>default</code>任务,明确规定无参时默认(default)的行为。符合语义,无需额外思考(<em>Don't make me think</em>)。</p>
<p>就连Python这样的通用语言,都把用<code>__name__</code>魔术变量标明主流程,作为编写脚本的一种建议实践:</p>
<pre><code>def fun1(): pass
def fun2(): pass
if __name__ == "__main__": fun1(); fun2()
</code></pre>
<p><strong>实在的意义,就应当用实在的语法写清楚,这没有任何可以退让的余地</strong>。隐喻胜过明确,这是make脱不了的原罪。</p>
<h3>丑陋的“workaround”(临时手段)</h3>
<p>这里说的例子是那个<code>.PHONY</code>伪文件。</p>
<blockquote>
<p>make仅能处理实在的<strong>文件依赖文件</strong>的关系。但实际构建中,难免出现抽象、不含实体文件的<strong>任务</strong>,例如clean——人人需要,但不产出实在文件。这时就要把任务表述成文件,然后用<code>.PHONY</code>参数告知make哪些文件是假的。</p>
</blockquote>
<p>用伪文件,把“任务”替代成“文件”,存在两点明显的问题:</p>
<ol>
<li>“任务”与“文件”的命名空间直接产生冲突。<br>
任务占用的名称,就必须人工注意实在文件不能再用,否则必然产生麻烦。</li>
<li>增删任务时,必须人工注意维护<code>.PHONY</code>列表。<br>
如果忘了把任务补到<code>.PHONY</code>中,构建过程就会产生无谓的空文件。空文件本身还不可怕,可怕的是如果没有及时发现,就会造成一次构建之后不能再次构建,白白消耗调试时间。</li>
</ol>
<p>而对于任何其他构建方法来说,都根本不存在这个问题。所有其他构建系统像看怪物一样,用诡异的眼神鄙视着make。</p>
<p>make提出了伪文件这个东西,并且还在手册中建议了“伪文件充当任务名”的用法,我相信make的开发者当初一定注意到了这个需求。但是任务(流程逻辑)和文件(内容存储)毕竟是相关却不同的两件事,分开管理才是必然的选择。</p>
<p>我不清楚make的开发者是没有想到这一点,还是自认为“<em>借用过来‘文件依赖文件’的已有模型更加‘简洁’</em>”。但结果上看,这个模型的错误是本质性且不可修正的。这个实现懒惰、简陋而不是所谓的“简洁”,最后的结果也是后患大于收益。</p>
<p>再举一个例子例子:UNIX声称“万物皆文件”,到头来还不是为了不同设备的逻辑,而保留了“块文件”、“socket文件”之类的区别?</p>
<p><strong>要替代就替代的聪明一些,把实在、重要的本质逻辑保留住</strong>。合理、明确,不回避客观区别的替代,和一时拼凑的“workaround”(临时手段)是两回事。后者一时使用尚可,但绝不应充当作为软件基础的“万灵药”。</p>
<h3>Makefile:介于语言和配置之间的“四不像”</h3>
<p>考试:请仅用<code>Makefile</code>语法(不依赖shell特性)写一个if/elseif/endif试试?</p>
<p>如果要用某种形式描述一个构建过程,其实:</p>
<ul>
<li>可以表示成纯粹的<strong>配置文件</strong>。不可独立运行,不含任何实质的代码,但绝对便于编写、修改。</li>
<li>可以表述成真正的<strong>程序代码</strong>。绝对灵活,所有语言特性随便用。</li>
<li>但一般都代码和配置文件联合使用,兼取两者之长。(即使是纯代码,其实数据和执行逻辑也会有一定的分离,而不是混在一起搞成“意大利面式编程”)</li>
</ul>
<p>审查<code>Makefile</code>的本质设计,其实是一种描述<strong>依赖关系</strong>的<strong>配置文件</strong>,描述了“文件依赖文件”和“文件依赖shell代码”两种关联。但偏偏<code>Makefile</code>也同时提供简单的流程控制、赋值等语句,使得<code>Makefile</code>也是一种可以控制流程走向的<strong>程序代码</strong>。</p>
<p>所以<code>Makefile</code>偏偏落在了<strong>配置</strong>和<strong>代码</strong>两者之间,既不是倒向一端,也不是两者的联合,最后形成了一个“四不像”的混合品。作为配置文件写起来太费神,作为程序代码又太简陋不够用。</p>
<p>我想问:就从<code>Makefile</code>的设计上来看,那个被奉为圭臬(事实上也确实很优秀)的“UNIX哲学”在哪里?在哪里?</p>
<h2>shell:躲不开的雷区</h2>
<h3>符号胜于语义</h3>
<p>shell使用各种符号来表达语义,难读难写。也就比那个正则表达式简单点不多。</p>
<p>以下两段构建脚本,你愿意读、写或改哪一个?</p>
<pre><code>lib_bundle := build/lib.min.js
libraries := node_modules/jquery/dist/jquery.js \
node_modules/underscore/underscore.js \
$(lib_bundle): $(libraries)
uglifyjs -cmo $@ $^
# What the heck does "c m o @ ^ $" means ???
</code></pre>
<pre><code>var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
gulp.task('lib:bundle', function () {
return gulp.src(['node_modules/jquery/dist/jquery.js',
'node_modules/underscore/underscore.js'])
.pipe(uglify())
.pipe(concat('lib.min.js'))
.pipe(gulp.dest('build/'));
});
</code></pre>
<h3>运行环境的高依赖</h3>
<p>shell是一个严重依赖系统环境的工具。一个make能够正确调用shell脚本,一般都需要:</p>
<ul>
<li>系统内有GNU工具链</li>
<li>工具链的详细用法(参数意义等),不能与编写者产生严重的冲突,甚至不能进行造成deprecated(弃用)的升级</li>
<li>PATH等基本的环境变量正确</li>
<li>但其他的环境变量还不能与<code>Makefile</code>中用到的变量冲突</li>
</ul>
<p>更可怕的是以上这些要素,基本上都是隐喻性的。没有明确的版本控制手段去保证不说,甚至连确认都是不现实的。以前能用的脚本可能换个发行版、升级个系统,甚至于换个用户就可能会发生问题。</p>
<p>我们既然已经有了npm版本控制,更何况shell构建本质上也是调用基于node的工具,那我们为什么还要去踩shell缺乏版本控制这个坑?</p>
<h3>shell调用js的性能问题</h3>
<p>shell调用js,每一个命令都需要启动/停止node进程,并且各个工具是顺序执行的。</p>
<p>而node构建工具,只需要使用同一个node进程,并且各个工具可以异步启停、并行运行。</p>
<p>这个效率区别是不需要具体比较的。</p>
<h2>总结</h2>
<h3>“shell自动化”——邪道之路</h3>
<p>从历史上来看,shell本来就是为了方便人类执行命令的小工具。而后人类发现了自动执行命令的便利性,从而将shell扩展成为一门轻量的脚本语言,这个发展历程是可以理解的。</p>
<p>其实简短的shell脚本也可以大大方便人类的工作,是个好用的工具。可是一旦shell脚本庞大起来,shell不适合自动化运行和大型程序管理的各种硬伤就开始暴露:</p>
<ul>
<li>提倡使用特殊符号,过于强调语法简短(首要问题)。<br>
这一点对于人类操作shell是优势,谁都想在命令行下少打几个字。<br>
但如果用于长时间编写、快速运行、长期维护的正式项目,shell的这个特点就会立刻表现为难读、难写、难改的短板。</li>
<li>缺少大型项目所需的版本管理、面向对象等特性</li>
<li>语言功能不足,例如缺乏最基本的数值计算</li>
<li>语言特性诡异,例如bash大多数情况下对空白字符不敏感,可偏偏变量赋值的等号两边不准加空格</li>
<li>shell这一层太薄,过于依赖命令行工具链,甚至一些很基本的任务shell层都不能自行消化,例如<code>[</code>。</li>
<li>命令行工具的提示都是为人类阅读而设计的,不适合机器解读与程序间交互</li>
</ul>
<p>shell从本质上,是方便人类手打命令的终端软件,而不是可靠的自动化工具。本质如此,将来就会一直如此。<strong>shell就是shell,也一直只会是shell,不应当赋予其过深的责任和负担</strong>。</p>
<p>除非①没有更好的选择②工作实在太少太简单,否则永远不要在正式项目中依赖shell自动化。</p>
<h3>不要被“技艺高超”的假象迷惑!</h3>
<p>必须承认:shell与make工具有不少“坑”,但一旦调试良好,它们确实能够稳定运行。并且工程师们经常会产生这种心态:<em>解决的问题越难,填平的“坑”越多,最后成功时的成就感就越强</em>。</p>
<p>这是一个思维陷阱。这个陷阱中用过程的复杂度替代了需求的复杂度,从而容易让人错误的评判和看待自己的工作。</p>
<p>可工程毕竟不是智力题。实际需求才是唯一的,只有需求本身的复杂度才需要尊重。代码只不过是完成任务的一种副产品。代码量越少越好,代码引入的额外复杂度越低越好,代码维护起来越容易越好。至于代码本身解决了多少难题,适配了其他工具多少的“坑”,一般都不值一提。</p>
<p>做黑客自有做黑客的合适场景,就如同业余时间做点智力题其实是个不错的爱好。但实际工程环境下,请老老实实做工程师,使用简单的工具解决同等的问题,不要炫技。</p>
<h3>我的推荐</h3>
<p>请使用npm的构建工具。我推荐目前(成文时)仍处于测试状态的Gulp 4。</p>
<p>Gulp 4最赞赏的地方是引入了简洁明确的语法,规定命令之间的串并行关系。从此可以把任意形状的<em>加权森林</em>(权值代表执行顺序)简单地表示成Gulp的代码:</p>
<p><img src="/img/bVlkwO" alt="代码所述的树结构"></p>
<pre><code>gulp.task('default', gulp.series('clean', 'build', 'deploy'))
gulp.task('clean', gulp.parallel('clean:a', 'clean:b'))
gulp.task('build', gulp.parallel('less', 'uglify'))
gulp.task('deploy', gulp.series('revision', 'copy'))
</code></pre>
<p>Gulp 4入门请通读《<a rel="nofollow" href="http://segmentfault.com/a/1190000002528547">Gulp 4.0 前瞻</a>》这篇文章,以及Gulp 4源代码目录中的所有<code>recipes</code>(参考代码),非常容易。</p>
<p>Gulp也有Gulp、Node和JavaScript的麻烦(例如并行代码的编写不良一般不会报错),但起码在Web构建这个环境下,比shell值得拥有。</p>
<p>如果你真的需要一些命令行的工具,那也应该舍弃shell这一层,在js、python等正常语言环境下调用它们。<strong>命令行工具是不需要shell的,启动子进程并且传递argc、argv的参数才是本质。</strong></p>
<hr>
<blockquote>
<p>原创发表在 SegmentFault.com 博客,转载请遵守 SegmentFault 相关规定(见页脚),作者为<em>沙渺 sha@miao.im</em>。</p>
</blockquote>
【2次元娱乐】paiza迷你编程游戏《工程师也要谈恋爱》漫画全汉化
https://segmentfault.com/a/1190000002411898
2014-12-08T10:51:39+08:00
2014-12-08T10:51:39+08:00
沙渺
https://segmentfault.com/u/shamiao
5
<p><a rel="nofollow" href="https://paiza.jp">paiza</a>是日本的一家工程师求职与技能训练网站。</p>
<p>paiza最近举办了一个迷你线上编程游戏活动《工程师也要谈恋爱》,刊载一个讲述工程师开发生活的漫画。玩家需要通过类似信息学奥赛(OI)的方式,提交代码解决算法问题来继续漫画的阅读。根据问题解决的正确与否,漫画的进展也会有所不同。</p>
<p>漫画总篇幅很短,只有7页。编程游戏本身难度也很低。所以在这里提供一个原创的翻译版本,方便非日语工程师无需进行游戏直接阅读。</p>
<p>地址:<a rel="nofollow" href="https://paiza.jp/poh/enkoi">https://paiza.jp/poh/enkoi</a></p>
<p><img src="/img/bVkhAH" alt="标题图"></p>
<h2>漫画</h2>
<p>游戏共3关,流程基本上是过关之后开启下一关。如果未能过关则进入GAME OVER画面,本关必须重新提交代码。唯一的分支是第3关以满分和部分分过关,进入的ENDING画面略有区别。具体请看每一页顶上的流程图。</p>
<h3>MISSION 1</h3>
<p><img src="/img/bVkhBb" alt="图1"></p>
<p><img src="/img/bVkhBh" alt="图2"></p>
<p><img src="/img/bVkhBj" alt="图3"></p>
<h3>其余漫画页面</h3>
<blockquote>
<p>剧透注意!(SPOILER WARNING !!!)(ネタバレ注意!!)</p>
<p>原本在paiza网站上,以下的页面不是无条件阅览的,必须提交代码开始游戏。<br>
Following manga pages are only available after submitting code in original paiza hackathon page.</p>
<p>这里作为翻译给出全部的翻译稿,但需要点击查看而不是直接张贴图片。我们希望您在实际游戏中参考使用,而不是直接跳到结局。<br>
All branches are translated and provided via a link (instead of showing image directly) now.<br>
I hope every Chinese reader use it while playing this game on paiza, instead of reading it directly.</p>
<p>感谢<a rel="nofollow" href="http://paiza.hatenablog.com/entry/2014/12/15/%E3%80%90%E4%B8%AD%E5%9B%BD%E8%AA%9E%E7%89%88%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%97%E3%81%9F%EF%BC%81%E3%80%91POH%E7%AC%AC4%E5%BC%BE%E3%80%8C%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A7">paiza官方开发日志</a>指出这个剧透问题。<br>
Thanks for <a rel="nofollow" href="http://paiza.hatenablog.com/entry/2014/12/15/%E3%80%90%E4%B8%AD%E5%9B%BD%E8%AA%9E%E7%89%88%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%97%E3%81%9F%EF%BC%81%E3%80%91POH%E7%AC%AC4%E5%BC%BE%E3%80%8C%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A7">paiza development blog</a> pointing out this spoiler problem.</p>
</blockquote>
<ul>
<li><a rel="nofollow" href="/img/bVkhBs/view">GAME OVER 1</a></li>
<li><a rel="nofollow" href="/img/bVkhBu/view">MISSION 2</a></li>
<li><a rel="nofollow" href="/img/bVkhBA/view">GAME OVER 2</a></li>
<li><a rel="nofollow" href="/img/bVkhBD/view">MISSION 3</a></li>
<li><a rel="nofollow" href="/img/bVkhBE/view">GAME OVER 3</a></li>
<li><a rel="nofollow" href="/img/bVkhBF/view">ENDING</a></li>
</ul>
<h2>题</h2>
<p>第1题和第2题直接算。第3题只要注意到O(N*T)肯定超时就没问题。把区间像虫子一样一格一格往前爬,最后的时间复杂度是O(N)。</p>
考虑 PHP 5.0~5.6 各版本兼容性的 cURL 文件上传
https://segmentfault.com/a/1190000000725185
2014-10-16T00:55:08+08:00
2014-10-16T00:55:08+08:00
沙渺
https://segmentfault.com/u/shamiao
21
<p>最近做的一个需求,要通过PHP调用cURL,以<code>multipart/form-data</code>格式上传文件。踩坑若干,够一篇文章了。</p>
<h2>重要警告</h2>
<p><strong>没事不要读PHP的官方中文文档!版本跟不上坑死你!</strong></p>
<h2>不同版本PHP之间cURL的区别</h2>
<p>PHP的cURL支持通过给<code>CURL_POSTFIELDS</code>传递关联数组(而不是字符串)来生成<code>multipart/form-data</code>的POST请求。</p>
<p>传统上,PHP的cURL支持通过在数组数据中,使用“<code>@</code>+文件全路径”的语法附加文件,供cURL读取上传。这与命令行直接调用cURL程序的语法是一致的:</p>
<pre><code>curl_setopt(ch, CURLOPT_POSTFIELDS, array(
'file' => '@'.realpath('image.png'),
));
equals
$ curl -F "file=@/absolute/path/to/image.png" <url>
</code></pre>
<p>但PHP从5.5开始引入了新的CURLFile类用来指向文件。CURLFile类也可以详细定义MIME类型、文件名等可能出现在multipart/form-data数据中的附加信息。PHP推荐使用CURLFile替代旧的<code>@</code>语法:</p>
<pre><code>curl_setopt(ch, CURLOPT_POSTFIELDS, [
'file' => new CURLFile(realpath('image.png')),
]);
</code></pre>
<p>PHP 5.5另外引入了<code>CURL_SAFE_UPLOAD</code>选项,可以强制PHP的cURL模块拒绝旧的<code>@</code>语法,仅接受CURLFile式的文件。5.5的默认值为false,5.6的默认值为true。</p>
<p>但是坑的一点在于:<code>@</code>语法在5.5就已经被打了deprecated,在5.6中就直接被删除了(会产生 ErorException: The usage of the <code>@filename</code> API for file uploading is deprecated. Please use the CURLFile class instead)。</p>
<p>对于PHP 5.6+而言,手动设置<code>CURL_SAFE_UPLOAD</code>为false是<strong>毫无意义</strong>的。根本不是字面意义理解的“设置成false,就能开启旧的unsafe的方式”——旧的方式已经作为废弃语法彻底不存在了。PHP 5.6+ == CURLFile only,不要有任何的幻想。</p>
<p>我的部署环境是5.4(仅<code>@</code>语法),但开发环境是5.6(仅CURLFile)。都没有压在5.5这个两者都支持过渡版本上,结果就是必须写出带有环境判断的两套代码。</p>
<p>现在问题来了……(挖掘机滚远点!)</p>
<h2>环境判断:小心魔法数字!</h2>
<p>我见过这种环境判断的代码:</p>
<pre><code>if (version_compare(phpversion(), '5.4.0') >= 0)
</code></pre>
<p>我对这种代码的评价只有一个字:<strong>屎</strong>。</p>
<p>这个判断掉入了典型的<strong>魔法数字陷阱</strong>。版本号莫名其妙的出现在代码之中,不查半天PHP手册和更新历史,很难明白作者被卡在了哪个功能的变更上。</p>
<p>代码应该回归本源。我们的实际需求其实是:有CURLFile就优先采用,没有再退化到传统<code>@</code>语法。那么代码就来了:</p>
<pre><code>if (class_exists('\CURLFile')) {
$field = array('fieldname' => new \CURLFile(realpath($filepath)));
} else {
$field = array('fieldname' => '@' . realpath($filepath));
}
</code></pre>
<h2>建议明确指定的退化选项</h2>
<p>从可靠的角度,推荐指定<code>CURL_SAFE_UPLOAD</code>的值,明确告知php是容忍还是禁止旧的<code>@</code>语法。注意在低版本PHP中<code>CURLOPT_SAFE_UPLOAD</code>常量本身可能不存在,需要判断:</p>
<pre><code>if (class_exists('\CURLFile')) {
curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true);
} else {
if (defined('CURLOPT_SAFE_UPLOAD')) {
curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
}
}
</code></pre>
<h2>cURL选项设置的顺序</h2>
<p>不管是<code>curl_setopt()</code>单发还是<code>curl_setopt_array()</code>批量,cURL的选项总是设置一个生效一个,而设置好的选项立刻就会影响cURL在设置后续选项时的行为。</p>
<p>例如<code>CURLOPT_SAFE_UPLOAD</code>就和<code>CURLOPT_POSTFIELDS</code>的行为有关。如果先设置<code>CURLOPT_POSTFIELDS</code>再设置<code>CURLOPT_SAFE_UPLOAD</code>,那么后者的约束作用就不会生效。因为设置前者时cURL就已经把数据实际的识读处理完毕了!</p>
<p>cURL有那么几个选项存在这种坑,务必小心。还好这种存在“依赖关系”的选项不多,机制也不复杂,简单处理即可。我的方法是先批量设置所有的选项,然后直到<code>curl_exec()</code>的前一刻才用<code>curl_setopt()</code>单发设置<code>CURLOPT_POSTFIELDS</code>。</p>
<p>实际上在<code>curl_setopt_array()</code>用的数组中,保证<code>CURLOPT_POSTFIELDS</code>的位置在后边也是可靠的。<strong>PHP的关联数组是有顺序保障的</strong>,我们也可以假设<code>curl_setopt_array()</code>内部的执行顺序一定是从头到尾按顺序<code>[注A]</code>,所以尽可放心。</p>
<p>我的做法只是在代码表现上加个多余的保险,突出强调顺序的重要性防以后手贱。</p>
<h2>命名空间</h2>
<p>PHP 5.2或以下的版本没有命名空间。代码中用到了空间分隔符<code>\</code>就会引发解析器错误。要照顾PHP 5.2其实容易想,放弃命名空间即可。</p>
<p>要注意的反倒是有命名空间的PHP 5.3+。无论是调用CURLFile还是用<code>class_exists()</code>判断CURLFile的存在性,都推荐写成<code>\CURLFile</code>明确指定顶层空间,防止代码包裹在命名空间内的时候崩掉。</p>
<h2>注解</h2>
<blockquote>
<p>【注A】 好吧我知道assume不是件好事,不过有些实在过分浅显的事实,就容我下个最低限度的断言吧</p>
</blockquote>
献给虚拟主机 Laravel 4 用户: 全功能 MySQL 队列驱动器 L4mysqlqueue
https://segmentfault.com/a/1190000000715781
2014-10-10T02:49:49+08:00
2014-10-10T02:49:49+08:00
沙渺
https://segmentfault.com/u/shamiao
2
<p><strong>几小时前刚刚发布的 Larevel 5.0 为队列功能提供了官方原生的<code>database</code>驱动器,完全取代了本软件包的功能。</strong></p>
<p><strong>对于Laravel 5及以上版本,本文的内容及所涉软件包均已失效,请勿实践。未来也不会再有<code>l5mysqlqueue</code>软件包。</strong></p>
<p><strong><code>shamiao/l4mysqlqueue</code>软件包完成了他的使命。我们永远怀念他。</strong></p>
<p><strong>沙渺,2015年2月4日。</strong></p>
<hr>
<p>laravel 4是一个高度依赖包管理器与命令行界面的php框架。不使用<code>composer</code>和<code>php artisan</code>两个工具几乎寸步难行。……不过这也并不意味着laravel就是虚拟主机没戏,必须VPS/云主机起跳的“小网站杀手”。</p>
<p>其实在cPanel虚拟主机上,部署laravel并不是那么难的事情,因为仁慈的cPanel面板,一般都开放有操作Linux原生cron jobs的设置页面。</p>
<p>只要有了cron这个口子,就可以获得执行命令行任务的机会,每分钟1次——这已经足够了。</p>
<p>但是实际使用中发现,laravel的其他功能都没毛病,就是<strong>异步队列</strong>根本不能用。考察laravel提供的原生异步队列驱动器,没有一个适合在虚拟主机环境中使用:</p>
<ul>
<li>同步(sync):只是残废的调试工具</li>
<li>Beanstalkd:虚拟主机不会有</li>
<li>Redis:虚拟主机不会有</li>
<li>IronMQ:要美元</li>
<li>Amazon SQS:要美元</li>
</ul>
<p>我考虑了很多方案(包括一些很土炮的馊主意),最终还是遵守了laravel的哲学<code>[注A]</code>,写了一个原生的MySQL队列驱动器。</p>
<h2>原理</h2>
<p>就是数据库一张表。加入任务时写入,执行任务时检索,需要删除时打标记(软删除)。</p>
<p>即使使用<code>Queue::later()</code>推迟任务,任务信息也会立刻写入表格,而不设计“主动推迟一定时间再写入”的复杂。表格中记录有任务的执行时刻,没到时间的任务会在检索时被自然忽略。</p>
<p>性能确实低下(受制于MySQL),绝对禁止用于大数量、高密度任务的场合。但是部署极其简易,所以很适合小网站、偶发性、时效性不强的异步任务。</p>
<h2>特点</h2>
<p>100%完整实现,Laravel文档规定的用法全部支持。具体而言:</p>
<ul>
<li>支持原生的<code>queue:listen</code>, <code>queue:work</code>等 artisan 队列操作</li>
<li>不在 artisan 上画蛇添足去定义额外的命令</li>
<li>支持记录重试次数,正确对接laravel的失败任务回收系统</li>
<li>支持任意多个队列,支持自定义默认队列名称</li>
<li>只占用1个数据表,支持自定义表名,允许数据库有表前缀</li>
<li>不使用Eloquent ORM</li>
</ul>
<h2>安装</h2>
<p><code>composer.json</code> 增加以下依赖包,并做 <code>composer update</code> <code>[注B]</code>:</p>
<pre><code>"shamiao/l4mysqlqueue": "~1.0"
</code></pre>
<p>编辑 <code>config/app.php</code> 增加 <code>provider</code> 条目:</p>
<pre><code>'Shamiao\L4mysqlqueue\L4mysqlqueueServiceProvider'
</code></pre>
<p>编辑 <code>config/queue.php</code> 设置队列驱动器:</p>
<pre><code>'default' => 'mysql', // 这个是连接名,随意。惯例是写成和驱动器同名的'mysql'
'mysql' => array( // 这个和'default'右边那个一致就好
'driver' => 'mysql', // 驱动器名,这里就必须写成'mysql'了!
'queue' => 'default', // 如有需要,可以指定默认队列名,推荐直接删去不写
'table' => 'queue',' // 如有需要,可以指定表格名,推荐直接删去不写
),
</code></pre>
<p>最后把本软件包(package)附带的迁移操作(migrations)并入数据库<code>[注C]</code>:</p>
<pre><code>php artisan migrate --package="shamiao/l4mysqlqueue"
</code></pre>
<h2>使用</h2>
<p>没什么好说的。这个驱动器对用户而言是透明的,所以Laravel文档的<a rel="nofollow" href="http://laravel.com/docs/queues">Queue</a>页面怎么说就可以怎么做。</p>
<p>是完整实现,所以文档中提到的功能全都没有禁忌。</p>
<p>如果在虚拟主机环境下,运行一个必须持续跑着的 <code>queue:listen</code> 或 <code>queue:work --daemon</code> 不方便,也可以像这样利用cron来变通的执行<code>[注D]</code>:</p>
<pre><code>* * * * * ( cd /home/username/your/laravel/dir; php artisan queue:work --tries=3 )
</code></pre>
<p>当然代价就是每分钟只能执行一个任务了。</p>
<h2>注意</h2>
<ul>
<li>删除任务时只打标记做<em>软性删除</em>,所以数据表<code>queue</code>必然只膨胀不减小。请记得自拟计划,在负载不高时把<code>status = 'deleted'</code>的任务真正的DELETE掉。</li>
<li>很多实现还会调整,如有任何变化请以GitHub说明为准。</li>
</ul>
<h2>链接</h2>
<p>From GitHub and Composer with love:</p>
<ul>
<li><a rel="nofollow" href="https://github.com/shamiao/l4mysqlqueue">https://github.com/shamiao/l4mysqlqueue</a></li>
<li><a rel="nofollow" href="https://packagist.org/packages/shamiao/l4mysqlqueue">https://packagist.org/packages/shamiao/l4mysqlqueue</a></li>
</ul>
<p>本项目代码均为独立,开发过程受到过两个不完善实现 <a rel="nofollow" href="https://github.com/barryvdh/laravel-async-queue">barryvdh/laravel-async-queue</a> 和 <a rel="nofollow" href="https://github.com/octoberrain/cron">octoberrain/cron</a> 的启发。</p>
<h2>注释</h2>
<blockquote>
<ul>
<li>A:①要艺术,不要把代码写的很“脏”。 ②接口遍地都是,有需要,就扩展。</li>
<li>B: 虚拟主机肯定不会提供composer。你需要自己下载<code>composer.phar</code>,放到主文件夹下边,然后执行<code>php /home/username/composer.phar update</code>。</li>
<li>C: 根据主机环境不同,可能部分主机要用<code>php5</code>代替<code>php</code>,也可能有部分主机没有PATH变量,必须手动打<code>/usr/bin/php</code>完整路径。</li>
<li>D:不妨写成<code>.sh</code>脚本文件并设置权限755,这样在cron job里只需要指定脚本完整路径即可,日后修改也方便。</li>
</ul>
</blockquote>
开源硬件通行资料 (1) 各种开发平台的UART串口位置
https://segmentfault.com/a/1190000000481517
2014-05-15T13:15:00+08:00
2014-05-15T13:15:00+08:00
沙渺
https://segmentfault.com/u/shamiao
2
<p>这是我们SegmentFault在开源硬件上追求跨界的努力。《开源硬件通行资料》每篇文章围绕一个<strong>话题</strong>,集合各种开发板完成同一个需求的方法,去总结各个开发平台之间的共性与区别。</p>
<p>让每种板卡的用户,都能平等的获取资料和知识,而不至于由于板卡的区别而分出阵营,这甚至对我个人都是一项很强的理想和渴望。</p>
<p>所有内容保证在我现有的以下板卡上实测:</p>
<ul>
<li>Raspberry Pi (Model B, UK)</li>
<li>CubieBoard 1/2</li>
<li>CubieTruck (CB3)</li>
<li>pcDuino V3</li>
<li>Banana Pi</li>
<li>BeagleBone Black (Embest, BB-Black中国版)</li>
<li>Arduino Nano (如果相关)</li>
</ul>
<p>如果其他板卡能够查到资料,也可能会收录一些未经实际测试的内容。同时欢迎其他厂商向SegmentFault提供测试平台,支持本系列文章的创作!</p>
<hr>
<p>第一篇我们从串口开始。拿到开发板之后,和板子的交互通常是键鼠直接操作,或SSH远程登录。</p>
<p>但如果碰到各种问题需要调试时,使用传统的串口直接强行连接开发板的命令行,在任何时候作为保底方案都是必要的。</p>
<p>本篇文章整理总结各种开发板的串口的位置及资料,以备不时之需。</p>
<h2>关于UART串口</h2>
<p>一般而言,对UART串口只关心3根线:</p>
<ul>
<li>RXD(数据接收) 一般是白色</li>
<li>TXD(数据发送) 一般是绿色</li>
<li>GND(地) 肯定是黑色</li>
</ul>
<p>一定要小心:<strong>这里的RXD和TXD的收发,都是对于两侧设备自己的角度而言的</strong>。<br>
所以两个设备连接时必须GND连GND(共地),RXD和TXD交叉。串口定义如此。</p>
<p>某些设备会自作聪明的标反RXD与TXD,引导用户看似方便的“R连R、T连T”,<strong>这其实是完全错误的,请千万小心</strong>。<br>
如果出现数据接收不到,可以试着翻转一下RXD和TXD,看一下您的模块是否存在这个问题。</p>
<p>一般而言,串口的连接器都是2.54mm的标准插针。如无特别注明,以下串口均为3.3V电平,串口配置均为<strong>115200 8N1</strong>。</p>
<p>USB转TTL可能会有VCC/3.3V/5V等类似的电源接口。这个接口的作用是对外少量供电,不是说两方板子的VCC需要互连——甚至互连了会有危险(电压不同会引起双方电源电流互灌)。<strong>请一定要把USB转TTL模块上的VCC留空不接</strong>!</p>
<p>注:<strong>RTS</strong>(黄)和<strong>CTS</strong>(蓝)是UART串口可选的控制信号,在高可靠性应用中可能会用到,平时并非必须。</p>
<h2>Raspberry Pi</h2>
<p>UART0用于调试,在连接器P1上。RXD:10, TXD:8, GND:6。<br>
Raspberry Pi实际上只有一个UART串口——虽然存在UART1,但和UART0共享同样的引脚位置。</p>
<p><img src="http://segmentfault.com/img/bVcicE" alt="RPi串口位置"></p>
<h2>CubieBoard 1/2</h2>
<p>UART0用于调试,在主板正中间的连接器J3上。RXD:3, TXD:4, GND:1,板子上有直接的文字标注。<br>
另外有UART3,4,5,6四个串口,可以从两侧的GPIO排针上引出。可参阅<a rel="nofollow" href="http://linux-sunxi.org/Cubieboard/ExpansionPorts">这个页面</a>。</p>
<p><img src="http://segmentfault.com/img/bVcicF" alt="CB1/2串口位置"></p>
<h2>CubieTruck (CB3)</h2>
<p>UART0用于调试,在主板右下角标有UART0的连接器CN7上。RXD:3, TXD:4, GND:1,板子上有直接的文字标注。<br>
另外有UART3,4,7三个串口,可以从两个GPIO双排针上引出。可参阅<a rel="nofollow" href="http://linux-sunxi.org/Cubietruck">这个页面</a>。</p>
<p><img src="http://segmentfault.com/img/bVcicG" alt="CT串口位置"></p>
<h2>pcDuino V3</h2>
<p>UART0用于调试。在主板加框印字<code>UART_0</code>的旁边的3pin排针上。<br>
另有UART2可以引出,其位置和Arduino一致(0、1号引脚)。</p>
<p><img src="http://segmentfault.com/img/bVcicH" alt="pcD串口位置"></p>
<h2>Banana Pi</h2>
<p>UART0用于调试。位置在J11(相对于Raspberry Pi多出来的2pin排针),需要从J12借用GND共地。<br>
另有UART2和UART3从26pin双排针上引出,UART7从J12双排针引出。</p>
<p><img src="http://segmentfault.com/img/bVcicV" alt="BnnPi串口位置"></p>
<h2>BeagleBone Black</h2>
<p>UART0用于调试。在CPU左侧唯一做成排针(而不是排座)的位置。另有UART1、UART2和UART4可以从左侧双排座引出。</p>
<p><img src="http://segmentfault.com/img/bVcicW" alt="BBB串口位置"></p>
<h2>Radxa Rock</h2>
<p>请参照<a rel="nofollow" href="http://wiki.radxa.com/Rock/extension_header">http://wiki.radxa.com/Rock/extension_header</a>。可使用UART0、UART3和UART1。</p>
<h2>Arduino</h2>
<p>Arduino系列产品的串口全部在0和1号引脚。(Due等少数型号可能有其他串口,请参阅对应型号的手册)</p>
<hr>
<p>《开源硬件通行资料》系列文章,<strong>周三</strong>更新,敬请关注!<br>
SegmentFault原创技术资料,原作者沙渺(shamiao),使用·转载请遵守本站相关版权声明。</p>
简析Windows Notepad里可选的字符编码
https://segmentfault.com/a/1190000000498924
2014-05-08T20:19:00+08:00
2014-05-08T20:19:00+08:00
沙渺
https://segmentfault.com/u/shamiao
6
<p>群里 @lizheming 问到了Windows Notepad(记事本)中保存文件的编码选项都是什么意思……</p>
<p>这篇文章就简单测试一下Windows Notepad的行为。</p>
<p><img src="http://segmentfault.com/img/bVcfVo" alt="Windows Notepad的可选编码"><br>
▲ Windows Notepad的编码包含ANSI、Unicode、Unicode big endian和UTF-8。</p>
<h2>警告</h2>
<p>本文仅仅阐述一个广泛使用的软件的技术事实,不代表作者支持或反对使用该软件。<br>
事实上作者推荐任何时候都不使用 Windows Notepad 来处理计算机程序代码。<br>
本文仅在某一个简体中文版64位Windows 7的实例下验证,仅供参考。不保证在其他相同或相异系统下能够重现一致的结果。</p>
<h2>注意</h2>
<p>本文严格区分Unicode的<strong>编码</strong>和<strong>字节序列化</strong>。<br>
Unicode的<strong>编码</strong>仅指使用数(通常写成16进制数)来一对一的代表字符的工作。这个数的范围仅受Unicode标准的约束,与计算机毫无关联。<br>
Unicode的<strong>字节序列化</strong>指为了能够写入计算机存储器,而把一个Unicode标准范围内的数,表示成N个字节的工作。</p>
<h2>测试用例</h2>
<p>测试用例为:“锟斤拷【断行】a【断行】”。(锟斤拷是一种信仰。)</p>
<p>所有字符的GBK和Unicode编码为:</p>
<ul>
<li>锟 GBK=<code>EFBF</code> Unicode=<code>U+951F</code>
</li>
<li>斤 GBK=<code>BDEF</code> Unicode=<code>U+65A4</code>
</li>
<li>拷 GBK=<code>BFBD</code> Unicode=<code>U+62F7</code>
</li>
</ul>
<p>以下ASCII字符的GBK和Unicode编码与ASCII一致:</p>
<blockquote>
<p>a=<code>0x61</code> CR=<code>0x0D</code> LF=<code>0x0A</code><br>
(Windows一个换行符占有两个字符:CR+LF)</p>
</blockquote>
<h2>ANSI</h2>
<p>在简体中文系统下,ANSI就是中华人民共和国国家标准定义的GBK编码。</p>
<p>Windows Notepad使用ANSI存储这个文件的结果如下:</p>
<pre><code class="lang-plain">EF BF BD EF BF BD 0D 0A 61 0D 0A
----- ----- ----- -- -- -- -- --
</code></pre>
<p>简单的使用GBK编码存储了所有的字符。最高位不是1的单字节并等同于ASCII,否则双字节。</p>
<p>这里要注意字节序(Endian)的问题<code>[注A]</code>。可以看到这里的字节序是<strong>大端在先(big-endian)</strong>的。</p>
<p>但是不必特意强调“大端在先的GBK”——因为从GB2312开始,标准就规定了存储方式是大端在先的<code>[注B]</code>。后来的GBK和GB18030-2000向下兼容。</p>
<p>ANSI的麻烦就是依赖系统——其他语言系统的ANSI就不是GBK了,打开GBK的文件必然乱码。并且GBK的字符集本身也太小。<br>
(千万不要说“我只用中文”——少了Unicode那些符号,网上那些颜文字都打不出来)</p>
<h2>Unicode系列</h2>
<p>Windows Notepad所说的“Unicode”、“Unicode big endian”和UTF-8,全都是同样的Unicode<strong>编码</strong>的不同的<strong>字节序列化</strong>存储方法。</p>
<h3>UTF-16 和 BOM</h3>
<p>这里的Unicode指UTF-16<code>[注C]</code>。UTF-16是极其简单粗暴的序列化方法——绝大多数的Unicode字符都在U+0000~U+FFFF的范围内<code>[注D]</code>,那就每个字符用两个字节,把Unicode编码的原始值写盘。</p>
<p>注意ASCII字符也必须浪费一倍的空间存储高8位的0x00——因为如果把高8位的0略了,解析时就再也没有其他的依据去断字。</p>
<p>对于UTF-16就存在大端和小端的问题了——UTF-16并不规定字节的大端在前还是小端在前。但UTF-16并不包含表示字节序的信息,总不能人工看看哪个解析是不乱码的吧……</p>
<p>Unicode提供的解决方式是,把一个<strong>零宽无断字空格符</strong>(<code>U+FEFF</code> ZERO WIDTH NO-BREAK SPACE)以UTF-16的方式序列化之后,塞到文件的最前边。这样UTF-16解析器读取文件的前两个字节,如果是<code>FE FF</code>就是大端在前,<code>FF FE</code>就是小端在前。</p>
<p>这个塞进去的东西就叫<a rel="nofollow" href="http://en.wikipedia.org/wiki/Byte_order_mark">BOM</a>(Byte Order Mark,字节顺序标记)。</p>
<blockquote>
<p>值得一提的是,<strong>零宽无断字空格符</strong>也常用于充当1个有效字符,破拆各种场合的字数限制。包括SegmentFault的问答和评论内容在内。</p>
</blockquote>
<h3>记事本的“Unicode”和“Unicode big endian”</h3>
<p>单写“Unicode”,根本就不是一种存储方法的完整表达。因为这只包含<strong>编码</strong>而没有<strong>字节序列化</strong>。</p>
<p>M$出现这种错误,我一点都不觉得奇怪。死记结论就可以了:<strong>Windows Notepad的“Unicode”就是UTF-16</strong>。</p>
<p>Windows Notepad使用<strong>“Unicode” = 小端在先的UTF-16</strong>,存储这个文件的结果如下:</p>
<pre><code> FF FE 1F 95 A4 65 F7 62 0D 00 0A 00 61 00 0D 00 0A 00
-BOM- ----- ----- ----- ----- ----- ----- ----- -----
U+FEFF 951F 65A4 62F7 000D 000A 0061 000D 000A <--Unicode原始值
</code></pre>
<p>Windows Notepad使用<strong>“Unicode big endian” = 大端在先的UTF-16</strong>,存储这个文件的结果如下:</p>
<pre><code> FE FF 95 1F 65 A4 62 F7 00 0D 00 0A 00 61 00 0D 00 0A
-BOM- ----- ----- ----- ----- ----- ----- ----- -----
U+FEFF 951F 65A4 62F7 000D 000A 0061 000D 000A <--Unicode原始值
</code></pre>
<h3>UTF-8</h3>
<p>UTF-8是一种用1~4个字节表示1个Unicode字符的<strong>变长的</strong>字节序列化方法。具体的实现细节看<a rel="nofollow" href="http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html">这篇文章</a>。UTF-8的好处在于:</p>
<ol>
<li>无论是IETF的推荐,还是实际业界的执行,UTF-8都是互联网的标准。</li>
<li>向下兼容,ASCII字符UTF-8序列化后仍是原样,任何ASCII文件也是有效的UTF-8文件。</li>
<li>没有字节序问题。UTF-8的字节序是由<a rel="nofollow" href="http://tools.ietf.org/html/rfc3629">RFC3629</a>定死的。</li>
</ol>
<p>Windows Notepad使用UTF-8存储这个文件的结果如下:</p>
<pre><code> EF BB BF E9 94 9F E6 96 A4 E6 8B B7 0D 0A 61 0D 0A
--BOM--- -------- -------- -------- -- -- -- -- --
U+ FEFF 951F 65A4 62F7 000D 000A 0061 000D 000A <--Unicode原始值
</code></pre>
<p>注意UTF-8前边仍然塞进去了<code>U+FEFF</code>按照UTF-8序列化的结果<code>EF BB BF</code>,作为前边提到过的<strong>BOM</strong>字节顺序标记。<strong>Windows Notepad存储的UTF-8,是带有BOM标记的UTF-8</strong>。</p>
<p>但是如果仅仅对于UTF-8而言,字节序是没有意义的。因为UTF-8的字节序被规范写死,<code>U+FEFF</code>编码后必然得到<code>EF BB FF</code>,得不出其他的。没有二义性,BOM就失去了原本的意义。也许只有区别UTF-8文件和UTF-16文件的用处……</p>
<p>如何对待UTF-8文件的BOM,RFC3629的第6章有详细的规定,不加详述。</p>
<p>值得一提的是,BOM我想很多PHP程序员都<a rel="nofollow" href="https://www.google.com/search?q=php%20bom">经历过并且恨之入骨</a>——PHP不认识文件中的BOM头并会将其作为HTTP Response的正文送出。这甚至在无缓冲的情况下,会导致<code>header()</code>等必须在Response开始前执行的函数直接失效。</p>
<p>所以PHP程序员总是会喜欢<strong>UTF-8 without BOM</strong>的编码方式——这基本也就宣布了Windows下的PHP开发,Windows Notepad完全的淘汰出局,哪怕是任何一星半点代码的临时修改。</p>
<h2>番外:Notepad++的字符编码测试</h2>
<p>ANSI没有区别,但Notepad++支持选择多国编码的不同ANSI编码方式(类似浏览器里选编码),可以轻松生成或读取Shift-JIS等其他字符集的文件。适合用于对付日文老游戏的<code>README</code>等文档。</p>
<p>UCS-2 Big Endian、UCS-2 Little Endian和前边UTF-16的两个例子一致。注意UTF-16的文件不提供“无BOM”的存储方法(提供了就坏了)。</p>
<p>UTF-8仍然代表“带有BOM标记的UTF-8”。但同时提供PHP程序员最爱的UTF-8 without BOM,就像:</p>
<pre><code> E9 94 9F E6 96 A4 E6 8B B7 0D 0A 61 0D 0A
-------- -------- -------- -- -- -- -- --
U+ 951F 65A4 62F7 000D 000A 0061 000D 000A <--Unicode原始值
</code></pre>
<p>Simple and clean.</p>
<blockquote>
<p><strong>注解</strong><br><code>[注A]</code> 对于一个双(多)字节的数,一定会按8位截断为1字节后写盘。那么写盘时先写最低8位还是先写最高8位,就是所谓的“字节序”(Endian)问题。例如,数<code>0x01020304</code>写盘时,是先写最低8位的<code>04 03 02 01</code>,还是先写最高8位的<code>01 02 03 04</code>?<br>
先写低8位的叫做小端在先(little-endian),先写高8位的叫做大端在先(big-endian)。实际采用何种字节序受系统环境、标准规范和软件实际编写的多方面控制,不一概而论。<br><code>[注B]</code> 字节序如果我没弄错,是GB2312采用的<a rel="nofollow" href="http://en.wikipedia.org/wiki/Extended_Unix_Code">EUC字符编码方法</a>控制的。<br><code>[注C]</code> 本文并不严格区分<strong>UTF-16</strong>与<strong>UCS-2</strong>。<br><code>[注D]</code> Unicode的最大值实际上达到了U+10FFFF,超出了两个字节能够存储的限度。<br>
但Unicode由于历史原因,留下了U+D800~U+DFFF这一段永久保留不用的空缺区域。<br>
因此对U+10000及以上的字符,UTF-16借助了这部分空缺区域,对这些编码超大的字符打破2字节16位的惯例,特别的用4字节32位去表示之。<br>
这一部分编码值太大的字符,超出了GBK的字符集范围,因此本文将<strong>完全忽略</strong>。如有机会再进一步测试。</p>
</blockquote>
沙渺的硬件收纳与整理教程 (0) 目的与总则
https://segmentfault.com/a/1190000000478459
2014-04-27T20:30:00+08:00
2014-04-27T20:30:00+08:00
沙渺
https://segmentfault.com/u/shamiao
3
<p><strong>起源</strong>: @binaryTree 五花肉开了生活教程(致敬 \-●ω●-/),于是我也跟风开一个<br>
相比人际交往,我还是更喜欢关注自己的生活秩序。这个系列文章聊一聊<strong>硬件器材与装备</strong>的<strong>收纳与整理</strong>,希望大家喜欢 =w=</p>
<hr>
<p>电子工程师、开源硬件玩家、电脑DIY爱好者,随着经验的日趋增长,和时间与金钱的不断投入,囤积的器件和设备也必然越来越多。</p>
<p>东西多了,总应该想到去好好整理一下的。我相信随意摆放弄得像猪窝一样,绝对不是一个好选择……<br>
但我其实一直认为,有一个重要的问题,在动手整理之前应该对自己提——</p>
<h2><em>我们整理硬件装备是为了什么?</em></h2>
<ul>
<li>是为了样子好看一点,不被爸爸妈妈或女朋友骂吗?</li>
<li>是为了不被尖锐的连接器扎手扎脚吗?</li>
<li>是为了防止不小心把板卡一P股坐两半儿吗?</li>
</ul>
<p>其实这些回答都是合理的。但我们往往忽视一点:收纳与整理的目标,其实真的很多很复杂,不能用片面的一两点来概括。</p>
<blockquote>
<p>举个简单的例子:该用木柜还是用层架摆放物品?木柜坚固、防尘,但昂贵且难以搬动。层架容易拆解移动,也相对便宜些,却需要经常擦灰,也更容易翻倒。在这个选择中,“防尘、稳定、价格、灵活性”只能四取二,谁比谁都并没有更好。</p>
</blockquote>
<p><strong>“世间安得两全法,不负如来不负卿”</strong>。收纳与整理,总是要权衡多方的目标,互相妥协,做出一个让自己的生活整体上最方便的选择。</p>
<p>这也就是我开第0篇的内容了——在提到具体的收纳工具之前,先全面的思考一下需要平衡哪些需求。<strong>看清目标,才能知道路怎么走,事怎么做</strong>。</p>
<hr>
<h2>SSSS四原则</h2>
<p>记忆和管理观点的一种流行实践,就是把英文首字母的缩写,编成一个耳熟能详的单词,例如KISS(Keep it simple, stupid)。</p>
<p>总结一下我的经验,我也试着弄了一个这种缩写,就叫“SSSS四原则”——展开后是“速度 稳定 安全 次要”(Speed Stability Safety Secondary)四个词。</p>
<h3>“Speed”速度</h3>
<p>东西整理之后总是要用的,永远不用的东西不该整理,而应该丢弃或抛售。但显而易见的一点是,拿取容器装起来的东西,必然比拿取直接放置的东西费时费力。</p>
<p>我们可以用<strong>查询时间</strong>和<strong>请求时间</strong>来理解这个模型。完全不包装不整理,请求时间最低,但查询时间太长(东西找不到)。而包装整理过度,查询时间省下了,但请求时间又增加(取放麻烦)。</p>
<p>所以既维持物品的整齐(减少查询时间),又给日常的取放保证一个合理的效率(照顾请求时间),这是整理和收纳的首要取舍之一。其中可能的考虑有以下这些:</p>
<ul>
<li>聚类 —— 人以类聚,物以群分。同一类的物品一定要放在一起。</li>
<li>易取 —— 取放步骤一定要少,最大限度减少取放的时间成本。由于设计特殊或者工艺粗劣造成启闭困难的容器,不得采用。</li>
<li>梯度 —— 保持所有物品都100%的同等易取绝对是不可能的。整理的时候必须形成梯度,常用的装备好拿,不常用的东西相对难拿。</li>
<li>消灭二义性 —— 必须保证1个物品只属于1个类别,防止同一种物品分属两边出现查找困难。这一点可能需要整理者的刻意设计。</li>
</ul>
<h3>“Stability”稳定性</h3>
<p>收纳整理是一种<strong>固定资产</strong>投资。一个好的整理方案,应该足够结实耐用,常年如一。只要我们的摆放不出现明显的不合理,就不应当担心整理架等自身是否会损坏。可能的考虑有:</p>
<ul>
<li>坚固 —— 整理设备本身必须耐用,不能用一用就碎裂、坍塌,反而危害物品的安全。有些塑料或轻质金属制品不结实,必须小心。</li>
<li>限重 —— 必须考虑被整理物品本身的重量,合理选择整理设备。一定不要不假思索的认为架子可以无限承载重物。</li>
<li>力学稳定 —— 必须考虑重量和机械结构的合理,绝对不能出现头重脚轻等错误。这一点不光依赖装备,还更加依赖于人的技巧。</li>
</ul>
<h3>“Safety”安全性</h3>
<p>物品整理了之后,往往随之而来的就是长年的存放。这里的安全指的就是如何保证物品本身不会因为长年的存放,而出现生锈、划痕等损伤。</p>
<ul>
<li>防尘 —— 防止灰尘直接落到物品上是第一位的。能尽量减少擦灰的精力那是更好。</li>
<li>机械保护 —— 防止屏幕划伤、连接器碰歪等机械伤害。</li>
<li>理化防护 —— 防止生锈、腐蚀、化学反应等伤害。这些危害对电子设备来说,非常的潜移默化,所以容易被忽视。经常造成的结果就是发现时就不可挽回。所以一定要小心!</li>
<li>意外伤害防护 —— 对于溅水、翻倒、重压等不可知的极端意外,虽然可能无法100%防止,但也要尽量通过收纳设计的可靠性,减少对物品本身的损伤,让收纳包装尽量为物品本身挡上一刀。</li>
</ul>
<h3>“Secondary”次要因素</h3>
<p>所有剩下的事情。</p>
<ul>
<li>节省金钱 —— 这一点放在最后,因为少花钱多办事绝对是奢望,收纳整理必然是个花钱的过程。在保证质量的前提下,享受合理的价格,以躲避地摊破烂货为第一原则,但又不购买过分“土豪”的产品。</li>
<li>美观 —— 基本美观大方即可,很多强迫症的想法最好收敛一下。</li>
<li>便携 —— 人生存的环境也是会变的。真正需要搬运或邮寄的时候,尽量给自己减少点麻烦。</li>
<li>渐进增强 —— 收纳的方案总要更改,不可能一次搞完,不要心存任何一劳永逸的幻想。收纳设计必须在每一步都保持一定的灵活性,防止被锁死在某种方案里出不来。</li>
</ul>
<h2>预告</h2>
<p>以上就是我能想到的总则。相信这些原则,能给收纳和整理的设计,提供一些好的参考,节省一些完全依赖自己思考的时间和难度。</p>
<p>我认为收纳与整理这种事情,<strong>观念高于金钱</strong>。其实整理架等装备,谁省一省钱都买得起。</p>
<p>愿不愿意维持一个规整的工作空间,多半看个人的观念和执行力。</p>
<p>爱上秩序和整洁,让我们从今天开始。</p>
<p><strong>下期预告:对付电阻、电容、晶振、LED等常规小元件的技巧(非开关类、非接插件、非IC)</strong>。下周末我们再见!</p>
<hr>
<p>《沙渺的硬件收纳与整理教程》系列文章,介绍电子工程师与硬件DIY爱好者,如何整理保存自己的板卡、元件、设备和其他物品。<br>
每周六日更新一篇,敬请期待!</p>
<p>SegmentFault原创内容(原载:<a rel="nofollow" href="http://blog.segmentfault.com/shamiao/1190000000478459">http://blog.segmentfault.com/shamiao/1190000000478459</a>)<br>
转载使用请遵守本站相关声明。<br>
本文责任者:@shamiao</p>
开源硬件平台全新简介 (2) Raspberry Pi “树莓派”(下): 问题与总评
https://segmentfault.com/a/1190000000465449
2014-04-14T17:35:00+08:00
2014-04-14T17:35:00+08:00
沙渺
https://segmentfault.com/u/shamiao
4
<p><img src="http://segmentfault.com/img/bVb7fP" alt="(标题图)"></p>
<p>我们在上篇《<a rel="nofollow" href="http://blog.segmentfault.com/shamiao/1190000000451314">开源硬件平台全新简介 (1) Raspberry Pi “树莓派”(上): 概述与优势</a>》中,介绍了Raspberry Pi的基本信息与竞争优势。当时留了个坑:“可以赞,就当然必须喷,没得喷不科学”。今天就填这个大坑。</p>
<p>(因为参加<a rel="nofollow" href="http://segmentfault.com/e/shenzhenmakerfaire-2014">Maker Faire Shenzhen</a>深圳制汇节造成的延期深感抱歉。欢迎您阅读我们对这场展会的所有原创报道!)</p>
<h2>树莓派林林总总的所有问题</h2>
<h3>博通,你要摁着多少资源自己专属?</h3>
<p>做为树莓派ARM核心的<a rel="nofollow" href="https://www.broadcom.com/products/BCM2835"><strong>Broadcom BCM2835</strong></a>这个芯片是<strong>专供</strong>树莓派的。这就造成了事实上无法采购这个芯片,用于复制、仿制或定制基于树莓派的产品。</p>
<p>更为残忍的是,BCM2835的手册(Datasheet)也不向公众提供,必须由合作商签订保密协议(NDA)才能获得。这样就造成了就算得到树莓派的电路图(基金会开放电路图倒是真的),也不能完全理解图中涉及BCM2835的部分。</p>
<p>并且博通对其VideoCore IV显示核心的驱动程序,也采取了和很多驱动程序一致的闭源发布。这就造成了依赖树莓派显示核心的程序,很难迁移到使用其他显示核心(例如Mali)的ARM SoC上:<br><img src="http://segmentfault.com/img/bVb7YA" alt="显示核心驱动的闭源状态说明"></p>
<p>这两点都是在树莓派的热销压力之下才有所改变。博通对BCM2835,放出了一个部分的Datasheet描述了芯片的外部总线(但仍没有放全部的Datasheet)。而VideoCore IV显示核心的文档和相关代码,则一直让所有的开发者整整等待了两年,直到<a rel="nofollow" href="blog.broadcom.com/chip-design/android-for-all-broadcom-gives-developers-keys-to-the-videocore-kingdom">2014年的2月份才对外提供</a>。而后还要树莓派基金会去填坑,<a rel="nofollow" href="http://www.raspberrypi.org/quake-iii-bounty-we-have-a-winner/">举办竞赛</a>号召爱好者去把这部分资料应用到树莓派上。</p>
<p>但根本的一点到现在仍然没有变:<strong>树莓派仍然难以仿制和改进,不可能对树莓派去实际执行开源项目的fork-republish机制</strong>。为了增强树莓派的定制性,树莓派基金会最近推出了一款笔记本内存大小的核心板,只包含BCM2835和内存,任何对外接口允许自己连接。——但在硬件设计没有变化的情况下,这种努力不过是一种临时手段(workaround),改变不了树莓派无法遵循开源模式的硬伤。</p>
<p>所以SUSE中国的一位资深工程师(恕我忘记尊姓大名)在<a rel="nofollow" href="http://www.bjgug.org/node/693/">2013年的一次线下聚会</a>中,甚至不将Raspberry Pi列入开源硬件之列。这不是没有道理的——不能复制,不能改进,开源软件只运行在皮毛的层次上,这样和传统闭源的PC,在概念上又有什么区别?</p>
<p>更有极端的说法直接称博通为“开源的敌人”——其实认真审视一下,这个说法虽然激烈,但恐怕也并不特别过分。</p>
<h3>基金会,你挖了多少坑没填?</h3>
<p>树莓派基金会真的很努力,也促成了很多和树莓派有关的开发项目。但不得不说的是,树莓派至今为止,仍然有很大一部分工作仍然“挖坑不填”。</p>
<p>用最大的一个坑举例子:<strong>DSI显示接口</strong>。<a rel="nofollow" href="http://en.wikipedia.org/wiki/Display_Serial_Interface">DSI</a>是连接液晶裸屏(或模块)的一个硬件接口,从使用情况来看极不流行,很难买到DSI接口的屏幕。在实用价值上,远远落后于常用于液晶电视、笔记本屏幕的LVDS接口。树莓派采用DSI也许有BCM2835硬件设计上的理由。但两年时间过去了,树莓派仍然没有拿出使用DSI接口实际驱动出一个屏幕的例子。</p>
<p>……这样问题就很简单了:那当初设计这个DSI口是做什么的?!</p>
<p><img src="http://segmentfault.com/img/bVb7Zi" alt="树莓派DSI接口照片"><br>
▲ 基金会,你为什么让所有树莓派的DSI接口一直吃灰?(<a rel="nofollow" href="http://en.wikipedia.org/wiki/File:RaspberryPi_Display_Serial_Interface.jpg">图片</a>协议:CC-BY-SA)</p>
<h3>软硬件设计,还要弄出多少缺陷才算完?</h3>
<p>树莓派的软硬件设计真的不怎么好,这一段要重点说。举3个例子。</p>
<h4>怎么给电都不够的5V电源</h4>
<p>树莓派使用单一5V供电,电源本身并不做任何处理,所有5V的设备直接取电,只有CPU的供电用数个低压差(LDO)单独提供。[注B]这本来是一种很简单、显然、低成本的<strong>无管理</strong>电源设计。但偏偏树莓派在电源上串联了一个<strong>自恢复保险</strong>(PTC)进去,试图防止出现短路、过流等故障。</p>
<p>PTC是一种简单的过流保护器件,过流时发热造成内阻上升抵抗过流,异常解除后可以冷却让内阻下降,恢复设备工作。但PTC简单就有简单的问题:PTC自身的电阻不低(实测很可能有0.2Ω以上),串接在电源上容易把电源电压拉下0.3-0.5V左右的程度。——对5V这个低压来说,电源跌落这个程度就很成问题了,极易造成依赖5V的USB等外设欠压异常。</p>
<p>事实上如果供电电压是5.2-5.4等比5V整数稍微高点的规格,那么树莓派得到的电压还可能在5V上。如果电源老老实实提供5V,那树莓派就麻烦了。这也就是许多人误解“<em>树莓派对电源要求高</em>”的原因——实际上要求高不是性能需要,只是一个设计错误使然。</p>
<p><img src="http://segmentfault.com/img/bVb70L" alt="树莓派F3保险丝的特写"><br>
▲ 一颗自作聪明的保险,搞坏了所有的事情。</p>
<h4>塞车又缺电的USB接口</h4>
<p>BCM2835处理器实际上只有<strong>1个</strong>USB主接口。这个接口也是树莓派唯一的高速数据通道。</p>
<p>常见的树莓派B型,虽然有两个USB和一个以太网接口,但这都是板载的<a rel="nofollow" href="http://www.microchip.com/wwwproducts/Devices.aspx?product=LAN9512">LAN9512</a>芯片的杰作[注C]。这三个接口实际上只是共享1个USB主端口的480Mbps带宽。所以树莓派的USB总线塞车,是每一个树莓派玩家心中的痛。只要图像采集、文件传输等有数据吞吐的任务稍微要求高一点,树莓派就必然的缴枪投降。</p>
<p>另外树莓派对USB的供电,只是把+5V接了上去,根本没有为USB设备上电时的突波做去耦设计[注D]。这样造成的恶果是哪怕USB设备的工作电流本身不大,其插入时的瞬时大电流也会导致CPU供电不足而重启。这个问题不挑设备,社区使用U盘、USB无线网卡等实测都出现过。</p>
<p>(所以对树莓派而言,用一个有电源输入的USB HUB,把USB的供电单独解决掉是必须的。社区里每天都在讨论“有源HUB怎么选”的问题。)</p>
<p>这又是一个弱智的毛病。……基金会你们做出样板之后,难道不连接各种各样的USB设备测试一下吗?</p>
<p><img src="http://segmentfault.com/img/bVb73w" alt="一个典型的有源USB HUB"><br>
▲ USB的热插拔本来应该无条件做到。为什么树莓派要我们自己花钱补充这种破烂才行?</p>
<h4>让新手与老手一起头痛的NOOBS</h4>
<p>树莓派的启动需要一个FAT32的Boot分区,和一个(或数个)Ext4的Linux主分区。这也是Linux系统的典型需求。树莓派原来的系统安装方式,是简单的用烧写工具,把.img系统镜像绕过分区表直接写盘。写好盘后分区表结构也就自动建立好了。但偏偏基金会认为这样还不够,“我们要直接把文件拷进SD卡里就能用”!于是基金会推出了官方的启动前运行环境“<a rel="nofollow" href="http://www.raspberrypi.org/introducing-the-new-out-of-box-software-noobs/">NOOBS</a>”。(noob /nu:b/ n. 菜鸟、新手)</p>
<p>使用NOOBS需要把NOOBS软件包(内置数个系统的大压缩包)拷入SD卡。NOOBS允许用户选择一个系统,利用SD卡的剩余空间,在树莓派上现场解压并建立分区结构。NOOBS不是真正的系统多启动工具,一次只能在SD卡的剩余空间里装一个系统,不过倒可以随时把系统推掉换成别的。</p>
<p>NOOBS其实只是带来了许多的麻烦:</p>
<ul>
<li>拷贝NOOBS意味着要同时把多个系统拷入SD卡,复制时间浪费极其严重,对于Class4的低速SD卡尤其难以忍耐。</li>
<li>NOOBS带着好几个系统镜像常驻SD卡,浪费空间。4GB及以下卡实际上根本无法使用NOOBS。</li>
<li>常驻就意味着陈旧。SD卡上NOOBS带的系统镜像,难以得到最新的更新。</li>
<li>不能真正的多启动,那为什么还要在SD卡上常驻这么多系统?</li>
<li>如果系统真要推掉重来,为什么不用电脑?</li>
</ul>
<p>……所以这主意到底谁想出来的?!刷卡操作也许确实需要给新手些微的指导,但提出NOOBS这种一堆麻烦替代1个麻烦的方案,到底是一种什么样的做事哲学啊?!</p>
<p><img src="http://segmentfault.com/img/bVb73c" alt="NOOBS安装系统的过程"><br>
▲ 系统刷写本该是计算机的任务。NOOBS如此越俎代庖是几个意思?</p>
<h2>对树莓派的总评</h2>
<p>综合社区这两年所有的抱怨,我们的批评大概也就以上这些了。</p>
<p>也许喷了树莓派很多,但请所有读者都不要忘记:<strong>这都只是硬币的另外一面</strong>。</p>
<p>树莓派目前仍然是硬件条件相对成熟,软件资源最为完备,社区支持最为广泛,英文资料最为全面,方案参考最为充足的硬件。<br>
树莓派也许不那么开源,但仍然是实践开源硬件工作的最好利器——也许是最好,不是也可以称为“最好之一”。</p>
<p>如果你是Linux和硬件开发的新手,那么树莓派目前肯定是不二之选。因为使用树莓派,意味着最大程度的保证成功,降低新手上路的挫折感。我们感觉对新手而言,也许其他的硬件也值得去尝试,但树莓派恐怕很大程度上还是必须的,同时也是很推荐放在第一个购买的。</p>
<p>当然,如果您已经是Linux/嵌入式/单片机/工控/通讯等任何一个领域的有经验人士,那么就未必需要一定去购买树莓派。您大可先了解树莓派的参数、能力、优点和问题,自行评估购买适合您的硬件平台。</p>
<p>关于树莓派,我们的介绍就是这些了。下集预告:<em>pcDuino</em>(全一篇),敬请期待。</p>
<h2>声明</h2>
<p>任何一篇文章,都无法完全覆盖每一个人各种条件的差异。对于部分人群,本文的参考一定会出现或多或少不适用的情况。<br>
因此本文全部的学习与采购建议<strong>仅供参考</strong>。开发者请按照个人情况,综合各种观点<strong>自行决断</strong>。</p>
<h2>注解</h2>
<blockquote>
<p>[注A] 题外话——我们信奉这一点:<strong>厂商只要宣传了硬件的能力,就必须给出相应的证明</strong>。<br>
有许多硬件厂商,硬件参数叫得响,“超越×××”吹上天,却拿不出可以运行的程序代码去证明硬件的能力。<br>
这种厂商和骗子没有区别。有句话就是送他们的:“<em>Talk is cheap. Show me the code.</em>” (<a rel="nofollow" href="https://lkml.org/lkml/2000/8/25/132">Linus, 2000</a>)<br>
回头审视树莓派——说实话,树莓派虽然还有坑没填,但已经填好的坑,在数量和质量上都已经比其他厂商绝对优秀了。</p>
<p>[注B] 细节不完全准确,但基本上如此。</p>
<p>[注C] LAN9512芯片充当HUB将USB口1分为3,并在芯片内部,把其中一个USB口永久连接上了USB有线网卡。<br>
因此对系统是HUB×1+USB网卡×1,对外是USB×2+以太网×1,所有设备分割1个Host端口的带宽。</p>
<p>[注D] 各种电子设备在上电(或工作负载突然增加,例如CPU压力测试)时总会出现电流(负载)突然增大的现象。<br>
如果负载只是单纯连接电源,负载突变就必然导致电源电压突变,影响甚至危害连接同一电源的其他单元电路。<br><strong>去耦</strong>就是使用必要器件吸收单元电路中负载的突变,让负载突变不反映到电源电压上。这个名称之意就是“去除单元电路与整体电路的耦合”。</p>
</blockquote>
<hr>
<p><img src="http://segmentfault.com/img/bVb4n4" alt="(SF开源硬件交流区宣传:子站openhw.sf.gg,Q群3722308136,欢迎加入!)"></p>
<p>SegmentFault原创文章,转载请遵守本站的有关声明。</p>
开源硬件平台全新简介 (1) Raspberry Pi “树莓派”(上): 概述与优势
https://segmentfault.com/a/1190000000451314
2014-04-02T18:00:00+08:00
2014-04-02T18:00:00+08:00
沙渺
https://segmentfault.com/u/shamiao
11
<p>TFT:<br>
(1)RPi上拖稿,RPi下、Arduino、BBB、CubieBoard1/2延期,非常抱歉。<br>
(2)我将参加SegmentFault在<a rel="nofollow" href="http://segmentfault.com/e/shenzhenmakerfaire-2014">深圳制汇节</a>的活动,届时欢迎关注SgF的相关报道!</p>
<p><img src="http://segmentfault.com/img/bVb4od" alt="(标题图)"></p>
<p>提到开源硬件,我们就从近两年最火爆的Raspberry Pi “树莓派”开始。</p>
<h2>Raspberry Pi</h2>
<p>Raspberry Pi问世于2012年,是尺寸仅有信用卡大小的一个小型电脑,基于ARM架构。公认的译名为“树莓派”,通常简称为Ras-Pi、RPi。</p>
<p>树莓派的开发者是英国的树莓派基金会(The Raspberry Pi Foundation)。树莓派使用<strong>博通(Broadcom)</strong>公司的核心芯片,这也是目前已知唯一的博通处理器的开发平台。</p>
<p><img src="http://segmentfault.com/img/bVb4mY" alt="图:Raspberry Pi “树莓派”主机"><br>
▲ 一台安装了外壳(非标配)并连接了USB外设的“树莓派”主机</p>
<h3>历史</h3>
<p>树莓派的最主要设计者是剑桥大学Eben Upton博士,他也是目前树莓派基金会的带头人。</p>
<p>2006年Eben在剑桥大学发现,学校计算机专业入学申请者的编程能力,明显呈现出逐年下降的趋势。以前的申请者都是真正的少年黑客,而现在的能写过几个静态网页就很不错了。</p>
<p>Eben认为这个现象的成因是昂贵的PC不适合给青少年们实践编程知识。青少年需要一个廉价、好玩、易定制,不怕折腾,不易损坏的开发平台。因此Eben以20世纪80年代英国的一种家用电脑BBC Micro为蓝本,提出了树莓派硬件的最初设想。BBC Micro在概念和功能上,和我国20世纪90年代的各种“学习机”非常相似。</p>
<p><img src="http://segmentfault.com/img/bVb4mW" alt="图:BBC Micro和“中华学习机”"><br>
▲ 英国的BBC Micro,和中国的“中华学习机”(Apple II兼容型微机)。这种概念构成了树莓派的灵感来源</p>
<p>而后随着2008年智能手机硬件的发展,廉价而性能足够强劲的移动处理器芯片,为树莓派的问世铺平了道路。之后Eben成立了树莓派基金会,宣布树莓派的目标为“造价25美元,运行Linux,信用卡尺寸,可以连接电视机,有高清视频播放能力”。</p>
<p>这个新硬件诱人的性能特点,吸引了包括媒体、计算机爱好者、软件开发者、硬件极客在内多方的注意。因此树莓派在2012年一开始发售,就出现了异常火爆的场面,甚至基金会一度需要通过限购手段满足市场需求。</p>
<p>经过基金会和社区长时间锲而不舍的努力,树莓派如今已经成为了软硬件资料最为丰富,开发者中最为流行的硬件设备。其中过程不再一一详述,但必须强调的是:<em>初期火爆之后还能长时间坚持推广和开发,才是树莓派项目最可贵的地方</em>。</p>
<h3>硬件配置</h3>
<p>树莓派根据配置的高低分为A型和B型。两种型号只有资源多寡略有差异,电路板和软件都没有任何区别。</p>
<table>
<thead><tr>
<th>项目</th>
<th>内容</th>
<th>注解</th>
</tr></thead>
<tbody>
<tr>
<td>系统核心</td>
<td>Broadcom BCM2835</td>
<td>包含CPU, GPU, 内部总线, 1个USB Host端口</td>
</tr>
<tr>
<td>处 理 器</td>
<td>ARM1176JZF-S @ 700MHz</td>
<td>ARM11系列,ARMv6指令集</td>
</tr>
<tr>
<td>图形核心</td>
<td>Broadcom VideoCore IV</td>
<td>内置高至1080P@30fps的H.264视频硬解</td>
</tr>
<tr>
<td>内 存</td>
<td>SDRAM,与显存共享</td>
<td>A型256MB,B型512MB</td>
</tr>
<tr>
<td>存 储</td>
<td>无内置存储,使用SD卡</td>
<td></td>
</tr>
<tr>
<td>电 源</td>
<td>5V,microUSB或GPIO端口</td>
<td>要求供电能力:A型300mA,B型700mA</td>
</tr>
<tr>
<td>网 络</td>
<td>100Mbps有线以太网</td>
<td>A型无内置网络,AB型均可加装USB无线网卡</td>
</tr>
<tr>
<td>外设端口</td>
<td>USB 2.0</td>
<td>A型1个,B型2个</td>
</tr>
<tr>
<td>显示设备</td>
<td>1. HDMI端口,支持自定义分辨率<br>2. 模拟视频接口(复合视频, RCA莲花线)<br>3. DSI液晶模块接口</td>
<td>HDMI高至1920x1200<br>模拟视频支持NTSC/PAL制式</td>
</tr>
<tr>
<td>音频设备</td>
<td>标准3.5mm立体声输出</td>
<td>无音频输入,可加装USB声卡</td>
</tr>
<tr>
<td>摄 像 头</td>
<td>CSI摄像头模块接口</td>
<td></td>
</tr>
<tr>
<td>底层扩展</td>
<td>26pin标准2.54mm间距GPIO端口</td>
<td>包含I2C, SPI, UART串口功能<br>支持5V工作电源输入/输出</td>
</tr>
<tr>
<td>尺寸规格</td>
<td>85.6 mm x 56 mm,重45g</td>
<td></td>
</tr>
<tr>
<td>操作系统</td>
<td>多种Linux发布版<br>(Debian, Arch, Fedora等)</td>
<td>另有非Linux系统的RISC OS</td>
</tr>
<tr>
<td>官方标价</td>
<td>A型25$,B型35$</td>
<td></td>
</tr>
<tr>
<td>国内价格</td>
<td>B型¥240~280(主流)</td>
<td>A型货源极其稀少而不计</td>
</tr>
</tbody>
</table>
<ul>
<li>价格均只含树莓派裸板,不含运行所必需的存储卡、电源或其他外设。</li>
<li>树莓派只在2012年年中,对电路布局做过一次微小改动推出了“修正版(Rev.2)”,根本不足以称为“第二代”。</li>
<li>目前出货的树莓派,均采用“修正版(Rev.2)”的电路板图,最初版图的产品已经不再出货。</li>
<li>基金会表示短时间内没有推出第二代硬件的计划。</li>
</ul>
<h2>购买的理由与优势</h2>
<h3>基本合理的性价比</h3>
<p>在当前流行的高性能ARM开发板的范围内,树莓派是最低价的产品。字面意思,不需要任何解释。</p>
<p>其他开发板基本需要¥300以上的投入,而树莓派由于货源广泛、竞争充分,现在已经能做到¥240~280的价格。这个价格范围已经非常接近$35直接换算为人民币的字面价格。</p>
<p>而在这个价格上,树莓派提供的性能也是合理的。ARM11核与512M的内存已经足够提供一般的计算性能。</p>
<h3>开发厂商的不懈投入</h3>
<p>软件的开发对于充分发挥开发平台的性能至关重要。没有软件的开发动作,硬件做的再牛也是一堆废铁,或者说只是一些可望而不可及的“可能性”,不能让用户真正的受益。</p>
<p>树莓派基金会在这一点上是非常积极的,主导了数项紧密联系树莓派硬件,直接惠及开发者与用户的软件项目。目前已经取得进展的成果例举几项:</p>
<ul>
<li>向用户免费提供树莓派专版的Mathematica和<a rel="nofollow" href="http://segmentfault.com/a/1190000000420077">Wolfram Language</a>
</li>
<li>向用户免费提供树莓派专版的Minecraft for Pi</li>
<li>替代X11的新一代底层显示服务器Wayland</li>
<li>编译显示驱动,以及运行开源游戏《雷神之锤3》的Step by Step指导</li>
<li>……</li>
</ul>
<p>这一点和部分厂商卖出硬件,抛出少的可怜的资料就算完事大吉的作风形成了鲜明的对比。我希望请所有的读者注意这一点:</p>
<blockquote>
<p>大肆宣传硬件有什么什么样的NB能力,而不提供(或不努力去提供)必要的软件支持去证明之,这种厂家和骗子没有任何区别。</p>
</blockquote>
<h3>社区的广泛参与</h3>
<p>树莓派的广泛流行,让开源社区的开发者们也为树莓派贡献了很多的成果。包括定制操作系统的整体镜像,二次开发函数库,自定义软件等,不一而足。例举几项:</p>
<ul>
<li>WiringPi, <a rel="nofollow" href="http://segmentfault.com/a/1190000000345451">RPi.GPIO</a>等底层总线操作库</li>
<li>RaspBMC, OpenELEC等家庭媒体中心专用系统</li>
<li>RetroPi, PiMAME等游戏主机模拟器专用系统</li>
<li>自由免费的《The MagPi》树莓派月刊</li>
<li>……</li>
</ul>
<h3>讨论和交流的便利</h3>
<p>做一名树莓派的用户是幸福的。</p>
<p>树莓派的使用人数最为广泛,网络上专门的论坛、网站、博客众多,所以出现问题很容易直接找到针对树莓派的直接答案,而不必“曲线救国”先寻找台式机Linux的做法再去套用到自己的硬件上。</p>
<p>另外树莓派问世并最初推广于英语文化圈。由于其交流气氛更加良好,更加注重版权与分享,因此出了问题即使中文资料比较缺乏,也相对较容易用英文搜索得到答案。这一点也很值得一提。</p>
<p>最后由于树莓派本身没有版本区别,整个社区集中在一个硬件上做事,因此互相交流经验、分享技术方案乃至于直接交换系统镜像,都不会碰到任何的障碍,非常方便。</p>
<h2>下篇预告</h2>
<p>树莓派作为最流行的硬件开发设备之一,有值得赞的地方,就当然有必须喷的地方,没有不正常。</p>
<p>所以下篇我们将大开喷戒,写一写树莓派所有已知的坑。</p>
<p>我们将在下篇最后,给树莓派下一个总评,并给出我们作为普通开发者对这个硬件购买与否,以及如何看待的建议。</p>
<p>下篇已经发布<a rel="nofollow" href="http://blog.segmentfault.com/shamiao/1190000000465449">!《开源硬件平台全新简介 (2) Raspberry Pi “树莓派”(下): 问题与总评》</a></p>
<p><img src="http://segmentfault.com/img/bVb4n4" alt="(SF开源硬件交流区宣传:子站openhw.sf.gg,Q群3722308136,欢迎加入!)"></p>
开源硬件平台全新简介 (0) 序章与目录
https://segmentfault.com/a/1190000000447884
2014-03-27T15:36:00+08:00
2014-03-27T15:36:00+08:00
沙渺
https://segmentfault.com/u/shamiao
6
<p>FTF:我已经加入了SegmentFault任职技术运营。SgF未来将开始关注包括Arduino、Raspberry Pi等各种开源硬件领域的开发与社区活动,我将重点承担这方面的工作,敬请期待 :D</p>
<h2>序章</h2>
<h3>开源硬件与创客运动</h3>
<p><strong>开源硬件</strong>是开源软件的延伸——以开源软件的工作模式,制造原理图与代码自由开放,可复制、可研究、可改进的硬件产品。</p>
<p><strong>创客运动</strong>是电子DIY的延伸——不把盈利当做一开始的目标,而是从“玩"起步,制造一些能提供实际方便的硬件产品,从中尽可能地创造价值。</p>
<p>开源硬件和创客运动共有社区先于商业,注重个人实践与小团体交流的特点,所以很自然的交织在了一起——相比于传统的学习方式,通过开源硬件进行学习更便于入门和交流;而开源硬件设备本身,又为创客运动提供了正式硬件平台,或是快速原型平台的基础支持。</p>
<p>硬件开发,已经在互联网精神与开源运动的强力驱动下,进入了一个全新的时代。</p>
<h3>开源硬件平台:有待重新审视的基石</h3>
<p>SegmentFault对开源硬件的内容建设,计划从重新评测市面上所有的开源硬件开发板开始。也许看起来这些开发板都有自己的简介,我们的工作不过是内容的重复,但其实不然。</p>
<p>市面上的开发板,经过了1-3年的发展,各自都发生了不同的变化。有的取得了长足进步,有的普普通通可以使用,有的毫无进步进而消亡。我们希望能以现在的眼光,公允的重新审视所有平台在软硬件(尤其是软件)的现有成果,帮助所有人做出自己的选择,而不至于陷入某些厂家的<strong>浮夸、吹嘘,乃至片面强调硬件参数的阴谋当中</strong>。</p>
<p>我们的收录标准是:(如有特殊价值,则可以考虑突破标准)</p>
<ul>
<li>¥500元以下</li>
<li>发售中,容易通过淘宝等普通渠道购买</li>
<li>不局限于电子工程师,而是在普遍的开发者社区中有一定影响</li>
</ul>
<h2>收录列表</h2>
<p>以下就是本系列文章待填的坑。完成时间都是严肃的,您可以收藏本索引贴并随时关注。</p>
<h3>基于单片机</h3>
<p>这一系列产品基于8位单片机,特点是速度、存储等资源特别的低(往往低至字节级),需要的编程技巧比较高,但实现的功能非常单一。不过单片机作品价格低廉,极易重复,耗电低,体积小,且不需要很高的加工工艺。</p>
<ul>
<li>Arduino <code>[TBD:14/4/1]</code>
</li>
</ul>
<h3>基于ARM11</h3>
<p>基于ARM的产品,都有与当前的智能手机等同的高性能,能够胜任图像处理、桌面环境、复杂互联网功能等多种开发任务。ARM开发平台经常采用Linux或Android系统,这也极大方便了普通的软件开发者入门硬件开发。(当然在耗电、小型化、重复生产等方面ARM也远不及单片机,对付简单需求并非非ARM不可)</p>
<p>ARM11是最后一代的经典ARM架构,使用ARMv6指令集。基于ARM11的开发板只有全球最为流行的ARM开源硬件平台:“树莓派”Raspberry Pi。</p>
<ul>
<li>
<a rel="nofollow" href="http://blog.segmentfault.com/shamiao/1190000000451314">Raspberry Pi (上)</a> <code>Published</code>
</li>
<li>
<a rel="nofollow" href="http://blog.segmentfault.com/shamiao/1190000000465449">Raspberry Pi (下)</a> <code>Published</code>
</li>
</ul>
<h3>基于ARM Cortex-A</h3>
<p>在ARM11之后,ARM产品线分化位Cortex-A/R/M。Cortex-A就是ARM的最高端产品,广泛用于需要丰富功能的智能终端设备,采用ARMv7指令集。ARM Cortex-A是当前所有智能手机处理器采用的架构。</p>
<p>目前所有其他的ARM开发板全部基于ARMv7,根据处理器的生产厂家分类为:</p>
<h4>珠海全志(Allwinner)系列</h4>
<ul>
<li>CubieBoard (1, 2) <code>[原定14/4/1,延期到深圳制汇节之后]</code>
</li>
<li>CubieTruck (=CubieBoard 3) <code>[TBD:14/4/?]</code>
</li>
<li>pcDuino (v1, v2) <code>[TBD:14/4/?]</code>
</li>
<li>Banana Pi <code>[TBD:待定]</code>
</li>
</ul>
<h4>德州仪器(Texas Instruments,德仪/TI)系列</h4>
<ul>
<li>Arduino TRE (前瞻) <code>[TBD:14/4/?]</code>
</li>
<li>BeagleBone Black <code>[原定14/4/1,延期到深圳制汇节之后]</code>
</li>
</ul>
<h4>瑞芯微(Rockchip)系列</h4>
<ul>
<li>MarsBoard <code>[TBD:14/?/?]</code>
</li>
<li>WaxBerry (01, 02) <code>[TBD:14/?/?]</code>
</li>
</ul>
<h3>基于x86</h3>
<p>这是英特尔不甘被开源硬件的大潮所抛下的产物。英特尔计划将x86设备引入开源硬件,从而推出了被调侃为“最差性能在售x86”的“夸克”处理器,以及对应的“伽利略”开发板。“夸克”处理器确实相比目前的x86计算机性能低下,但其能以单一低电压工作的特点,以及与其他开源硬件板卡同一尺度的体积,再加上x86架构得天独厚的兼容优势,使得英特尔在开源硬件领域中也争得了一个不容忽视的地位。</p>
<ul>
<li>Intel Galileo <code>[TBD:14/4/?]</code>
</li>
</ul>
从印刷学的观点略谈字体显示的现在和未来
https://segmentfault.com/a/1190000000401380
2014-01-30T03:41:50+08:00
2014-01-30T03:41:50+08:00
沙渺
https://segmentfault.com/u/shamiao
14
<p>来自于<a href="http://segmentfault.com/q/1010000000401308">这个</a>问题。</p>
<p>文字是人类最容易加工、查询、保存,并且最为准确的表意工具。<strong>内容为王</strong>,追究文字的观感,对人类阅读文字材料的工作是相当重要的。</p>
<p>乔布斯在做苹果之前就有研究字体设计的经历,这在之后也成为了苹果设计思想的一部分。现在看来,字体设计的独到之处,也是苹果为什么成为苹果的一个意义非常深的理由。</p>
<p>注:</p>
<ol>
<li>信心一贯不高,因此略谈成性。有错误请不要见怪。</li>
<li>图没做,有时间会补的。</li>
<li>本文并不刻意区分<strong>Retina屏幕</strong>和<strong>视网膜屏幕</strong>两个名词。</li>
</ol>
<h3>衬线和非衬线</h3>
<p>前端设计师可能会熟悉,写<code>font-family</code>字体列表时,最后一定要有一个<code>serif</code>或<code>sans-serif</code>兜底。这两个名称就代表了采用浏览器的默认<strong>衬线</strong>和默认<strong>无衬线</strong>字体,作为前边都匹配不到时的最后选择。</p>
<p><strong>衬线</strong>(serif)这个概念来源于字体排版印刷学,代表除了笔画本来必要的形状之外,在笔画起始端、末端和关键节点处多出来的装饰形状。衬线体的代表是<strong>宋体</strong>和<strong>Times</strong>。例如“口”这个字,必要的形状只是一个方块。但用宋体写一个“口”,可以看到在四个角都有多出来的形状。</p>
<p>而<strong>非衬线</strong>(sans-serif, sans是哪个语言的否定前缀来着?)就是相反的意思,字形只包含笔画的必要形状。非衬线体的代表是<strong>黑体</strong>和<strong>Arial</strong>。</p>
<p><img src="http://segmentfault.com/img/bVbS3m" alt="衬线字体和非衬线字体" title="衬线字体和非衬线字体"><br>▲ 衬线字体和非衬线字体</p>
<p>衬线体的优点是能够改善<strong>大段文字的阅读效果</strong>。不知道你是否看过这样一个笑话一般的实验:</p>
<blockquote>把一段话的英文单词,只保留首末字母不动,中间全部打乱,仍然能够正确阅读:<p>Aoccdrnig to a rscheearch at Cmabrigde Uinervtisy, it deosn't mttaer in waht oredr the ltteers in a wrod are, the olny iprmoetnt tihng is taht the frist and lsat ltteer be at the rghit pclae. The rset can be a total mses and you can sitll raed it wouthit porbelm. Tihs is bcuseae the huamn mnid deos not raed ervey lteter by istlef, but the wrod as a wlohe. Amzanig!</p>
<p>“剑桥大学出品”这个也许是编的,这段文字本身的结论其实也不完全准确。正确的结论应该表述为:<strong>存在于上下文中的单词,关键音节保持不动,其他部分打乱之后,仍然几乎不会影响阅读的准确性。</strong></p>
<p>(例如:第一个词<em>Aoccdrnig</em>,其实不光<em>A</em>和<em>g</em>不动,在<em>Accord</em>和<em>ing</em>断开位置的音节也没有动,所以才容易阅读)</p>
</blockquote>
<p>提起这个是说明,人类在阅读文字和图形的时候,都有<strong>识别关键点</strong>的天然习惯。只要关键点对上了,人类对图形的判断就决定了个八九不离十。而剩余的部分,哪怕看起来占的面积再多,也只有让人类更加确信一点儿的价值。</p>
<p>衬线字就抓住了人类的这个思维特点。人类对<strong>字形</strong>的认识来源于书写,而书写的关键在于:(1)起点和终点、(2)笔画必须通过/折返的关键点。(例如,我们写一个数字7,重点就在于左上角起头,右上角折弯,正下方收笔。至于两条笔画是直是弯,弧度多少,并没有太多的意义。)</p>
<p>在人类需要花费时间阅读大量文字时,每个文字的阅读时间就变得重要。不同的文字虽然笔画有差异,但决定字形的关键点,其实都不会非常多。并且笔画是1维的线条,多一条少一条比较敏感;而关键点是没有维度的点,多几个感觉也不太明显。</p>
<p>所以使用衬线字体,允许人类迅速抓住字形的关键点,就可以用快速、均匀的速度读出每一个文字。衬线字体阅读文字的时间如果画一条曲线,会是<strong>比较平直</strong>的。相反,如果用非衬线字体,就会出现简单字读得快,复杂字读得慢的现象,时间曲线是<strong>起伏明显</strong>的。选择哪个更加舒服,这很清楚。</p>
<p>但衬线体的麻烦在于:不好对付少量大号的文字。因为这时文字量少,意义一目了然,识别字形的时间已经不重要了。而在字号放大之后,衬线字体就会由于多余的图形“花眼”而显得难看。所以现在报纸和书籍的标题,一般都倾向于非衬线体。</p>
<p>衬线体排正文,非衬线体打标题,这呈现了一种天然互补的形态。</p>
<h3>字体光栅化:妥协与混乱</h3>
<p>一切都随着显示器的发明而改变了。</p>
<h4>计算机显示与印刷学的不一致</h4>
<p>一切都源于<strong>dpi (dot per inch = 点每英寸)</strong>,这项表征图形点阵疏密程度的指标的区别。</p>
<p>印刷技术可以轻易达到250-300dpi的密度,因此可以轻易的在一个小空间内,表现出字体很多的细节。印刷品拿普通放大镜看,细节也是清楚的,只有拿40X以上的<a href="https://link.segmentfault.com/?enc=6kCRH26qkjufAMIsDLYi1Q%3D%3D.GPGyM9iwkOqx32wWorXVThHdVmyqyMj%2F%2Fmxq1Mgjxd%2BfV3O4Wqs5GZKgOm4MR1%2BPTJ%2BGREsWJjCGQ41ekCEuuzxyObK0JlEo4DmVl38O%2FXu3ip4L5gIufeACQW0YWU5mHzopeCEOazfPaUiJgYF1fUrs98AbYsUjiXoCJO7NZwLJ5FyHzADRgkGQOFl%2F0qNtg5D1p%2FVZrDTIge50N06tHE9faAw8NXLA5rArHgEIY6ol454FDtiudZJCXxbqMVVPhm8GNLkAGX8RvK6DgnEphmkntYG%2FRIuvOxIJMRHsDdWhFFc%2BNqu3XNZzSY%2BeGFeC" rel="nofollow">网点显微镜</a>才能看到印刷的点阵。</p>
<p>但显示器则不然。从诞生到现在,普通显示器的dpi也不过70-150的水平,因此人眼凑近了看就能看到正方形网格的点阵。显示器无法表现很精细的小型图案,这是一个现实的困难。</p>
<h4>点阵渲染,矢量抗锯齿渲染,和矢量对点阵的倒逼</h4>
<p>由于首先产生的显示器是单色(甚至没有灰度),分辨率也很低的,所以<strong>点阵字形</strong>就是一种当然的想法了——每个字符占用一定范围的点阵,每个点只有黑和白两种状态。</p>
<p>随着屏幕进入灰度和彩色时代,以及分辨率的增大,显示和打印大号字的需求开始显现。这时人类就发现:对每个字号都做一套点阵,过于费时费力。并且字号大空间就充足,字形可以更加接近字体的本征,个别点的差异并不重要。总之,投入人类技巧去设计点阵字体越来越亏本。</p>
<p>人类很懒,最喜欢用自动化解决一切。所以这时用数学曲线表示形状的<strong>矢量字形</strong>,很自然就被提了出来。矢量字形可以缩放到任意大小,但缩放之后毕竟仍然是数学描述,想要在屏幕的点阵网格上显示躲不了<strong>光栅化</strong>一步,确定屏幕上的哪些点落在字形的曲线之内。</p>
<p>但显示器毕竟精细度不够,单纯的光栅化必然产生锯齿。因此让字形边缘产生灰度,不限于黑白两色的<strong>抗锯齿</strong>技术,随着电脑性能发展也被提了出来。</p>
<p><img src="http://segmentfault.com/img/bVbS3n" alt="对于大字的点阵渲染与抗锯齿渲染" title="对于大字的点阵渲染与抗锯齿渲染"><br>▲ 对于大字的点阵渲染与抗锯齿渲染</p>
<p>但戏剧性的是,有了抗锯齿技术,矢量字形在小号字上的表现也得到了改善。小号字只要引入灰度,不是纯的黑白,就能大大增加信息量和表现力。哪怕不设计点阵字形,直接用矢量字形渲染也能够辨认。</p>
<p><img src="http://segmentfault.com/img/bVbS3p" alt="对于小字的点阵渲染与抗锯齿渲染" title="对于小字的点阵渲染与抗锯齿渲染"><br>▲ 对于小字的点阵渲染与抗锯齿渲染,小字的抗锯齿渲染虽然模糊,但仍是可辨的</p>
<p>所以……是否可以完全抛弃小号字的点阵字形呢?很可惜的是,这个尝试的效果并不完美:单纯的矢量渲染,会让整个字“毛茸茸”的一片,并不十分容易辨认。</p>
<p>面对这个问题,就产生了<strong>认可</strong>和<strong>改造</strong>两种平行的道路,看下一节。</p>
<h4>微软与苹果:是接近印刷学,还是创造显示学?</h4>
<p>这一段我说的不好。要看<a href="https://link.segmentfault.com/?enc=C2tQ8TJjyL3G%2FfQMHNaD1Q%3D%3D.Ew4XHdhX3Vyti0C2S8wdqZebxDDTnw3hoBmTIgoxEKSjGUgqWlfP3GDf3lCdhORJKgTk8rku7wg99ke0qGC2mou7MeycMahvm%2BJNrEcejGsWKqDOMM%2FPj3VHvpE9GDHJsySBcW4rBTroUF2qB9wl3g%3D%3D" rel="nofollow">《Joel谈软件》中的一节</a>,大师对苹果和微软的字体抗锯齿技术做出的分析。</p>
<p>苹果选择<strong>认可</strong>,去追近印刷学——保持简单,保持统一,原样就是原样。</p>
<p>微软选择<strong>改造</strong>,去创造新的显示学——将点阵字体的优势,引入矢量显示的系统当中,单纯为了屏幕显示的效果去做各种各样的微调(workaround)。</p>
<p>实践不同,但都没有错。并且有一点最终相同的地方是:<strong>都是在显示器dpi不足的情况下做的妥协</strong>。</p>
<h4>独特的计算机文化:衬线与非衬线的倒错</h4>
<p>点阵字形由于一般是塞在小空间里,所以设计中需要发挥很多的技巧,让有限的空间中字形怎样既保证清晰可辨,又尽量追求美感。</p>
<p>在这种现实压力下,点阵字形(尤其对于小字)往往会丢失一些字体的本征特点。例如从DOS时代就开始使用的经典16px宋体,在“一”、“口”等简单字的边角,也许还能留下1像素的衬线。但在“霸”、“编”等稍复杂的字上就表现不出衬线了,甚至对于“饕”、“餮”等字连清晰表现全部的笔画都不太可能。</p>
<p>「此处加16px点阵渲染下这几个字的图」</p>
<p>在矢量抗锯齿渲染之下,面临的问题也类似:衬线只能渲染出一些很浅很小的灰度点,到头来会让字在整体上显得很“脏”,显示效果还不如非衬线的清晰可读。</p>
<p>这也就是说,对于大篇幅正文使用的<strong>小字</strong>,<strong>衬线字形几乎不太可行</strong>,非衬线字形基本上没有商量。——而这时,文章标题为了突出显示,就必须和正文产生明显区别,所以更习惯于用衬线字。</p>
<p>计算机上<strong>标题衬线字,正文非衬线字</strong>的习惯用法,相对于印刷学的习惯,是一个非常有趣的倒错。</p>
<p>这种现象在西文网页更加常见。多逛逛洋人的报纸、电视媒体的网站,就总能看到。</p>
<h3>中文字体之惑</h3>
<p>西文很简单,但中文复杂的要死。就比如说:在12px的字高下,西文真的能勉强做出点阵版的Times衬线字,并且衬线还很明显。而中文根本没有指望。</p>
<p>复杂的东西必定难以产生、难以组合和难以变化。积重难返,知道这个“重”在何处,才能知道如果发生了变革会“变”在哪里。</p>
<h4>Windows“宋体”:历史的堆叠造成的二元性</h4>
<p>Windows的宋体是一个各种成果堆叠,所制造出来的怪胎。</p>
<p>DOS时代,最开始用点阵字体来表示汉字的时候,是没有明确的“字体”一说的。而开始使用矢量字体时,由于中文印刷学的指导,在“宋仿楷黑”中“宋”自然而然的排在了第一位。</p>
<p>Windows在这一点上犯了糊涂。Windows希望将所有旧的默认字体,组合成单一的默认字体,因此就造成了宋体既是矢量的衬线字,又是点阵的非衬线字的怪象。(微软顺手还引入了另一个怪象,就是宋体在西文上还是最不常用的<strong>等宽字体</strong>……唉……真是业界毒瘤)</p>
<p>Windows曾经多次修改过宋体的点阵部分的字形,最近一次是Win7的修改,但多年过去都不敢颠覆这个问题,把小字的渲染还原成正确的矢量渲染。习惯成自然,Windows宋体的这个现象恐怕是要一直存在下去,直到Windows和微软公司生命的尽头了。</p>
<h4>微软雅黑:姗姗来迟的大一统</h4>
<p>不过Windows Vista和7也带来了微软壮士断腕和“曲线救国”的决心:虽然宋体改不了,但总可以新做一个默认字体,在非衬线上保持统一,来结束这场怪象!</p>
<p>这就是我们熟知的<strong>微软雅黑</strong>。这是一个点阵、矢量和西文部分都精心设计的,一套非常优秀而全面的纯非衬线字体。</p>
<p>M$为了微软雅黑花了不少的$,江湖传闻每个字100$。不管是真是假,在目前看来,这绝对是值得的。微软现在大多数的UI设计,都受惠于字体回归科学的努力。</p>
<p>可惜微软毕竟是个靠吃老本为生的技术后进者——这个努力,相比Mac和Linux都晚了一步。Mac和Linux的中文,一开始就干脆的采用了非衬线体。并且主要依赖矢量渲染,点阵渲染只是用于微调。</p>
<h3>Retina:一场伟大的革命</h3>
<p>一切又随着Retina屏幕的问世而改变了。</p>
<p>人类不断的在显示器上追求更高的dpi,终于让显示器的dpi赶上甚至超越了印刷的水平。Retina屏幕我记得已经实现了300+的dpi。</p>
<p>形象的来说,我们在普通屏幕上看印刷稿件,需要放大到300%以上,检查所有细节无误才算有保证。但在Retina屏幕上,只需100%的原样,就能真正的<strong>所见即所得</strong>——见到的细节有多少,印出的细节就有多少。</p>
<h4>Retina屏幕:让无数精妙的旧技巧欲哭无泪的破坏者</h4>
<p>量变引发质变。dpi增加一点,一般只是图像变小一点。但dpi增加到一定程度,就会导致一个恐怖的质变:**<strong>屏幕上的单个像素点,从此以后就不再可辨了</strong>**!!!</p>
<p>这也就是说,以下历史悠久的光栅渲染技术,在Retina屏幕上就被无情的淘汰掉了:</p>
<ul>
<li>所有的点阵字型 (无差别全部废弃)</li>
<li>矢量渲染小字时的修正技术 (Retina屏幕下,像素数永远足够,不存在小字的概念)</li>
<li>有1px精细边框的小图标</li>
<li>无抗锯齿的<a href="https://link.segmentfault.com/?enc=e%2BFh4P9c9sfB5yMi%2BlFXtA%3D%3D.eb2yGlmnhQPG40%2BTWnnrM0zLBh46EOuNeYX5CTI2AA5GT2fil8Wk9UrCD8iZNK30VEHyUO6wLJrJVnsfjzXMFw%3D%3D" rel="nofollow">Bresenham直线绘制法</a> (单点和1像素的直线都不能画了)</li>
<li>……(一时想不起来,不过必然还有很多)</li>
</ul>
<p>可以说这是非常可惜的事情:这些不单单是成熟的技术,更是一门门本身就充满着工程之美的<strong>技艺</strong>。</p>
<p>这些代表着人类创造力的美妙技艺,最终消失在历史当中,这虽然是技术的进步,但也不得不说是艺术上的的遗憾。</p>
<h4>视网膜屏显示学:思想亟待追赶技术的蓝海</h4>
<p>不破不立。但先破和后立之间,一般都存在着一道鸿沟。</p>
<p>现在的Retina也是如此。虽然技术上达到了这样的水平,但在软件设计上,可以说<strong>我们还没有足够的思想和工具,去完全把Retina屏幕的优势全都用好</strong>。(这一点我只知道结论,举不出例子,非常抱歉)</p>
<p>视网膜屏上如何显示?这必将又是一门有待研究开发,通过不断迭代才能得到认识的新科学。</p>
<h3>未来将会如何?</h3>
<p>随着Retina彻底颠覆了我们对“计算机显示”的认识,我们就必须思考:在新的显示技术的推动下,我们的显示学究竟会有什么样的未来?</p>
<h4>预测A:计算机显示学消亡,印刷学统一一切?</h4>
<p>显示器达到了印刷的细密度之后,印刷的所有技巧就可以全部套用到显示器上。这样,未来的显示学也就等同于印刷学和平面设计学,显示技术就是可以动的印刷技术,不再需要两套并行的方法论。</p>
<h4>预测B:计算机继续创造全新的显示学,仍然与印刷学保持并列?</h4>
<p>人类对计算机显示学,已经形成了难以动摇的习惯。则将来人类在视网膜屏幕的时代,仍将发展计算机显示学所产生的方法,仍将尊重人类在上一时代已有的审美观。最终形成一套不完全像印刷学,但可以和印刷学并列的新的显示科学?</p>
<h4>前路在何方?</h4>
<p>不知道。</p>
<p>我只能看到的是:显示技术的革命,这又将是一个必然发生,但无比漫长的进化过程。</p>
<p>人类花了几十年,创建了以<strong>肉眼可辨光栅</strong>为技术基础和行事前提的显示科学。没有理由不相信,人类还需要再用上几十年的时间,才能将这一套旧科学彻底的送进博物馆。</p>
<p>前路漫漫,只有不要停止思考和尝试,才是唯一的发展之道。</p>
略谈人类与计算机的智能
https://segmentfault.com/a/1190000000400198
2014-01-28T03:06:56+08:00
2014-01-28T03:06:56+08:00
沙渺
https://segmentfault.com/u/shamiao
3
<p>来自<a rel="nofollow" href="http://segmentfault.com/q/1010000000399651#a-1020000000400191">这个问题</a>的答案。</p>
<p>人类思维和智能的本质是什么?思考这个问题,也许能让我们更好的了解计算机和人类自己。</p>
<p>——甚至某种方面上来看,我都有点觉得:<strong>计算机其实就是我们自己</strong>……</p>
<h3>人类总体上是一种逻辑动物</h3>
<p><strong>人类的判断和行为,总体上是通过“因为……所以……”、“如果……就去……”这类的逻辑建立起来的。</strong>你可以思考一下,大多数的事情是不是这样。</p>
<p>就你的例子就可以。哪怕表述如此乱七八糟,都可以非常简单的做分析和说明:</p>
<ul>
<li>人脸识别:人类也是凭对人脸的固定印象,先粗略的观察形状和颜色。如果差不多,则找出人脸的边缘形状,寻找头发、五官等关键要素,最终确认是人脸的图案。更细微的,人类对人脸各部分根据位置和颜色可以做详细分解,对细微动作所对应的感情可以匹配固定的印象。虽然人的识别能力比计算机要高的多,但本质仍然是图像识别和数据库匹配。</li>
<li>识图翻译:世界上所有的物品都可以寻找到一些判断条件,只要符合某些条件那就是这个东西。这仍然是逻辑性的。目前当然是不可能做到(多大的工作量!),所以百度识图只是接受现实退而求其次。</li>
<li>气话的判断:学习语言就是学习社会文化。如果你认为语言只是分析词句,那太幼稚了。比如说“我去”,人类也是首先学习正规的语法,理解一般的语言逻辑。而后在社会上频繁看到“我去”这种突然出现的不合逻辑的用法,从而学习并记住了“我去”这种说法可能有其他意义。对其他的气话和潜台词也差不多。</li>
</ul>
<p>所以总体上来看,人类的思维是由<strong>经验</strong>和<strong>逻辑</strong>组成的。通过机械的记忆和学习获得<strong>经验</strong>,而后通过<strong>逻辑</strong>推导得到新的<strong>经验</strong>,而<strong>经验</strong>本身又包含对<strong>逻辑</strong>的改进和发展……如此往复迭代。</p>
<p>我并不否认人类存在着凭空<strong>想象</strong>的能力,并且人类对这个能力的机理几近一无所知。但可以肯定的是:<strong>想象</strong>只是必要时推人类一把的催化剂,必然不是人类逻辑的主体。人类通过想象获得发展思维的线索,但仍然必须使用<strong>经验</strong>和<strong>逻辑</strong>,将<strong>想象</strong>的事情变为现实。</p>
<h3>熵增原理:自发的有序是不存在的</h3>
<p>我坦白这一点是过于复杂的物理概念,我解释不明白,但结论能够最简单的说明:“有序必须在某种介入之下完成。自发而随机的过程最终只会增加混乱。”</p>
<p>提到这个就是说,人类之所以成为现在的人类,就是由于人类高超的按<strong>逻辑</strong>主导去做事的能力。如果人类只使用<strong>想象</strong>来决定行为——我无法想象这样发展到最后会变成什么样子。</p>
<p>这也就决定了,人类目前对人工智能的研究方向,仍然倾向于继续授予计算机明确的逻辑,让计算机在<strong>明确</strong>的道路上越走越远。(哪怕是自学习这种连程序逻辑都自发产生的发明,那也是明确逻辑)</p>
<p>并且长期的来看,这一点是有效的。笑话人不如人,不要光看着计算机出丑。这么多年来人工智能有发展的地方,题主不要睁眼瞎行吗?</p>
<h3>离散化:确保数据和逻辑准确性的伟大发明</h3>
<p>曾经人类是很依赖模拟手段的——用物理量的真值,表示所期望的数据。例如用电压,表示音频波形在某点的振幅。甚至出现过<strong>模拟计算机</strong>这样的尝试——真正操作物理量,用来完成运算过程。例如加减乘除,就真的用运算放大器,对电压进行加工。</p>
<p>但模拟最终仍然在数字的面前节节败退。可以说数字实现侵入一个行业,就在一个行业内势如破竹,把模拟的打得没有喘息之机。(最近的一个例子是数码照相替代模拟胶片。我记得在2000年后不久,那时候还有专家说“模拟胶片可以原子级无限清晰的记录影像,这一点数字永远赶不上,模拟相机永远是选择之一”。现在一看,结果呢?模拟相机现在也只是“还有”而已…)</p>
<p>因为模拟终究无法保证数据的<strong>准确</strong>。模拟的干扰广泛,并且无法处理高频,数据加工、存储过程中操作一次损一次,最终剩下的全是噪声,无法达到实际可用的精度。对<strong>模拟电子技术</strong>和<strong>数字电子技术</strong>这两个学科有基本的认识,就不难理解这一点。</p>
<p>如果你认可:</p>
<ol>
<li>人类无法单凭纯粹的想象,就创造出一个全新而有序的事物;(熵增原理)</li>
<li>人类只会把自己的思考和行为方式,抽象成计算机去实现之;(经验-逻辑关系)</li>
<li>如果把程序代码视为数据,那么数据本身就是逻辑;</li>
<li>数据的胡乱,就是行为的胡乱,最终必然导致全面的混乱。(还是熵增原理)</li>
</ol>
<p>那就容易理解为什么数据的准确和恒定,对计算机是如此的重要。</p>
<p>从这一点上来看,讲数据抽象成0和1的<strong>离散化</strong>,无疑对计算机历史来说是最伟大的发明——通过丢弃最小分度以下的精度,去换取最小分度以上的完全准确,漫长的历史证明了这个取舍怎么看都是值得的。</p>
<p>为什么用2进制,只是因为电子技术上用2进制做出来最为方便,并无太多考虑。不详细讨论。</p>
<h3>模拟什么,就要有更高的计算能力</h3>
<p>玩过模拟器就知道了。如果需要模拟一个其他部件,那么准确的描述其他部件本身,并准确重复其他部件受到<strong>刺激</strong>时表现的<strong>行为</strong>,其信息量和计算量必然比其他部件本身要更多。</p>
<p>所以计算机如果要完全的模拟人类,在计算速度、存储器等性能上,就必须优于人类。这不存在任何的商量。</p>
<p>这一点你可以去看新闻(生物学家估计人脑容量是多少多少字节之类的)。人类现在观察自己的能力和机理,都犹如看一个深不见底的深渊:连深度的估计都做不了,只是知道地表粗浅的表象而已,就更不要提把这个“深渊”用机器复现出来了。</p>
<p>连要超越的指标都不知道,到最后又何谈超越呢?目前的人工智能的粗劣表现,很多都是由于这个问题:不是程序员不想算,而是考虑实际条件,目前的机器实在算不出。</p>
<p>我记得东京大学有台超算叫做“地球模拟器”——这个名字就代表了一个永恒但又无法实现的梦想——人类希望能用计算机准确的“跑”出地球的未来。但想做到这一点,就要有包含和超越地球所有物质的计算能力,这终究又是不可能的。</p>
<p>注:你不能看机械工作计算机比人类快多少——机械工作只不过使用了人类逻辑的一个小小的子集。</p>
<h3>目前计算机的本质</h3>
<ol>
<li>能够实现“逻辑动物”的基本框架;</li>
<li>性能特点和人类互有长短,各有擅长之处;</li>
<li>复杂智能所需要的存储、运算能力,和人类仍然差距巨大;</li>
<li>计算上追近人类的性能,能力上追近人类的行为方式,仍然是目前唯一可行的技术路线;</li>
<li>人类对自己有多深的了解,才能创造出多接近人类自己的计算机。</li>
</ol>
<h3>“瓶中之脑”:放下架子,人类在本质上也并不优越</h3>
<p>“瓶中之脑”是一个思想实验:把大脑拿出来放到瓶子里,保持存活并接通所有的神经信号,给大脑输入完全符合现实世界的全部信息。那么大脑能否知道自己是在真实的人体中,还是在这样的实验瓶子中?</p>
<p>这个实验也有其他的表述方法,例如:</p>
<ul>
<li>我们究竟是实体的人,还是更高智能生物发明的‘计算机’中跑的一段程序?</li>
<li>是不是有一个更高智能生物,像巫师看水晶球一样,无条件的观察和暗中影响着我们这个宇宙?(等同于有神论与无神论的争端)</li>
<li>我为一台游戏机种编写的软件,能否主动判断出运行环境是真机还是模拟器?(注意,这个表述已经是一个实际问题!!)</li>
</ul>
<p>这个问题是有结论的——<strong>不知道,也不可能知道</strong>。这个世界在本质上是结果论的:只要所有的响应都和现实一致,那么这个“现实”是真是假,到头来也无法察觉。</p>
<p>那么从这个意义上去反思:我们作为人类,所鼓吹的“智能”到底是个什么东西?</p>
<p>从人类获取智能,以及用计算机重复自己的智能的这个经历来看,人类的“智能”其实也不过是一些经验与行为方法的总称。只要能够实现同样的这些内容,那<strong>无论是真人还是计算机</strong>,都可以称为是<strong>有智能</strong>的。例如:</p>
<ul>
<li>人类能够计算一些数学问题,计算机通过一定的程序也能计算,计算机有没有智能?</li>
<li>人类用人类的方法下棋,计算机用计算机的算法下棋,计算机有没有智能?</li>
<li>人类按照自己的意志和行为习惯操作家居设备,计算机分析人类的行为习惯(甚至猜测人类的意志)去操作家居设备,计算机有没有智能?</li>
<li>机械设备的堵转等故障,人类通过观察决定紧急停机,计算机通过传感决定紧急停机,计算机有没有智能?</li>
</ul>
<p>所以将“智能”神化成人类的“专利”,号称机器“不能重现”的说法,从原理上看就是荒谬的。</p>
用JS和Python略谈Null和Undefined
https://segmentfault.com/a/1190000000381846
2014-01-09T08:07:16+08:00
2014-01-09T08:07:16+08:00
沙渺
https://segmentfault.com/u/shamiao
3
<p>from <a rel="nofollow" href="http://segmentfault.com/q/1010000000381586#a-1020000000381836">http://segmentfault.com/q/1010000000381586#a-1020000000381836</a></p>
<p>随意的略谈,文中有错的概率接近1。所以并不要对本文过于当真,任何不满请随意留言开喷。</p>
<p>注意本文严格区分Null/<code>null</code>,Undefined/<code>undefined</code>。</p>
<h2>Null & Undefined</h2>
<p>从各种语言的语义上看,Undefined相当于一个变量并没有明确的被赋值。很多语言将其视为一种错误或异常:</p>
<pre><code class="lang-python"># Py2.7
print(a) # NameError: name 'a' is not defined
</code></pre>
<p>而Null相当于变量被明确指定了没有值,而不是由于意外的原因被忽略掉了:</p>
<pre><code class="lang-python"># Py2.7
b = None
print(b) # None
</code></pre>
<p>Null和Undefined体现的区别在于:</p>
<ul><li>是否被赋值了是<strong>定性</strong>问题,具体赋的什么值是<strong>定量</strong>问题。</li>
<li>遗忘了赋值一般是<strong>人为错误</strong>,赋了空值是肯定是<strong>正当逻辑</strong>。</li>
<li>Undefined一般是无心的,Null肯定是故意的。</li>
</ul><p>也就是说从函数传参的语义上讲:</p>
<ul><li>如果是空值,传Null是最好的。</li>
<li>不明确传值,不代表参数指定为空,而是尊重参数的默认值。</li>
<li>如果不明确传值,参数也没有默认值,多数语言会认为“缺少必要参数”是一个错误。</li>
</ul><h2>JavaScript的怪异之处</h2>
<p><code>null</code>和<code>undefined</code>是JavaScript的两颗雷。</p>
<p>JS的怪异之处就在于<code>undefined</code>真的是一个可以使用的值。在其他语言需要用特别方法取消一个变量的定义(甚至定义之后不能取消)的时候:</p>
<pre><code class="lang-python"># Py2.7
a = 1
del a
a # NameError: name 'a' is not defined
</code></pre>
<p>JS只需把<code>undefined</code>赋给变量:</p>
<pre><code class="lang-javascript">a = undefined;
</code></pre>
<p>这个行为和臭名昭著的VB6,在不开启“强制变量声明”(<code>Option Explicit</code>)时的行为是非常相似的:</p>
<pre><code class="lang-visualbasic">MsgBox(Str(undef1)) ' 我记得是Nothing,转换后为vbNullString
' -----------------
Option Explicit
MsgBox(Str(undef1)) ' 我记得是编译错误:变量未定义
</code></pre>
<p>在函数传参中<code>undefined</code>的用途在于弥补JavaScript没有参数默认值语法的缺欠:</p>
<pre><code class="lang-javascript">function f(x) {
if (x === undefined) {x = 1;}
return x;
}
console.log(f()); // 1
</code></pre>
<p>从这个意义上,还好JavaScript没有设计<code>function f(x=1)</code>的语法。要不然给x传<code>undefined</code>,就不知道语义是尊重x的默认值,还是把x的值覆写成<code>undefined</code>了……</p>
<h2><code>null</code>和<code>undefined</code>参与运算的麻烦</h2>
<p>JS的<code>null</code>如果进入运算,真的会被解析成为<code>0</code>或<code>false</code>:</p>
<pre><code class="lang-javascript">(1 + null) # 1
(1 * null) # 0
(1 * null) # Infinity
</code></pre>
<p><code>undefined</code>进入运算,一律得到<code>NaN</code>:</p>
<pre><code class="lang-javascript">(1 + undefined) # NaN
(1 * undefined) # NaN
(1 / undefined) # NaN
</code></pre>
<p>麻烦就在于这些现象都是<strong>不报错</strong>的。对于其他语言,想都想得到:</p>
<pre><code class="lang-python"># Py2.7
1+None # TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
1+a # NameError: name 'a' is not defined
</code></pre>
<p>这也就是说在JS程序中,如果忘记判断外部输入的变量是否存在,会使程序产生<strong>不确定的行为</strong>。虽然外部输入不检验肯定是程序员的错……但从语言设计上看,这比其他语言干脆报错,在可靠性上不可否认的差了很多。</p>
<h2><code>null</code>和<code>undefined</code>的判断</h2>
<p><code>null</code>和<code>undefined</code>逻辑判断时都认为是false。</p>
<p>但是如果碰到大坑<code>==</code>的时候……自己看吧。我是不理解<code>==</code>的时候这TM都是个什么语义:</p>
<pre><code class="lang-javascript">var foo;
console.log(foo == null); // true
console.log(foo == undefined); // true
console.log(foo === null); // false
console.log(foo === undefined); // true
console.log(null == undefined); // true
</code></pre>
<h2>JS不明确,编程者要明确</h2>
<p>避免问题只有一种逻辑:编程者明确自己的语义。</p>
<ul><li>不要放过小错误,在最小的程序上也要注意语义的明确性。玩JS没有这个习惯确实容易挂的。</li>
<li>如果明确为空,那就是<code>null</code>,而不是忽略掉。尽量避免容忍参数的默认值,因为程序员不知道(也关心不到)未来上游代码会如何改变参数为默认值时的行为。</li>
<li>使用外部指定的变量前,检验存在性,必要时抛出错误。永远不假设变量有值。</li>
<li>判断一个量已定义且非空,只使用:<code>if (a !== null && a !== undefined)</code>。</li>
</ul><p>JS的坑要多少有多少,需要编程者的认真控制,避免程序出现不可预测的行为。从这一个角度上看,JavaScript让编程者很累,绝对是非常非常烂(faulty)的。不知道有多少人还记得<em>《JavaScript: The Definitive Guide》和《JavaScript: The Good Parts》的那个笑话</em>(图)?如果没有对应的利益,恐怕避免使用JavaScript总是个不差的想法。</p>
<p><img src="http://segmentfault.com/img/bVbLuN"></p>
<p>不过Unix哲学也指导我们:<strong>更坏就是更好</strong>。虽然有问题但实用可行,快速面世并不断发展的方案,比某些大公司闷头完善,结果最后黄花菜都凉了才扔出来的东西要强。</p>
<h2>其他参考</h2>
<p><a rel="nofollow" href="http://justjavac.com/javascript/2013/04/14/javascript-quirk-2-two-non-values-undefined-and-null.html">http://justjavac.com/javascript/2013/04/14/javascript-quirk-2-two-non-values-undefined-and-null.html</a><br>
相当值得一读(尤其值得注意一下域名)</p>
<p><a rel="nofollow" href="http://www.ruanyifeng.com/blog/2011/06/10_design_defects_in_javascript.html">http://www.ruanyifeng.com/blog/2011/06/10_design_defects_in_javascript.html</a><br>
这篇文章的意义是了解一下<code>==</code>和<code>===</code>运算符,在碰到<code>null</code>和<code>undefined</code>时候的怪异行为。<br>
如果你决定去读这篇文章,请注意我不认为原作者的这句话是个正确的判断:</p>
<blockquote>
<p>在编程实践中,null几乎没用,根本不应该设计它。</p>
</blockquote>
[小帖士] 千万不要把Python脚本的第一行写成“#! /usr/bin/python”!
https://segmentfault.com/a/1190000000361999
2013-12-17T03:17:06+08:00
2013-12-17T03:17:06+08:00
沙渺
https://segmentfault.com/u/shamiao
21
<p>设置.py文件为可执行的Shabang标记,只有唯一一种正确的写法</p>
<pre><code class="lang-python">#! /usr/bin/env python
</code></pre>
<p>以下写法都是错的:</p>
<pre><code class="lang-python">#! /usr/bin/python
#! /bin/python
</code></pre>
<p>错在两点:</p>
<ul>
<li>小的错误是,你不清楚不同的发布版环境下,python可执行文件到底在哪里。</li>
<li>大的错误是,如果在virtualenv虚拟环境下运行脚本,则python解释器必须使用virtualenv虚拟环境文件夹下<code>bin/</code>目录中包裹的python。但此时<code>/usr/bin/python</code>仍然存在,则脚本将在脱离虚拟环境的解释器下运行!这是个会直接导致暴死的错误!</li>
</ul>
<p>所以别忘了用env命令,保证严格遵守<code>$PATH</code>等约束,让脚本调用python的行为,和自己在命令行下手打python回车是完全一致的。</p>
为什么WordPress主题,有必要严格按照开源模式发布和维护
https://segmentfault.com/a/1190000000361996
2013-12-17T03:05:36+08:00
2013-12-17T03:05:36+08:00
沙渺
https://segmentfault.com/u/shamiao
0
<p>这个问题来自于我和<a rel="nofollow" href="http://www.dmeng.net/dmeng-theme-beta.html">多梦主题</a>的作者间,一个随意的问答。</p>
<blockquote>
<p>我:“主题没有开源许可证……”<br />
作者:“如果分享个免费模板还要开源许可证的话,那做好事是不是也要个好人证?哈哈,在哪里办啊?”</p>
</blockquote>
<p>交谈本身不过是说笑,但很多WordPress主题作者,觉得主题“做出来发布就够了”的思维,还是非常广泛的。</p>
<p>我为这一点感到深深的担忧。</p>
<h2>主题采用开源模式的可行性</h2>
<p>复习开源软件的开发模式:</p>
<ul>
<li>开源发布代码,吸引其他人把代码fork(分支)走,自己去修改调试。</li>
<li>他人做了改进,提出pull request,向原作者发送自己改动的代码。</li>
<li>原作者审阅、微调他人的贡献,做merge操作合并修改。</li>
<li>这样主题的改进和升级,原作者就只需要审批一部分,亲自做一部分。</li>
<li>和辛苦的全部亲力亲为相比,哪个比较高效一些?</li>
</ul>
<p>网站主题具有<strong>用户粘性特别特别大</strong>的先天特点。这是任何其他开源软件望眼欲穿、羡慕嫉妒恨的天生优势。原因在于换主题对于网站的改动是颠覆性的,所以任何一个有常识的站长,都会把换主题作为最后不得已的选择。</p>
<p>主题只要选定了,出了问题就宁可凑合使用,甚至于自己做hotfix。以我的网站为例,我的网站主题直到现在,用的还是2010年发布的WPInk Blue。用到现在连这个主题的原发布页都没了,还在用。</p>
<p>所以,如果采用开源软件的开发模式,那么只要有一个前端开发者采用了你的主题,就有一个开发者会和你一起并肩战斗。</p>
<h2>主题采用开源模式的必要性</h2>
<p>所谓必要性,简而言之就是:“不开源就死亡”。这是一个不那么一步到位,但是能够推导出来的逻辑。</p>
<p>首先,单打独斗==死亡。个人开发是无法长久的。无数一个人开发的主题,都重复这样一个循环:发布-受欢迎-收集意见-拼命维护几个月-失去动力-放任自流,最后白白浪费开发者精力,大量优秀的主题最终无法在最新的WP环境下再利用。</p>
<p>历史无数次的重复,狡辩没有任何用处。如果要正经的长久运行一个WP主题,就只有两种模式行的通:<strong>要么开源模式,要么商业销售</strong>。也许商业销售的同时,也提供免费的缩减版吸引客户,那只是商业化的小细节。</p>
<p>但如果发布时没有开源许可证,那就相当于关上了开源模式的大门。因为许可证是进行确权的必要文件,保证他人的使用、修改,甚至派生新作品的合法性。No License相当于只按照版权法做事,他人不用说修改了,甚至连使用主题有什么条件,从法律上都是不明确的。</p>
<p>开源模式从文化上讨厌这一点。虽然没有明言,但在开源文化中No License绝对有一种淡淡的“挖坑陷害”的印象——软件给你用,把柄我拿着。简而言之:<strong>了解开源模式的开发者,看见No License的开源作品都是绕着走的</strong>。</p>
<p>别人没有明确给出的东西,开源模式从不偷拿。请任何人不要用中国互联网“拿来主义”的丑陋面貌,来揣测开源社区。更不要把这个现实状况视为“理所当然”,以为别人会去违心的遵守。这做不到。</p>
<p>所以最后的结论就是:不做开源保证==不是真正的开源==没人参与开发==单打独斗==死亡。</p>
<h2>其他的好处</h2>
<p>除了以上所说的,还有几点其他的好处没有提到:</p>
<p>其一,明确使用开源模式,能够吸引更厉害的开发者。接触这些人,对自己的提高很有帮助,并且和这些人的沟通是非常轻松的。</p>
<p>其二,可以避免“lamer”的出现。lamer(伸手党)把原作者当作免费定制主题的“苦力”,甚至有时会引发双方对骂一类的糟心事。而在开源模式中,只须直接对lamer说:“你行你去改”,简单省事。</p>
<p>其三,就是保证了即使自己由于种种原因,最后不再维护这个主题,他人也可以合法的接手过来,从最后一个版本为起点,继续升级和维护工作。</p>
<h2>结论,我对WordPress主题开发者的建议</h2>
<p>简而言之,如果你希望你的主题开发,能够出现以下的良性现象:</p>
<ul>
<li>不会一人包揽全部工作</li>
<li>保证长久的升级</li>
<li>接触更多的优秀开发者</li>
<li>避免lamer的压力</li>
</ul>
<p>那么你就值得按照开源模式开发:</p>
<ul>
<li>选个开源许可证,跟主题的代码放一起</li>
<li>很多情况下MIT许可证就不错(内容简析:软件怎么用都行,只要源代码中保留原作者署名)</li>
<li>学习git版本管理系统(没那么难)</li>
<li>把主题的代码托管到GitHub平台上</li>
</ul>
<p>GitHub也许对WP主题开发者会陌生一点。GitHub是最主流的开源作品托管平台。他人查看代码、参与开发都需要GitHub。任何开发者都绝对值得接触GitHub,不需要解释。</p>
<p>Note: 本作品采用知识共享署名 4.0 国际许可协议进行许可。首次发表于:<a rel="nofollow" href="http://shamiao.com/wp/65">http://shamiao.com/wp/65</a></p>
了解fixubuntu.com受威胁事件,远离Ubuntu绑架!
https://segmentfault.com/a/1190000000334711
2013-11-11T20:47:10+08:00
2013-11-11T20:47:10+08:00
沙渺
https://segmentfault.com/u/shamiao
1
<h2>写这篇文章做什么?</h2>
<ol>
<li>作为新闻事件的报道。</li>
<li>警告所有的Linux用户,就算是自由软件,也要远离对单一公司主导的依赖。</li>
<li>作为历史记录,让后人不要忘记发生过这件事。</li>
<li>我目前是Ubuntu黑,写这个不违背我的想法。</li>
</ol>
<h2>Ubuntu的隐私纠纷</h2>
<p>简而言之,Ubuntu从12.10版本,为Dash桌面搜索框组件加入了在线搜索功能。在用户键入搜索词汇时,搜索词会提交到Canonical的服务器,与Amazon通信,向用户提供和搜索词相关的商品推荐。</p>
<p>为人诟病的是:此功能是选择退出(opt-out)的,即系统安装后就默认打开,没有开启提示,也没有相关的隐私通告。也就是说,用户并不知道他们键入的搜索词汇(不管是什么难以启齿的内容)全都通过了Canonical的服务器。</p>
<p>关于Ubuntu此行径的新闻报道<a rel="nofollow" href="http://arstechnica.com/business/2012/09/ubuntu-bakes-amazon-search-results-into-os-to-raise-cash/">见此</a>,<a rel="nofollow" href="http://nakedsecurity.sophos.com/2012/11/01/ubuntu-search-amazon-privacy/">见此</a>,<a rel="nofollow" href="http://yro.slashdot.org/story/12/09/22/1319216/ubuntu-will-now-have-amazon-ads-pre-installed">也见此</a>。</p>
<h2>fixubuntu事件</h2>
<p>于是有发烧友开设了<a rel="nofollow" href="https://fixubuntu.com">https://fixubuntu.com</a>,提供了删除ubuntu商业推广组件的shell代码。</p>
<p>这个行为惹火了Canonical,导致他们欲动用一切可能的手段,将此内容除之而后快。于是Canonical动用了对Ubuntu持有商标的借口,以“滥用商标”为由向fixubuntu发函。</p>
<p>而fixubuntu也毫不客气。信函被曝光是理所当然的了,而fixubuntu也做出了一个犀利的声明:</p>
<blockquote>
<p>声明:如果你是 ①一个傻逼 ②一个律师 ③二者兼具,请注意本站与Canonical没有任何关联,也未经其所谓“授权”。<br />
本站批评Canonical在Ubuntu中侵犯隐私的功能,并指导用户删除之。所以这必然被Canonical所不容。<br />
我们只是描述性的使用受商标保护的词汇“Ubuntu”。如果不用,人们就无法找到本站,也无法阅读理解本站的内容。</p>
</blockquote>
<p>广大IT媒体也对Ubuntu所震惊,纷纷发表报道谴责Ubuntu滥用商标权去抹杀批评言论,见<a rel="nofollow" href="http://arstechnica.com/information-technology/2013/11/canonical-abused-trademark-law-to-target-a-site-critical-of-ubuntu-privacy/">这里</a>、<a rel="nofollow" href="http://www.wired.com/wiredenterprise/2013/11/fixubuntu/">这里</a>还有<a rel="nofollow" href="http://www.techdirt.com/articles/20131107/17583725174/disappointing-to-see-canonical-act-like-trademark-bully-over-ubuntu.shtml">这里</a>。</p>
<p>而在舆论压力下,某些人口中所谓的“救世主”Matt Shuttleworth也不得不发表<a rel="nofollow" href="http://www.markshuttleworth.com/archives/1299">声明</a>承认错误,并心虚的关闭评论。而fixubuntu也做了最低程度的修改,从网站上删除了圆环三圆点Ubuntu logo,但不改动网站的任何文字内容,也仍然正常使用Ubuntu文字和官方字形。</p>
<p>而从Canonical公司的<a rel="nofollow" href="http://www.canonical.com/intellectual-property-policy">知识产权声明</a>页面的第3节《商标权利》,可以得知:</p>
<ul>
<li>可以在任何讨论、评价、批判、恶搞中自由使用Ubuntu商标,只要不擅自声称代表Canonical的意见或获得其授权。</li>
<li>唯有在网站用于营销目的时,在域名中使用Ubuntu商标才需要得到事先授权。</li>
</ul>
<p>也就是说fixubuntu的开设没有任何法律问题,Canonical的指责是明显的泼粪。估计他们的脑子里认为:“不明确声明‘不代表Canonical’,我们就可以扣上‘擅自代表Canonical’的帽子,想怎么办就怎么办了!”</p>
<h2>我的评价</h2>
<p>闻所未闻。</p>
<p>无法想象。</p>
<p>丧心病狂!</p>
<p>面对侵犯隐私的批评,用户都做到自己“DIY”这一步了,Canonical居然还要大棒伺候,除之而后快?</p>
<p>Canonical的人都是脑袋撞猪的白痴吗?想想看,这样一个小众网站,会有多大比例的“隐私狂人”真的去用?Canonical商业行为的蛋糕又能切掉几克几两?且不说这个指责正当与否,只简单的分析一下收益和代价,Canonical你划得来吗?!</p>
<p>这件事上,Ubuntu的气量之小,眼光之浅,脑筋之笨,行为之蠢,让包括我在内的大批Linux爱好者震惊!这简直是Microsoft这种非自由软件的代表者都不会做的事情!</p>
<p>严重点说,就凭这一件事,Ubuntu就彻底撕破了伪善者和“人类友好”的画皮,露出了“对我不利就收拾你”,妄图以自由软件上位,最终成为M$之后的下一个软件魔王的真面目!</p>
<h2>我的提议</h2>
<p>简单的来说,我呼吁所有的Linux用户远离Ubuntu。选择Ubuntu,就意味着受控制、被威胁,就等同于把达摩克利斯之剑挂在了自己的头顶上。</p>
<p>就算不那么极端,也要远离Ubuntu所倡导的“特色”软件,而只使用其他Linux发布版也支持良好的工具。例如抛弃Unity桌面改用GNOME!</p>
<p>就算开源,也要留心!依赖Ubuntu或任何单一公司维护,而缺乏社区参与的方案,小心也会被绑架,最终难以脱身!</p>
<p>最后,我也很想给所有崇拜Ubuntu的脑残粉打打脸:自由软件界,只有高质量的代码,而没有下一个M$,更没有万能的上帝!认为发布版“唯Ubuntu”,甚至希望靠Ubuntu来“拯救”世界的信徒们,你们的脑子真的是锈死在了上一个时代!</p>
<p>面对软件,不要崇拜任何人,不要依赖任何单一的方案,更不要相信所谓的“信誉”——长期来看,全是假的!</p>
<p>只有我们自己才是主宰我们自己的神。</p>
博客开篇第一篇,写写我做行政区划层级的方法
https://segmentfault.com/a/1190000000329012
2013-11-02T04:08:25+08:00
2013-11-02T04:08:25+08:00
沙渺
https://segmentfault.com/u/shamiao
10
<p>省-市-区这种三级地址的结构,是我们中国人写软件经常遇到的需求。(当然,往上可能有国家/地区,往下可能有乡镇/街道,层级数量是不一定的)</p>
<p>这个需求一般要满足几个特点:</p>
<ul>
<li>必须表现省市区的隶属关系。——不用说。铁则。</li>
<li>存储要够省。——如果每条记录里都存所有的省市区,那可太笨拙了。其实如果存储省下了,好查询,也好维护。</li>
<li>必须有快速的回溯。——找出包含小行政区的所有上级,是最频繁的查询需求。</li>
<li>对付其他的查询方法,也要够灵活。</li>
<li>一般使用中看作是死的,但不能写死,因为行政区划总有更新的一天。<br />
必须保证系统最低限度的灵活性。</li>
</ul>
<h2>左右值无限层级存储方法</h2>
<p>我喜欢用左右值法来组织省-市-区的表格。</p>
<h3>数据结构</h3>
<pre><code class="lang-markdown">-------------------------------------
name lbb ubb depth 注:ID省略
-------------------------------------
吉林省 1 14 1
长春市 2 7 2
朝阳区 3 4 3
南关区 5 6 3
辽源市 8 13 2
龙山区 9 10 3
东丰县 11 12 3
辽宁省 15 18 1
沈阳市 16 17 2
-------------------------------------
</code></pre>
<p>数据表就像这样。这个组织方法的特点是:</p>
<ul>
<li>每一个区划覆盖一个<strong>整数值区间</strong>。</li>
<li>各个区间只嵌套不交叉。</li>
<li>甚至各区间的上下限不允许重叠,左右值是无重复的。<br />
<strong>这是一个相当重要的优化</strong>。下边的查询,没有这个条件基本都不能成立或不太方便。</li>
<li>最小的区间长度至少为1。</li>
<li>总体是一个无限嵌套的结构。</li>
</ul>
<p>以上边这个表格为例,他表示了这样一个分层结构:</p>
<pre><code class="lang-markdown">1------------------吉林-------------------14 15---辽宁---18
2------长春------7 8--------辽源--------13 16-沈阳-17
3-朝阳-4 5-南关-6 9-龙山-10 11-东丰-12
</code></pre>
<p>这样,省-市-区就只需要存储一个数字,即该区划的左值,就可以了。</p>
<p>但必须注意:虽然存左值完全够实用了,但为了数据安全,必须另开设一个字段存储对应区划的ID,作为“真正唯一的关联数据(虽然不怎么用得到)”。而左值只能当作“非常有用的冗余数据”,做好一更新随时会被重写的准备。原因末尾“优势和弱点”一节会解释。</p>
<h3>查询方法</h3>
<p>根据号码查询单个区划的名称,查左值相等的就行了。</p>
<p>如果要列出所有的上级区划,尤其方便。由于区间包含是一个简单的数学关系,所以再也不必要像典型的存储父记录号码那样,费时费力去做回溯操作。</p>
<p>只要把包含区间的值的所有区间取出,就是这个区划的所有上级。并且,由于大区间的左值一定比小区间的小,所以只需要按照左值升序,就可以把区间从大到小排序。</p>
<p>例如查询<code>朝阳区(左值=3)</code>的所有上级: </p>
<pre><code class="lang-sql">SELECT * FROM regiontable WHERE lbb<=3 AND ubb>3 ORDER BY lbb ASC
</code></pre>
<p>得到结果:</p>
<pre><code class="lang-markdown">-------------------------------------
name lbb ubb depth
-------------------------------------
吉林省 1 14 1
长春市 2 7 2
朝阳区 3 4 3
-------------------------------------
</code></pre>
<p>即吉林省-长春市-朝阳区。</p>
<h3>遍历方法</h3>
<p>数据表中存储了<code>depth</code>冗余字段代表层级深度。这个字段会在查询中起大作用。</p>
<p>例如列出下级所有区域(已知当前级别的左右界和深度):</p>
<pre><code class="lang-sql">SELECT * FROM regiontable WHERE lbb>左界 AND ubb<右界 AND depth=(深度+1) ORDER BY lbb ASC
</code></pre>
<p>如果列出第一级那就只查depth=1就行了。</p>
<p>这个查询不仅可以用于行政区划表格,也可以用于用户数据表格。例如列出属于某区域的所有用户(已知此级别的左右界):</p>
<pre><code class="lang-sql">SELECT * FROM userinfo WHERE region>=左界 AND region<右界
</code></pre>
<p>而无论对于多大的区域,这个查询效率的都是均等的高,彻底杜绝区域大了查不动。</p>
<h3>优势和弱点</h3>
<p>左右值法快就快在查询和排序都有数学的自然性。一步到位,无需回溯,效率是显而易见的。</p>
<p>而弱点同样显而易见:修改极其麻烦,并且往往是牵一发而动全身。一处写入,估计大半张表格都要跟着修改。算法就会很麻烦。</p>
<p>而这要注意对于任何一个记录,其左右值都是不稳定的,只能用于查询,<strong>绝对不能用来与数据表建立持久稳定的关系</strong>!这也就是前边说过一定要存区划ID的意义。</p>
<p>这个需求中,我们恰好使用了优势,而规避了弱点。因为行政区划数据天天查,但很少改。</p>
<h2>具体地址怎么存?</h2>
<p>像淘宝那样,把省市区放在一行内,摆在地址文本框的旁边,并明示用户:地址中不必再输入省市区。</p>
<h2>如何应对不存在的省市区名称?</h2>
<p>我推荐的方法是:提示用户能选到多细选多细,如果再细没有了,就放在具体地址前边,一起输入到地址文本框中。</p>
<p>我是绝对反对整几个小文本框,给用户在找不到自己的省市区的时候,去自定义输入的。<br />
理由也很简单:就算我们的行政区划数据库再老,再不准确,那也能保证99%的人都能找到自己的地址。<br />
所以这个思路看似很自然(没有就自定义嘛!),但其实是在为了1%的需求投入100%的开发精力,到头来是极其愚蠢和低效的。</p>
<h2>数据哪里来?</h2>
<p>要求不高的,<a rel="nofollow" href="http://zh.wikipedia.org/wiki/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD%E8%A1%8C%E6%94%BF%E5%8C%BA%E5%88%92">维基百科去抄</a>,或者到别的程序里去扒。</p>
<p>要求高的,去买民政部出的《<a rel="nofollow" href="http://item.jd.com/11255025.html">中华人民共和国行政区划简册2013</a>》,权威性没商量。</p>
<p>注意港澳台的行政区划问题。我建议以下的方案:</p>
<ul>
<li>香港、澳门特别行政区,仅列在中华人民共和国下,不列入单独的国家/地区。</li>
<li>中华人民共和国下存在台湾省,但不再向下延伸;</li>
<li>第一级国家/地区(如果有的话)单列台湾,并按<a rel="nofollow" href="http://zh.wikipedia.org/wiki/%E5%8F%B0%E7%81%A3%E8%A1%8C%E6%94%BF%E5%8D%80%E5%8A%83">维基百科资料</a>,或参考台湾执政部门资料,填入对应的下级行政区域。最好使用正体(繁体)中文。</li>
<li>另外,小心使用洋人做的国家列表。要是一不小心新疆和西藏单列了国家和地区,就自己考虑后果吧。</li>
</ul>