JavaScript Templates

翻译:ShiningRay @ Nirvana Studio

(译者:JsWiki的第一个版本是用ASP+JavaScript写的,中间就用到了它)

对于Web应用的开发者,来自 TrimPathJavaScript Templates 模版引擎是一个轻量的 APL / GPL 开源 组件,他让你的程序运行在Web浏览器中的时候可以直接基于模版编程(像PHP/ASP/JSP)。

  • The JST 引擎是完全用标准JavaScript写成的。

  • 他支持一套丰富的模版标记语法,和FreeMarker, Velocity, Smarty十分相似。

  • 对于大量的硬编码的字符串连接和主流的DOM/DHTML处理来说,JST是一个更容易阅读的替代方案。

借助JavaScript Templates来创建你自己的rich web application(像GMail/OddPost/Bloglines)。

更多信息:

JST 10 分钟简介

首先,在我们的HTML页面中,我们载入TrimPath JST组件到浏览器中

接下来,建立一些结构化JavaScript数据——仅仅是一些对象和数据

下面,这是一个JST模版的例子,它可以指导如何“渲染”数据。我们把我们的JST模版放到HTML页面的隐藏的<textarea>标签中:

这里是显示如何利用 API进行模版处理的代码

返回的结果是:

除了可以从 <textarea>元素中获取我们的模版之外,我们也可以从JavaScript的字符串中获取:

你也许想立即看看这个,JST demo page.


我应该把JST的模版放在哪里?

在上面的介绍中,我们把我们JST模版放在了HTML页面的隐藏的<textarea>标签中。

  • 当然,你可以在一个页面中有多个 <textarea> 元素,来保存不同的JST模版。

    • 一般的语法是:<textarea id="my_template_jst" style="display:none;"> ... 模版内容 ... </textarea>

    • 把JST的<textarea>放入<form>中必须要谨慎,除非你想在点击提交时把他们发送到服务器。

  • 为何使用 <textarea>?

    • <textarea> 元素有一个很好的特性是不会把它内部的代码innerHTML的格式搞乱。

      • <textarea>的innerHTML的稳定性十分重要因为JST语法允许在任何奇怪的位置放入一些控制流标记(如 if/elseif/for),甚至在HTML标签中,例如:

        • <option value="${country.name}" {if country.name == currCountry}selected{/if}>

      • 就和大多数服务器端模版语言一样,JST的语法并不是真正的HTML/XHTML/XML。因此,使用<textarea>来存放JST模版是最好的。

    • 关于<textarea>要注意: 浏览器会转化<textarea>的内容中的 < 和 > 变成 &lt; 和 &gt;。

      • 所以,TrimPath.parseDOMTemplate() 和 TrimPath.processDOMTemplate() 会自动把 < 和 > 转化回来,这样你仍然可以使用这个模版。

还有,你也可以把 *.jst 文件放在Web服务器上,并且通过XMLHttpRequest 或者是隐藏的iframe把他们载入到浏览器中。

服务器端 JST 执行

我们设计JST时不仅让她可以在浏览器中运行同时还可以在任何单独的JavaScript解释器中运行(例如 Mozilla Rhino 或者是 SpiderMonkey)。核心的 JST 引擎不会依赖关键的DOM/DHTML/浏览器。

PHP 对比 PERL

本文是 http://tnx.nl/php – 如果你要复制他,请保持这个链接。

翻译:ShiningRay @Nirvana Studio

目录

参数和返回值极其矛盾

要展示这个问题,下面有一个函数列表,里面的函数用来匹配用户定义的内容:(也许甚至那些用PHP的人才会使用这个文档,只是用来查看该用哪个函数:P)

  匹配 替换使用 大小写敏感 返回的数字 数组参数 返回匹配 s/m/x标志 偏移(-1=结尾)
ereg ereg   所有 数组 0
ereg_replace ereg 字符串 所有 0
eregi ereg   所有 数组 0
eregi_replace ereg 字符串 所有 0
mb_ereg ereg[1]   所有 数组 0
mb_ereg_replace ereg[1] 字符串/表达式 所有 0
mb_eregi ereg[1]   所有 数组 0
mb_eregi_replace ereg[1] 字符串 所有 0
preg_match preg[2]   皆可 一个 数组 0
preg_match_all preg   皆可 所有 数组 0
preg_replace preg 字符串/表达式 皆可 无/所有 0
str_replace str 字符串 所有 数字 0
str_ireplace str 字符串 所有 数字 0
strstr, strchr str   一个 子串 0
stristr str   一个 子串 0
strrchr str   一个 子串 -1
strpos str   一个 索引 n
stripos str   一个 索引 n
strrpos char[3]   一个 索引 n
strripos str   一个 索引 -1
mb_strpos str[1]   一个 索引 n
mb_strrpos str[1]   一个 索引 -1

这种问题还存在在其他的函数组里,不仅仅是匹配的这部分而已。

(在Perl中,所有这些功能都可以通过四个简单的操作符来完成。)

[1] 用于处理多字节字符
[2] PCRE regex: 所谓的“Perl兼容”的正则表达式。
[3] 在PHP5中也是字符串str

PHP对大小写不敏感的操作使用不同的函数

(这个可能会有两方面的争论。有些人认为提供不同的函数更好,即使这意味着又要记很多名词了)

在Perl中,你可以使用两个lc() 或者是 /i 标志,而PHP通常会提供一个大小写敏感的变量。而且,大小写不敏感的那些版本的函数名的命名方式也不一致。

Perl: $foo cmp $bar lc $foo cmp lc $bar
PHP: strcmp($foo, $bar) strcasecmp($foo, $bar)
     
Perl: index($foo, $bar) index(lc $foo, lc $bar)
PHP: strpos($foo, $bar) stripos($foo, $bar)
     
Perl: $foo =~ s/foo/bar/ $foo =~ s/foo/bar/i
PHP: $foo = str_replace('foo', 'bar', $foo) $foo = str_ireplace(...)
PHP: $foo = ereg_replace('foo', 'bar' ,$foo) $foo = eregi_replace(...)

PHP的函数命名方式的不一致

  • 大小写不敏感的函数有一个’i’或者’case’在函数名的不同的位置。
  • 毫无表现上的规律 有下划线的 和 没下划线的:
    Perl的核心函数名则没有含有下划线的。
  • PHP 有 unlink 、link 和 rename 和系统调用一致,但是 touch 的系统调用是 utime, 不是 touch。
  • 同时你也无法确定单词的顺序:
    • 宾语 动词:base64_decode, iptcparse, str_shuffle, var_dump
    • 动词 宾语:create_function, recode_string

    Perl的核心函数都是“动词 宾语”结构的除了替代的 dbm* 函数。(注意里面的 sys 是一个前缀,而不是一个宾语。同时 flock 和 lstat
    是根据系统调用命名的。shm* 和 msg* 是库函数调用)

  • “to” 还是 “2”?

    ascii2ebcdic, bin2hex, deg2rad, ip2long, cal_to_jd (jdto*, *tojd), strtolower,
    strtotime,

PHP没有词法范围

Perl 有词法范围和动态范围。PHP则没有。

对于为什么词法范围很重要的解释,可以参考 Coping with Scoping.

  PHP Perl
超全局(Superglobal) [1]
全局(global)
函数局部
词法域(块局部)
动态域

[1] Perl有一些变量总是在main:: 命名空间中。这些类似于PHP的超全局变量。
[2] 在子过程的块中使用一个词法变量,就可以作为一个函数的局部变量。

PHP的主名空间中函数太多

(使用编译了所有核心分发包中的可用扩展的核心库,我们使用了2003年11月发布的版本)

注意,Perl的一些函数有简短的等价语法:

[1] 来源:PHP Quick Reference
[2] 来源:perldoc perlfunc

PHP缺少抽象令 TIMTOWTDI* 走向糟糕的极端

*(There Is More Than One Way To Do It,有不止一种方式来完成它)

为什么PHP有3079个函数但是Perl却只有206个?在PHP中,常常有好几个十分相似的函数。在Perl中,你要了解和记住的要少很多。

另外一个重要的因素是模块的使用,尤其是DBI模块——它用来提供数据库支持,而不是把很多特性塞进内核,占用了空间却很少用到。

(不常用的模块不计算在内(所以这里排除了PHP的PEAR和Perl的IO::File)). 如果核心没有提供类似的功能,那么这些模块也会算在里面。为了简便起见,内部的工作方式将会忽略。)

[1] 因为系统的LIST语法和DBI的占位符,显式转义常常是不需要的。
[2] 在Perl中是由PerlIO层来处理的。

  • Re^2: Is Perl a good career move? by Juerd, 2005
    • 依然没有命名空间
    • 没有闭包,甚至没有匿名函数
    • 没有良好的HTML分析器
    • 没有简单的MIME构建工具
    • 没有良好的WWW库
    • 没有 CPAN
    • 没有数组
    • 没什么用的逻辑操作符
  • Yaywoo! by Dave Brown, 2004
    • 使用system()无法避免(不安全的)shell
    • XY-problem
    • 大量的不同程序,但是,很多只是做了差不多的事情的变体
    • 第二参数和返回值毫无意义
    • 函数名的差劲拼写方式
  • Why PHP sucks by Edwin Martin, 2004,中文翻译 为什么PHP令人不爽(对于大型系统)
    • 不良的递归支持
    • PHP 不是线程安全的
    • PHP 由于商业原因而不健全
    • 没有命名空间
    • 非标准的日期格式化字符
    • 混乱的许可证
    • 不一致的函数命名规则
    • 魔法引用地狱
  • Perl vs. PHP – octo’s subjektiver Vergleich by Florian Forster, 2003 (German)
    • Perl 比 PHP 快很多
    • Perl 比 PHP 更丰富
    • Perl 比 PHP 有更好的文档
    • PHP 缺乏模块化支持
    • PHP的here-docs对Windows用户毫无用途
    • PHP 缺少一致的数据库API
    • PHP 缓存数据库查询结果很危险
    • 图形上,PHP实际上被限制在了 GD 中
  • I hate PHP by Keith Devens, 2003
    • 白痴似的——调用时不推荐引用传递
  • Experiences of Using PHP in Large Websites by Aaron Crane, 2002
    • PHP 是推荐把表现和业务逻辑结合起来的
    • 没有命名空间造成很多问题
    • php.ini 的全局配置
    • 过分简单化导致了额外的复杂度
  • PHP Annoyances by Neil de Carteret, 2002
    • 没有真正的引用或者指针
    • 毫无命名空间的概念
    • 毫不组件化
    • 想变成Perl,但事实上也没想变成Perl
    • 没有标准的DB接口
    • 所有的PHP社区都是针对非程序员的
    • 不支持链式方法调用 (现在已经不是了 –tnx.nl)
    • 没有全局变量除非通过导入
    • register_globals 和 $_REQUEST 都让人痛心
    • 数组都是哈西表
    • PEAR 并不是 CPAN
    • Arrays 不能内插值替换成字符串(如$a=array();$b="$a";是错误的)
    • 没有类似 “use strict” 用来检验变量名的功能
  • PHP: A love and hate relationship by Ivan Ristic, 2002
    • 社区令我不安
    • 知识渊博的人少之又少
    • Zend 发布的文章还建议不安全的实践方式
  • My list of PHP shortcomings by Nathan Torkington, 2001
    • 没有命名空间
    • 所有的函数都是全局的
    • 没有真正的引用
    • 没有真实的数据结构 (现在已经不是了 –tnx.nl)
    • 没有匿名函数

参考

EFnet #php:
19:45 <+Dragnslcr> Comparing PHP to Perl is like comparing pears to newspapers

Perl Monks: PHP – it’s “training wheels without the bike” — Randal L. Schwartz

为什么PHP令人不爽(对于大型系统)

译者:本文其实非常老,大约是2002年的文章,主要针对的是PHP4的版本,其实文中很多问题都随着PHP的不断发展而解决了。有批评才有进步嘛。

Edwin Martin <edwin@bitstorm.org>.

翻译:ShiningRay @ Nirvana Studio

我在过去的四年里一直致力于PHP应用的开发。PHP确实十分容易编写。但是PHP也有一些十分严重的缺陷。

下面我会给出我的理由,为什么PHP不适合于比小型业余网站更大的网站。

1. 对递归的不良支持

递归是一种函数调用自身的机制。这是一种强大的特性可以把某些复杂的东西变得很简单。有一个使用递归的例子是快速排序(quicksort)。不幸的是,PHP并不擅长递归。Zeev,一个PHP开发人员,说道:“PHP
4.0(Zend)对密集数据使用了栈方式,而不是使用堆方式。也就是说它能容忍的递归函数的数量限制和其他语言比起来明显少。”见bug
1901
。这是一个很不好的借口。每一个编程语言都应该提供良好的递归支持。

2. 许多PHP模块都不是线程安全的

在几年前,Apache发布了Web服务器的2.0版。这个版本支持多线程模式,在这个模式下,软件一个一部分可以同时运行多个。PHP的发明者说PHP的核心是线程安全的,但是非核心模块不一定是。但是十次有九次,你想要在PHP脚本中使用这种模块,但这又使你的脚本不能合适Apache的多线程模式。这也是为什么PHP小组不推荐在Apache
2 的多线程模式下运行PHP
。不良的多线程模式支持使PHP常被认为是Apache 2依然不流行的原因之一。

请阅读这篇讨论: Slashdot: Sites Rejecting Apache 2?.

3. PHP 由于商业原因而不健全

通过使用缓存,PHP的性能可以陡增500%[见基准测试]。那么为什么缓存没有被构建在PHP中呢?因为Zend——PHP的制造者,它在销售自己的Zend
Accelerator
,所以当然,他们不想抛弃自己的商业产品这块肥肉。

但是有另一个可选择的: APC.
(Zend后来推出Zend Optimizer,免费的加速器——译者)

4. 没有命名空间

设想某个人制作了一个PHP模块用来阅读文件。模块中一个函数叫做read。然后另一个人的模块可以读取网页的,同样包含一个函数read。然后我们就无法同时使用这两个模块了,因为PHP不知道你要用哪个函数。

但是有一个很简单的解决方法,那就是命名空间。曾经有人建议PHP5加入这个特性,但不幸得是他没有这么做。现在,没有命名空间,每个函数都必须加上模块名作为前缀,来避免名称冲突。这导致了函数名恐怖得长,例如xsl_xsltprocessor_transform_to_xml让代码难于书写和理解。

5. 不标准的日期格式字符

很多程序员对 日期格式字符
都很熟悉,它是从UNIX和C语言中来的。其他一些编程语言采用了这个标准,但是很奇怪的,PHP有它自己的一套完全不兼容的日期格式字符。在C中,“%j”表示一年中的当天,在PHP中他表示一个月中的当天。然而使事情更混乱的是:Smarty
(一个很流行的PHP模版引擎)的 strftime
函数和 date_format
函数,却使用了C/UNIX的格式化字符。

6. 混乱的许可证

你也许认为PHP是免费的,所有的在手册中提到的PHP模块也是免费的。错了!例如,如果你想在PHP中生成PDF文件,你会在手册中发现两个模块:PDF
ClibPDF。但是这两个都是有商业许可证的。所以,你所使用的每个模块,你都要确保你同意他的许可证。

7. 不一致的函数命名规则

有些函数名称是有多个单词组成的。一般有三种单词组合的习惯:

  1. 直接拼接:getnumberoffiles
  2. 用下划线分开:get_number_of_files
  3. 骆驼法则:getNumberOfFiles

大部分语言选择其中一中。但是PHP都用到了。

例如,你想要把一些特殊字符转换成HTML实体,你会使用函数htmlentities
(直接拼接单词)。如果你要使用相反的功能,你要用到它的小弟弟html_entity_decode。由于某些特殊的原因,这个函数名是由下划线分隔单词。怎么能这样呢?你知道有一个函数叫strpad。或者他是str_pad?每次你都要查看一下到底这个符号是什么或者直接等他出现一个错误。函数是不分大小写的,所以对于PHP来说rawurldecode
RawUrlDecode之间没有什么区别。这也很糟糕,因为两个都使用到了同时他们看上去还不一样,混淆了阅读者。

8. 魔法引用的地狱

魔法引用(Magic quote)可以保护PHP脚本免受SQL注入攻击。这很好。但是出于某些原因,你可以在php.ini中关闭这个配置。所以你如果要写出一个有弹性的脚本,你总要检查魔法引用是开启还是关闭。这样一个“特性”应该让编程更简单,而事实上变得更复杂了。

9. 缺少标准框架

一个成长中的网站没有一个整体框架,最终会变成维护的噩梦。一个框架可以让很多工作变得简单。现在最流行的框架模型时MVC-模型,在其中表现层、业务逻辑和数据库访问都分离开了。

很多PHP网站不使用MVC-模型。他们甚至没有一个框架。甚至现在有一些PHP框架同时你都可以自己写一个,关于PHP的文章和手册没有提高框架的一个字。同时JSP-开发人员使用像Struts的框架、ASP开发人员使用.Net,看起来好像这些概念都广泛被PHP开发人员所了解。这就说明了PHP实际上到底是多专业。

总结

超载的汽车什么问题?

对于非常小的项目,它可以是一个十分符合人意的编程语言。但是对于较大的和更为复杂的项目,PHP就显出他的薄弱了。当你不断地摸索之后,你会发现我提到的某些问题的解决方案。所以,当解决方案已知之后,为什么不能修正他呢?另外为什么这些修补不在手册中提到呢?

一个开源的语言十分流行是一件好事。但不幸得是,它不是一个伟大的语言。我希望所有的问题能有一天得到解决(也许在PHP6?),然后我们就将拥有一个开源语言,他既开源,又好用。

到现在,当你要启动一个多于5个脚本页面的项目的时候,你最好考虑C#/ASP.Net 或者 Java/JSP或者也许Python同样是一个更好的选择。

在我写了这篇文章之后,一些人告诉我一些其他的类似文章:

Cake 指南:建立一个简单的Blog

注意!
本指南针对cake_0.9.1_dev 发布版不是SVN的版本。请不要把指南的代码改成于SVN一样。

Cake 指南:建立一个简单的Blog

这是一个还在不断变化的一个应用框架的指南。文章中的东西可能可以运行,但是如果有些东西无法运行,请您先仔细阅读 API 文档 。错误报告对我们很有价值,所以,请报告任何影响您使用的地方。

注意:命名方法的约定已经从下划线_风格变成了骆驼回归法则。所以,在我们目前的发布版中link_to()已经变成linkTo()

默认Cake目录结构一览

在本篇指南中,我们将一起使用Cake来创建一个简单的Blog应用。我假设你对PHP十分了解,可以在你的系统中游刃有余(包括从命令行中运行程序),同时已经有一个配置好的开发服务器环境,例如运行PHP的 XAMPP

下载

下载最新的Cake 包并解压缩到你的Web服务器的DOCUMENT_ROOT下(因为本文是指南的缘故,我假设他可以从http://localhost/cake/下访问)。你可以看到基本的目录结构。

创建数据库

创建一个用来存放Blog帖子的表格,并且初始化一些数据。以下是SQL语句:

注意表名称是复数形式, 这是Cake使用的所有表格的默认形式。还有,idcreatedupdated字段是有特殊含义的,等一会儿你就会发现。也就是说现在你不应该更改他们的名字

配置Cake的数据库设置

若要配置数据库访问,我们编辑 config/database.php.default (他应该是自己包含说明的), 并将它保存为config/database.php. 如果 config/database.php 不存在,Cake仍会运行,并且不使用数据库访问。

创建一个模型类 (MVC中的Model)

现在给帖子表格posts创建一个模型类,新建 app/models/post.php包含以下内容 :

app/models/post.php

这已经足以使Cake来关注这个模型了,载入、并把它和数据库表格连接在一起。注意:模型类的类名是单数形式。坚持命名约定十分重要,因为如果不这样,Cake就不会自动运行。默认情况下,模型类和数据库表使用同一个单词而前者以单数形式,后者是复数形式。

建立一个控制器助手类

新建一个控制器助手类。把下面的内容放在 app/helpers/posts_helper.php中:

app/helpers/posts_helper.php

创建一个控制器类 (C ontroller)

新建一个控制器类. 把下面的内容放在 app/controllers/posts_controller.php中:

app/controllers/posts_controller.php

控制器已经准备好了,这是我们需要在他里面添上行为(Action)来对存储的数据进行操作。添加一个方法到 PostsController 类中 :

app/controllers/posts_controller.php

PostsController::index() 不需要其他内容了,除了需要一个模版(在Cake中称之为视图“View”)。

建立一个视图 (View)

把下面的代码放入 app/views/posts/index.thtml:

这应该可以正确运行,我们来测试一下。我假设你可以通过浏览 http://localhost/cake/ 来得到Cake目录,这样测试一下我们的新控制器,它指向 http://localhost/cake/posts/index 。你会(希望如此)看到类似于下面的一些东西:

为什么我没看到呢?

如果你遇到了一个页面,上面说“not Found: The requested URL /posts/index was not found on this server,”你可能要使用 http://localhost/cake/index.php?url=posts来访问. 很明显,这很不美观。如果你遇到了一个页面上说“Fatal error: Call to a member function on a non-object …”那么你可能要检查一下你的配置内容,有没有把config/database.php.default 改名为 config/database.php. 参见 Blog指南中的故障及解决方法

我们现在做了些什么?

让我们回顾一下。我们创建了数据库表posts,一个模型类Post,一个控制器PostsController 和他的 index()方法,还有一个视图文件app/views/posts/index.thtml。我觉得他一点也不困难。让我们继续吧。

帖子标题是连接到/cake/posts/view/[post_id]的. 让我们点击它。

噢~,对了,我们忘记添加PostsController::view()行为了。让我们现在完成它:

app/controllers/posts_controller.php

还有视图文件:

app/views/posts/view.thtml

回到浏览器,刷新,看到:

成功了!

添加功能

在指南的第一部分之后,我们有了一个帖子的列表,同时我们可以查看帖子了。当我们完成了第二部分,我们可以:

  • 添加一个新的帖子。
  • 删除不要的帖子。
  • 编辑存在的帖子。

添加一个新的帖子

添加一个新的帖子:

app/controllers/posts_controller.php

同时行为的模版文件是:

app/views/posts/add.thtml

现在你可以通过地址”/cake/posts/add”来访问 add 页面 page at the address “/cake/posts/add”, 或者,我们可以在索引页面的底部放上一个快捷的 “Add new post”连接:

app/views/posts/index.thtml

现在让我们试试添加一些帖子。

像我这样没规矩的,添加了一个没有标题的帖子。当然,我们可以通过数据有效性检验(Data Validation)来避免这种不正确的行为。

数据有效性

数据有效性规则是放在数据模型中的。

app/models/post.php

了解API文档中更多关于有效检验器的内容。

删除一个帖子

app/controllers/posts_controller.php

delete 行为没有模版。在成功删除之后,我们只要显示一个快捷消息(所谓的“flash”),然后转回索引页面。

现在我们添加一个删除行为的连接到每一个blog帖子的视图中:

app/views/posts/index.thtml

在完成它之后,我们就可以删除那些空白标题的帖子了。

编辑帖子

app/controllers/posts_controller.php

app/views/posts/edit.thtml

你也可以在表单标签中用

来代替直接使用html的<input>标签。

同时, 在 index.thtml 中, 我们添加一个编辑连接:

从视图中分离逻辑

让我们回头看一下 index.thtml 视图:

app/views/posts/index.thtml

我们应该从视图中删除 findAll() 的调用,然后把它放在控制器重。这可以建立一种更好的逻辑和视图的分离。然后我们从控制器重为视图获取数据。现在就让我们完成它吧。

在帖子的控制器中,我们从posts模型中获取所有的记录,并且储存在变量 data 中。

app/controllers/posts_controller.php

同时在视图中,我们通过对每行数据的迭代,来全部显示他的内容。

app/views/posts/index.thtml

这太简单了,不是么?

jMemorize 介绍

jMemorize是一个 Java 应用程序,可以使用著名的Leither系统来管理你的抽认卡,让记忆事情不仅有效而且有趣。它可以管理你的整个学习过程和特点分类、统计和一个优雅的外观。

Leitner 原理

Wikipedia的文章这样定义Flashcard (抽认卡):

一个抽认卡是一小块纸片,在学校里用来作辅助教学(主要是英语国家)。抽认卡可以用来记录词汇、历史事件时间、公式等等。使用抽认卡的目的主要是帮助[[记忆]]。你在每个卡片上写下一个问题(同时在背面记下答案),用它们来测试自己,并根据你的测试、学习结果把它们进行排序、分组。

这种策略使得学习更有选择性,也是就:在一组的卡片越难,你就越要花时间复习这一组。于是,你就能节省很多学习时间。

这个方法是由德国心理学家 Sebastian Leitner 在 19世纪70年代提出的。 Pimsleur 语言课也是基于类似的想法。

这个基本思路是根据它们对你的难易程度把卡片划分成不同的级别。这是通过你尝试回答一些重复问题来完成的。每次你了解的一个卡片的正确回答,它就会放到下一个较高的卡片级别中。如果某个卡片你没有回答出来,它就会被放回到开始的级别。

这个系统是和时间表结合在一起的。已经学习过的卡片,会在一个特定的失效期过后,重新被确认为要学习。级别越高,失效时间就越长。例如一个卡片第一
次成功检测过之后,会被安排在一天之后再次学习,当这个卡片连续三次都回答正确了之后,系统会在一周内都认为学过了。只要这个卡片被认为是学过了,它就不
会在当前学习阶段出现。

总之,这个系统将管理你的个人学习过程并让你关注于学习本身,同时它会自动决定哪个事物现在要再学习,尽可能让这些事情从你的时间中抽出来。

特性

  • 新建、编辑、删除和重置卡片。
  • 通过建立卡片分类来安排卡片。
  • 你当前的学习情况的可视化演示。
  • 可方便地自定义你的学习会话设置。这包括以下功能:
    • 选择要学习的分类。
    • 选择只学习新的卡片、过期卡片和所有卡片。
    • 选择卡片/时间的限制。
    • 选择你是否想以普通模式(顺序)学习还是随机模式。
    • 选择预设的时间表或自己创建自定义时间表。
  • 可以搜索你的卡片
  • 还有很多很多

许可证

本软件由 Riad Djemili ( riad.de) 开发以 GPL license.发布。这表示 jMemorize 是自由软件,不仅可以自由分发二进制代码,同时还提供完整的源代码。

JavaScript的类型转换

我阅读了birdshome的在JavaScript中也玩变量类型强行转之后,在文章后面留言。也许我是表达不清楚,所以我想澄清一些情况,首先我反对使用“强制类型转换”这个词。

首先我们可以看C中间的强制类型转换,只能存在于基本类型之间,像整数可表示类型和浮点数类型之间,比如(int)2.45,这是可以的,但是他们和字符串之间都没有强制转换的可能,因为字符串实际是一个指针。因此不可能出现(char *)2.45就可以获得"2.45"这个字符串的情况,反之也不能。
而在C++中,我们可以创建一个类,并且重载强制转换操作,来完成这种情况,那么就必须要求有相应的对象,但是对于基本类型,这也是不可以的。

然后,因为JavaScript在对象模型上主要参考了Java,我们可以再参考一下Java的类型转换:在Java中,基本类型之间的强制转换也不是这样的,比如,整数要转换成字符串,必须使用Integer.toString()静态方法或者String.valueOf()静态方法,把字符串转换为整数,必须使用Integer.valueOf()

可见,不能把JavaScript中的类型转换看作为“强制类型转换”。

在JavaScript中,Double类型和Int类型都是看作为Number对象,因此无论是typeof 1还是typeof 1.0,都是返回number。这样我们可以不用去管是Int还是Double类型,让JavaScript解释引擎内部去处理。

  • 如果要把Number转换成String,可以使用NumbertoString()方法,(象(1).toString()括号必须或者 1 .toString()空格必须,否则会编译出错,如果是变量则无需),或者调用String()函数,以及直接进行字符串连接(字符串变量或常量后直接进行“+”操作)这几种方法都会自动调用解释引擎内部的 NumberToString()函数,或者根据进制调用其它函数,基本类似。
  • 如果要把String转换成Number,可以使用Number()函数,他会自动判断String中是整数还是浮点数,然后内部使用相应的数据类型,另外可以使用全局函数parseInt()和parseFloat(),他们根据你的要求进行转换。同样的,他们在解释引擎内部机制上是使用了 StringToNumber,StringToInt等等内部的函数。
  • 而如果是Double转换成Int,必须使用Math.floor()函数(截尾取整)或者Math.round()(四舍五入)。使用parseInt只能增加运算量,先从Double到String再到Int
  • Int转换成Double,无须考虑任何问题,直接把Int当成Double进行运算

注:NumberString函数是特殊的函数,在JS引擎中,他会自动判断是作为构造函数调用还是普通调用,所以既可以使用new关键字,也可以作为函数直接调用。

关于JS的参考手册,微软有一个CHM十分不错,指南、API参考都有,是Windows脚本技术,中文版,我在MSDN上下载的。而关于JS解释引擎的,我参考的是Netscape的Spidermonkey,现在由Mozilla组织维护。

在数据库中存储层次数据

在数据库中存储层次数据

作者:Gijs Van Tulder
翻译:ShiningRay @ NirvanaStudio

无论你要构建自己的论坛,在你的网站上发布消息还是书写自己的cms [1]程序,你都会遇到要在数据库中存储层次数据的情况。同时,除非你使用一种像XML [2]的数据库,否则关系数据库中的表都不是层次结构的,他们只是一个平坦的列表。所以你必须找到一种把层次数据库转化的方法。

存储树形结构是一个很常见的问题,他有好几种解决方案。主要有两种方法:邻接列表模型和改进前序遍历树算法

在本文中,我们将探讨这两种保存层次数据的方法。我将举一个在线食品店树形图的例子。这个食品店通过类别、颜色和品种来组织食品。树形图如下:

1105_tree

本文包含了一些代码的例子来演示如何保存和获取数据。我选择PHP [3]来写例子,因为我常用这个语言,而且很多人也都使用或者知道这个语言。你可以很方便地把它们翻译成你自己用的语言。

邻接列表模型(The Adjacency List Model)

我们要尝试的第一个——也是最优美的——方法称为“邻接列表模型”或称为“递归方法”。它是一个很优雅的方法因为你只需要一个简单的方法来在你的树中进行迭代。在我们的食品店中,邻接列表的表格如下:

1105_table1

如你所见,对每个节点保存一个“父”节点。我们可以看到“Pear [4]”是“Green”的一个子节点,而后者又是“Fruit”的子节点,如此类推。根节点,“Food”,则他的父节点没有值。为了简单,我只用了“title”值来标识每个节点。当然,在实际的数据库中,你要使用数字的ID。

显示树

现在我们已经把树放入数据库中了,得写一个显示函数了。这个函数将从根节点开始——没有父节点的节点——同时要显示这个节点所有的子节点。对于这些子节点,函数也要获取并显示这个子节点的子节点。然后,对于他们的子节点,函数还要再显示所有的子节点,然后依次类推。

也许你已经注意到了,这种函数的描述,有一种普遍的模式。我们可以简单地只写一个函数,用来获得特定节点的子节点。这个函数然后要对每个子节点调用自身来再次显示他们的子节点。这就是“递归”机制,因此称这种方法叫“递归方法”。

要实现整个树,我们只要调用函数时用一个空字符串作为$parent$level = 0: display_children('',0); 函数返回了我们的食品店的树状图如下:

注意如果你只想看一个子树,你可以告诉函数从另一个节点开始。例如,要显示“Fruit”子树,你只要display_children('Fruit',0);

节点的路径

利用差不多的函数,我们也可以查询某个节点的路径如果你只知道这个节点的名字或者ID。例如,“Cherry”的路径是“Food”>“Fruit”>“Red”。要获得这个路径,我们的函数要获得这个路径,这个函数必须从最深的层次开始:“Cheery”。但后查找这个节点的父节点,并添加到路径中。在我们的例子中,这个父节点是“Red”。如果我们知道“Red”是“Cherry”的父节点。

这个函数现在返回了指定节点的路径。他把路径作为数组返回,这样我们可以使用print_r(get_path('Cherry')); 来显示,其结果是:

不足

正如我们所见,这确实是一个很好的方法。他很容易理解,同时代码也很简单。但是邻接列表模型的缺点在哪里呢?在大多数编程语言中,他运行很慢,效率很差。这主要是“递归”造成的。我们每次查询节点都要访问数据库。

每次数据库查询都要花费一些时间,这让函数处理庞大的树时会十分慢。

造成这个函数不是太快的第二个原因可能是你使用的语言。不像Lisp这类语言,大多数语言不是针对递归函数设计的。对于每个节点,函数都要调用他自己,产生新的实例。这样,对于一个4层的树,你可能同时要运行4个函数副本。对于每个函数都要占用一块内存并且需要一定的时间初始化,这样处理大树时递归就很慢了。

改进前序遍历树

现在,让我们看另一种存储树的方法。递归可能会很慢,所以我们就尽量不使用递归函数。我们也想尽量减少数据库查询的次数。最好是每次只需要查询一次。

我们先把树按照水平方式摆开。从根节点开始(“Food”),然后他的左边写上1。然后按照树的顺序(从上到下)给“Fruit”的左边写上2。这样,你沿着树的边界走啊走(这就是“遍历”),然后同时在每个节点的左边和右边写上数字。最后,我们回到了根节点“Food”在右边写上18。下面是标上了数字的树,同时把遍历的顺序用箭头标出来了。

1105_numbering

我们称这些数字为左值和右值(如,“Food”的左值是1,右值是18)。正如你所见,这些数字按时了每个节点之间的关系。因为“Red”有3和6两个值,所以,它是有拥有1-18值的“Food”节点的后续。同样的,我们可以推断所有左值大于2并且右值小于11的节点,都是有2-11的“Food”节点的后续。这样,树的结构就通过左值和右值储存下来了。这种数遍整棵树算节点的方法叫做“改进前序遍历树”算法。

在继续前,我们先看看我们的表格里的这些值:

1105_table2

注意单词“left”和“right”在SQL中有特殊的含义。因此,我们只能用“lft”和“rgt”来表示这两个列。(译注——其实Mysql中可以用“”来表示,如“left`”,MSSQL中可以用“[]”括出,如“[left]”,这样就不会和关键词冲突了。)同样注意这里我们已经不需要“parent”列了。我们只需要使用lft和rgt就可以存储树的结构。

获取树

如果你要通过左值和右值来显示这个树的话,你要首先标识出你要获取的那些节点。例如,如果你想获得“Fruit”子树,你要选择那些左值在2到11的节点。用SQL语句表达:

这个会返回:

1105_table3

好吧,现在整个树都在一个查询中了。现在就要像前面的递归函数那样显示这个树,我们要加入一个ORDER BY子句在这个查询中。如果你从表中添加和删除行,你的表可能就顺序不对了,我们因此需要按照他们的左值来进行排序。

就只剩下缩进的问题了。

要显示树状结构,子节点应该比他们的父节点稍微缩进一些。我们可以通过保存一个右值的一个栈。每次你从一个节点的子节点开始时,你把这个节点的右值添加到栈中。你也知道子节点的右值都比父节点的右值小,这样通过比较当前节点和栈中的前一个节点的右值,你可以判断你是不是在显示这个父节点的子节点。当你显示完这个节点,你就要把他的右值从栈中删除。要获得当前节点的层数,只要数一下栈中的元素。

如果运行这段代码,你可以获得和上一部分讨论的递归函数一样的结果。而这个函数可能会更快一点:他不采用递归而且只是用了两个查询

节点的路径

有了新的算法,我们还要另找一种新的方法来获得指定节点的路径。这样,我们就需要这个节点的祖先的一个列表。

由于新的表结构,这不需要花太多功夫。你可以看一下,例如,4-5的“Cherry”节点,你会发现祖先的左值都小于4,同时右值都大于5。这样,我们就可以使用下面这个查询:

注意,就像前面的查询一样,我们必须使用一个ORDER BY子句来对节点排序。这个查询将返回:

我们现在只要把各行连起来,就可以得到“Cherry”的路径了。

有多少个后续节点?How Many Descendants

如果你给我一个节点的左值和右值,我就可以告诉你他有多少个后续节点,只要利用一点点数学知识。

因为每个后续节点依次会对这个节点的右值增加2,所以后续节点的数量可以这样计算:

利用这个简单的公式,我可以立刻告诉你2-11的“Fruit”节点有4个后续节点,8-9的“Banana”节点只是1个子节点,而不是父节点。

自动化树遍历

现在你对这个表做一些事情,我们应该学习如何自动的建立表了。这是一个不错的练习,首先用一个小的树,我们也需要一个脚本来帮我们完成对节点的计数。

让我们先写一个脚本用来把一个邻接列表转换成前序遍历树表格。

这是一个递归函数。你要从rebuild_tree('Food',1); 开始,这个函数就会获取所有的“Food”节点的子节点。

如果没有子节点,他就直接设置它的左值和右值。左值已经给出了,1,右值则是左值加1。如果有子节点,函数重复并且返回最后一个右值。这个右值用来作为“Food”的右值。

递归让这个函数有点复杂难于理解。然而,这个函数确实得到了同样的结果。他沿着树走,添加每一个他看见的节点。你运行了这个函数之后,你会发现左值和右值和预期的是一样的(一个快速检验的方法:根节点的右值应该是节点数量的两倍)。

添加一个节点

我们如何给这棵树添加一个节点?有两种方式:在表中保留“parent”列并且重新运行rebuild_tree()
函数——一个很简单但却不是很优雅的函数;或者你可以更新所有新节点右边的节点的左值和右值。

第一个想法比较简单。你使用邻接列表方法来更新,同时使用改进前序遍历树来查询。如果你想添加一个新的节点,你只需要把节点插入表格,并且设置好parent列。然后,你只需要重新运行rebuild_tree() 函数。这做起来很简单,但是对大的树效率不高。

第二种添加和删除节点的方法是更新新节点右边的所有节点。让我们看一下例子。我们要添加一种新的水果——“Strawberry”,作为“Red”的最后一个子节点。首先,我们要腾出一个空间。“Red”的右值要从6变成8,7-10的“Yellow”节点要变成9-12,如此类推。更新“Red”节点意味着我们要把所有左值和右值大于5的节点加上2。

我们用一下查询:

现在我们可以添加一个新的节点“Strawberry”来填补这个新的空间。这个节点左值为6右值为7。

如果我们运行display_tree() 函数,我们将发现我们新的“Strawberry”节点已经成功地插入了树中:

缺点

首先,改进前序遍历树算法看上去很难理解。它当然没有邻接列表方法简单。然而,一旦你习惯了左值和右值这两个属性,他就会变得清晰起来,你可以用这个技术来完成临街列表能完成的所有事情,同时改进前序遍历树算法更快。当然,更新树需要很多查询,要慢一点,但是取得节点却可以只用一个查询。

总结

你现在已经对两种在数据库存储树方式熟悉了吧。虽然在我这儿改进前序遍历树算法性能更好,但是也许在你特殊的情况下邻接列表方法可能表现更好一些。这个就留给你自己决定了

最后一点:就像我已经说得我部推荐你使用节点的标题来引用这个节点。你应该遵循数据库标准化的基本规则。我没有使用数字标识是因为用了之后例子就比较难读。

进一步阅读

数据库指导 Joe Celko写的更多关于SQL数据库中的树的问题:
http://searchdatabase.techtarget.com/tip/1,289483,sid13_gci537290,00.html [6]

另外两种处理层次数据的方法:
http://www.evolt.org/article/Four_ways_to_work_with_hierarchical_data/17/4047/index.html [7]

Xindice, “本地XML数据库”:
http://xml.apache.org/xindice/ [8]

递归的一个解释:
http://www.strath.ac.uk/IT/Docs/Ccourse/subsection3_9_5.html [9]

[1] http://www.sitepoint.com/glossary.php?q=C#term_28
[2] http://www.sitepoint.com/glossary.php?q=X#term_3
[3] http://www.sitepoint.com/glossary.php?q=P#term_1
[4] http://www.sitepoint.com/glossary.php?q=P#term_50
[5] http://www.sitepoint.com/glossary.php?q=%23#term_72
[6] http://searchdatabase.techtarget.com/tip/1,289483,sid13_gci537290,00.html
[7] http://www.evolt.org/article/Four_ways_to_work_with_hierarchical_data/17/4047/index.html
[8] http://xml.apache.org/xindice/
[9] http://www.strath.ac.uk/IT/Docs/Ccourse/subsection3_9_5.html

PEAR DB_Table 简介

翻译:ShiningRay @ NirvanaStudio

原文地址:http://wiki.ciaweb.net/yawiki/index.php?area=DB_Table

什么是 DB_Table?

DB_Table 是一个用于访问数据库表的面向对象接口。作为一个PEAR DB类的一个 包装,它提供了一些帮助你构建自动的创建、插入、更新和选择功能的方法。同时还有利用 HTML_QuickForm 自动构建输入表单的方法。

DB_Table提供了什么?

DB_Table 提供了:

  • 一个嵌在类属性中的模式描述系统,包括:
    • 列定义
    • 索引定义
    • 标准查询
    • 创建HTML_QuickForm元素的属性
  • 从描述的模式自动创建表
  • 一个抽象的API这样即便你改变了数据库后台也不需要改变你的PHP调用。这套API扩展了PEAR DB的功能,同时包含:
    • 用于根据预定义的SQL查询来获取一个结果数组的select() 方法
    • 用于根据预定义的SQL查询来获取一个PEAR DB_Result 对象的selectResult() 方法
    • create(), insert(), update(), 和 delete() 方法
    • 自动模式检验
    • 根据描述的表模式进行插入和更新字段时的自动检验
  • 时间和日期数据类型抽象,覆盖了数据库的原始数据类型
    • 即使你更改了数据库的后端,也无需修改你的查询。
    • 不需要通过类型转换方法来改变查询的值。
    • 当你插入或者更新列时,DB_Table 根据DB_Table 数据类型自动检验数据(datatype automatically (对所有的数据类型: integer, string,等等.).
  • 根据描述的模式自动创建HTML_QuickForm元素,利用以下方法:
    • getForm() 获取整个表单对象,有HTML_QuickForm元素和规则。
    • getFormGroup() 获取一组HTML_QuickForm元素。
    • getFormElement() 获取单一的HTML_QuickForm元素
    • addFormElements?() 对一个存在的HTML_QuickForm元素添加一个表单元素。
  • DB_Table 支持以下数据库(通过PEAR DB):
    • FrontBase
    • Microsoft SQL
    • MySQL
    • Oracle 8i
    • PostgreSQL
    • SQLite

类似的项目

PEAR MDBMDB2 提供真正的日期和时间数据类型的抽象,以及根据XML定义模式新建表的功能,但是它缺乏自动查询定义也不支持快速生成表单。同时,即使你在XML文件中定义了你的模式,每次进行查询的时候你还是要手工通知MDB/MDB2你的列类型是什么。

Metabase (by Manuel Lemos) 是PEAR MDB的祖先, Manuel也写了 Metastorage

PEAR DB_DataObject 是一个完整的数据对象封装器,支持完整的模式和连接表格,可以在一个外部的.ini文件中描述。

Propel 是“一个为PHP5设计的全服务对象持久和查询工具”。他的意思,用实际的语言说,就是Propel可以让你把你的数据库当成一系列的对象,提供了一套简便的API来存储和查询数据。其他与这类工具相关的名词有:对象关系映射(Object Relational Mapping, ORM)和数据访问对象(Data Access Objects,DAO)。Propel提供了一个生成器,用来创建SQL语句和PHP类同时提供了管理对象持久化和取回的运行时环境。

DB_DataContainer 和DB_Table十分相似。从网站上看“DB_DataContainer是一个PEAR兼容的数据库持久层和数据封装类。一个持久层封装了持久化对象所需要的行为,也就是,从一个持久存储器中载入、保存和删除对象,一般来说这个存储器是一个SQL数据库服务器。目前唯一支持的持久机制是关系数据库。数据库抽象由PEAR DB提供。未来将会提供对普通文件的支持。

使用XPWeb应用极限编程(隐喻)

隐喻

XP隐喻 (metaphor)必须能帮助你用可以理解的语言描述你的系统。

为了达到这个目的,我们可以在XP< /span>Web的“隐喻”一节中列出所有的隐喻术语。这些术语由一个名字/标题和他的解释组成。

“超描述”

通常作为“超描述”的东西现在是一个称为“文本模式”的一个大的功能特性。
如果在隐喻中定义了一个词,把{}放在词的两边,就可以把它变成“超词”……
注意你可以用词的复数形式(“words”可以找到“word”定义)。

事实上,搜索定义有三个级别:

  1. 搜索词的实际定义
  2. 如果没有结果,查找包含该词的那个词的定义。
  3. 最后,查找词的定义中包含该词的。

要获得更多的关于模式和如何修改的信息,看这里.

例子

参照只包含两个词的隐喻的例子,同时你注意第一个“连接”到了第二个。

尝试点击“senses”连接在第一个词的描述中,应该会出现一个弹出窗口连接到第二个词……

同时,通过点击“修改”,注意这个连接只会基于这个词的复数形式(用括号括出)。

使用XPWeb应用极限编程(安装)

ShiningRay @ NirvanaStudio

安装

安装 XP Web

  • 确保你已经拥有mysql(或者其他数据库,请在配置中指定)和一个可以运行PHP4或PHP5脚本的Web服务器(和相应的PHP库!如:如果要连接postgresql数据库必须启用php_pgsql扩展)。
  • [ 下载 ] the latest version of the tool (XPWeb.tgz 或者 XPWeb.zip).
  • [ 解压 ] (unzip 或者 ungzip 和 untar) 文档.
  • 把目录中的XPWeb 目录放到那个可以运行PHP和数据库的网络服务器的目录中。
  • 你也许想在配置文件里[ 配置 ] 一些参数,例如数据库名称,登录名和密码 ,
    注意:配置十分重要,包括负载因子和其他参数,必须在初始化/更新数据库之前完成……

    You should do this in a [ 本地配置]

    你只要新建一个 [ Config_local.inc.php ] 文件,并把它放到XPWeb的根目录下 -和Config.inc.php同一位置- 并且将你要“本地化”的设置从 Config.inc.php 中复制过来。

    你在本地进行的设置会覆盖默认的设置,那些没有放在本地文件中的设置就会设为默认。

    既然这样当升级到新的XPWeb的时候,可能会有一些新的默认设置,但不用担心你已经存在的本地设置(分发包里面不包含Config_local.inc.php文件,所以可以保证你在升级的时候原来的设置不会被覆盖……)
    这里有一个完整的Config_local.inc.php 文件的例子:

    <?php

    defineOnce(“XP_REPORT_TEMP_PATH”,”../../temp”);

    defineOnce(“XP_DEFAULT_LANGUAGE”,”fr”);

    defineOnce(“XP_DB_PASS”,”myPass”);

    ?>

    记住你无需复制所有的设置,只需要那些你要对默认设置进行改变的那些值……

  • 使用浏览器访问 [ index.php ] 页面,这时页面应该给出一个连接来新建或者初始化数据库。

升级 XP Web

任何XPWeb 的升级都会保证你所有的数据安全,当然也不会改变你使用 XPWeb 的习惯;-)

  • 如何创建一个本地的配置文件已经在上面解释过了。如果你已经有了一个文件, [ keep it safe ].
  • 按照一般安装的步骤替换你的XPWeb 目录(你不会丢失任何项目数据)。
  • 只要简单的 复制 你的 Config_local.inc.php文件到 XPWeb 根目录 (XPWeb/).
  • 浏览[ XPWeb/index.php ] 并按照他的指示来更新数据库。
    注意: 在进行数据库初始化或更新之前必须有一个正确的配置文件……
  • 好了,你肯定跃跃欲试了!
  • XPWeb 2.2之前版本进行升级的注意点

    XPWeb 直到2.2,任务是用“完成百分率”表示的。从2.2到以后,你要使用“done”和“todo”字段来替代。数据库的升级会删除“actual completion”(实际完成)字段并且生成相应的“done”和“todo”字段。确保你想要直接跳到当前版本。
    你也许应该考虑在升级之前备份一下你的数据库……

问题?

  • 你的Web服务器的配置文件应该说明以“.php”为扩展名的文件是PHP代码文件并且 “<?php” 和 “?>” 之间的代码是 php (这时通常的配置).
  • 要有一个正在运行的数据库服务器同时你必须在Config_local.inc.php中指定正确的 [ 数据库 用户/密码] 参数。
  • 如果你在身分验证上遇到了麻烦,你可以检查PHP Session 配置。你需要能在Web服务器上能处理Session文件。
    你也可以改变Config_local.inc.php 中的XP_SESSION_PATH 参数
  • 不能管理选择当前的项目或者在本地网络上登录 XPWeb ?
    这里有一个对于IE的 已知的bug你可能感兴趣(服务器名不能包含点)。
  • 请在你想问我们关于PHP的配置的问题前检查一下 php.net 手册……
  • 一般来说,关于[ 使用 ] 而不是 [ 安装 ] XPWeb的帮助, 请记得参考内部的帮助文件。
  • 请访问Sourceforge.net上的在线论坛来获得更多的帮助。

相关连接

  • XPWeb网站
  • XP Web 在Sourceforge.net上的论坛


  • Wampserver PHP/MySql for Windows
  • 熵.ch PHP5 for Mac OS X
  • PHP 安装
  • MySql 安装
  • CVSWeb
  • xUnit 测试框架
  • XProgramming.com
  • 其他 XP 软件
  • 极限编程Wiki