2013年5月21日星期二

阮一峰的网络日志

阮一峰的网络日志


如何理解当代中国----《十亿消费者》读后感

Posted: 20 May 2013 08:20 PM PDT

前几天,我读完的美国人 James McGregor 写的《十亿消费者》(One Billion Customers)。

James McGregor 本来是《华尔街日报》和道琼斯集团在华负责人,后来辞职经商。为了对记者生涯做总结,他就写了这本书,让西方人了解如何在中国做生意。

在书中,他通过对一些涉及高层的案例介绍,披露了中国政府商业管理的内幕,对中国社会有精准深刻的分析。很难相信,一个外国人如此懂中国。

我认为,对于想在中国经商的人,这本书是必读的。尤其是如何处理与中国政府的商业关系,大概很难找到比它更好的读物了。

此书不太可能在国内正式出版,译言网有一个网友翻译的中文版,质量相当好,推荐阅读。

下面就是我整理的一些摘录。

========================================

《十亿消费者》摘录

作者: [美] James McGregor

(题图:刘勃麟的 Hiding in the City 系列)

一、 中国文化

1.

贤明君主自上而下的管理,是中国数千年社会秩序的基础。直到今天,也是中国普遍流行的商业管理模式。

2.

中国文化的核心,就是追求和谐,哪怕这种和谐是表面的。

3.

中国文化提倡,个人追求恭顺和礼仪,接受来自上层的命令和决策。

4.

中国儿童在生活中学到的第一课就是纪律,家长教育孩子遵守纪律。

5.

中国文化提倡克制、克制、再克制。

6.

中国人习惯服从领导。领导人可以对所有问题下命令,甚至包括下属的家庭矛盾。

7.

由于推崇稳定和强调服从,中国人对那些充满魅力、照顾属下、发号施令的领袖惟命是从。

8.

中国人的这种服从性格,在制造业中颇有成效。但是一旦走出国门,或者进入需要更具创造力的领域如研发,中国模式的缺点就暴露无遗。

9.

什么是中国公司中最常见的四个字?"听领导的!"

二、 重视稳定和秩序

10.

中国政府最重视的,就是稳定和秩序。

11.

不管能否控制,中国政府假装一切皆在控制之中。

12.

任何敢于公开与政府对抗的人,都将在国家稳定的名义下遭到无情镇压。

13.

想在中国生存,你不能让这个体制感到为难。

14.

如果想要打击对手,你可以把对手描绘成破坏体制的人。

15.

与中国政府产生矛盾时,你要说明你公司的业务是如何有利于中国,而不是政府如何错了。你不能让人觉得体制很差。

16.

为了维持表面上的秩序,中国人的行为准则,不是有没有内疚,而是会不会被揭露。

17.

只要不被抓住,中国人做任何事都可以心安理得。在这种环境下,政府不得不变得强权和无所不在,加大你作恶之后被抓住的可能。

18.

中国的法律假设,一切皆在政府控制之下,除非法律明文允许,否则你做的事情都是不合法的。而美国法律假设,政府不得干涉人民自由,除非法律明文禁止,否则你可以做任何事情。

19.

为什么中国的交通很乱?因为各种政治压制和社会控制的存在,中国人把油门和方向盘当作发泄工具。

三、 教育和思想控制

20.

中国教育体制培养出来的人是被领导的,而不是领导别人。

21.

这种教育体制的结果,就是强大但却缺乏主动的劳动力大军,而创新性的商业领袖和各种经理人非常之少。

22.

教育在中国是最大的优势,也是最大的劣势。

23.

中国人记忆超群,精于数学,安于枯燥冗长工作。但是死记硬背的教育体系大大削弱了人们的分析和领导能力。

24.

为了维持秩序,中国政府对信息和思想实施控制。这样必然会扼杀创新。

25.

政府相信必须控制信息才能保住权力,但是中国需要公民掌握充分的信息,才能在全球经济中竞争。这使得媒体得以探索自己的笼子到底有多大。

26.

自由意味着知道你自己的笼子有多大。

四、 官员的务实性

27.

表面上要维护秩序,但实际上,为了有效管理国家,中国政府官员在效率和私人问题上是非常务实的。

28.

从高级到基层的党政官员,满嘴都是最新的口号,但是一旦这些官方的政治反刍完毕,谈话立刻就转到经商。

29.

对于大多数中国官员来说,生活的准则就是"指鹿为马"。说的是一套,做的是另一套,因为否则就会威胁到整个体制的稳定。

30.

由于这种两面性的存在,中国的反腐收效甚微,因为整个体制与诚实正直格格不入。

31.

中国政府在决策和处理与人民关系的时候,更像在经营一家公司。就像在公司里一样,党的高层有一些民主,但是在基层则几乎没有。

32.

由于人民不相信政府,政府只能通过促进经济增长,来保持威信。加快经济发展速度,政府只有两个药方:私有化和大量投资。

33.

中国政府喜欢那些能帮助国家解决困难的公司。

五、 重视金钱利益

34.

对政治体系的不信任、腐败、快速的形势变化,又没有社会保障,一切让中国人惴惴不安。大家都觉得,获得安全感的唯一方法,就是快速获得财富。

35.

很多中国人只信任钱。

36.

在葬礼上,一个核心环节就是烧纸钱,给死人送去资产。

37.

在婚礼上,来宾们在公然注视下,排队送礼金,每个人的信封被撕开、清点、并纪录下来。

38.

由于急于致富,除非被迫,否则没有人会排队。每个人都拼命往前挤。这也是商业的运作模式。

39.

快速致富的必然推论是"谁也不要相信"。中国人对体制、对陌生人有着深深的不信任。结果就是商业环境中到处是不诚实。

40.

中国人之间毫无信任可言。在中国做买卖,人们的预期就是对方会骗自己。

41.

中国社会是自私的。中国人竞争能力很强,合作能力很弱。

42.

中国人是全世界最自我、最自私的民族之一。

43.

单个来说,中国人是凶猛可怕的商人。但是,中国人很难组建大型组织,这样的组织需要人们分享观点、平等共处。

44.

在中国,你要么有钱,要么听话。

六、 人际关系与法律

45.

因为表面上需要维持秩序,而实际上又是另一套做法,所以,人际关系在中国社会的重要性,远大于西方。

46.

法律和合同的规定,不如人际关系重要。

47.

在中国做生意,不要完全依赖于法律,你会输的。法律只是你商业行为的一种论据。

48.

中国人事部门主管的权力远高过西方,因为那些被录用的人往往对他们心怀感激。

49.

机会来自和有权有势的人的交往。

50.

在这个需要和层层官僚及个人利益打交道的国家,单靠个人能力是无法成事的。

51.

在一个缺乏公平和公正的法律体系中,你的人际网络能确保你的安全。

52.

商业公司如果不把政府关系当作业务最关键的一部分,那么它的业务就会出现问题。

53.

在中国,市场总能取得胜利。

54.

亲吻干部,拥抱客户。

(完)

文档信息

2013年5月11日星期六

阮一峰的网络日志

阮一峰的网络日志


如何做到 jQuery-free?

Posted: 11 May 2013 04:59 AM PDT

jQuery是现在最流行的JavaScript工具库。

统计,目前全世界57.3%的网站使用它。也就是说,10个网站里面,有6个使用jQuery。如果只考察使用工具库的网站,这个比例就会上升到惊人的91.7%。

虽然jQuery如此受欢迎,但是它臃肿的体积也让人头痛不已。jQuery 2.0的原始大小为235KB,优化后为81KB;如果是支持IE6、7、8的jQuery 1.8.3,原始大小为261KB,优化后为91KB。

这样的体积,即使是宽带环境,完全加载也需要1秒或更长,更不要说移动设备了。这意味着,如果你使用了jQuery,用户至少延迟1秒,才能看到网页效果。考虑到本质上,jQuery只是一个操作DOM的工具,我们不仅要问:如果只是为了几个网页特效,是否有必要动用这么大的库?

2006年,jQuery诞生的时候,主要用于消除不同浏览器的差异(主要是IE6),为开发者提供一个简洁的统一接口。相比当时,如今的情况已经发生了很大的变化。IE的市场份额不断下降,以ECMAScript为基础的JavaScript标准语法,正得到越来越广泛的支持。开发者直接使用JavScript标准语法,就能同时在各大浏览器运行,不再需要通过jQuery获取兼容性。

下面就探讨如何用JavaScript标准语法,取代jQuery的一些主要功能,做到jQuery-free。

一、选取DOM元素

jQuery的核心是通过各种选择器,选中DOM元素,可以用querySelectorAll方法模拟这个功能。

  var $ = document.querySelectorAll.bind(document);

这里需要注意的是,querySelectAll方法返回的是NodeList对象,它很像数组(有数字索引和length属性),但不是数组,不能使用pop、push等数组特有方法。如果有需要,可以考虑将Nodelist对象转为数组。

  myList = Array.prototype.slice.call(myNodeList);

二、DOM操作

DOM本身就具有很丰富的操作方法,可以取代jQuery提供的操作方法。

尾部追加DOM元素。

  // jQuery写法
  $(parent).append($(child));

  // DOM写法
  parent.appendChild(child)

头部插入DOM元素。

  // jQuery写法
  $(parent).prepend($(child));

  // DOM写法
  parent.insertBefore(child, parent.childNodes[0])

删除DOM元素。

  // jQuery写法
  $(child).remove()

  // DOM写法
  child.parentNode.removeChild(child)

三、事件的监听

jQuery的on方法,完全可以用addEventListener模拟。

  Element.prototype.on = Element.prototype.addEventListener;

为了使用方便,可以在NodeList对象上也部署这个方法。

  NodeList.prototype.on = function (event, fn) {
    []['forEach'].call(this, function (el) {
      el.on(event, fn);
    });
    return this;
  };

四、事件的触发

jQuery的trigger方法则需要单独部署,相对复杂一些。

  Element.prototype.trigger = function (type, data) {
    var event = document.createEvent('HTMLEvents');
    event.initEvent(type, true, true);
    event.data = data || {};
    event.eventName = type;
    event.target = this;
    this.dispatchEvent(event);
    return this;
  };

在NodeList对象上也部署这个方法。

  NodeList.prototype.trigger = function (event) {
    []['forEach'].call(this, function (el) {
      el['trigger'](event);
    });
    return this;
  };

五、document.ready

目前的最佳实践,是将JavaScript脚本文件都放在页面底部加载。这样的话,其实document.ready方法(jQuery简写为$(function))已经不必要了,因为等到运行的时候,DOM对象已经生成了。

六、attr方法

jQuery使用attr方法,读写网页元素的属性。

  $("#picture").attr("src", "http://url/to/image");

DOM元素允许直接读取属性值,写法要简洁许多。

  $("#picture").src = "http://url/to/image";

需要注意,input元素的value属性返回的是输入框中的值,链接元素的href属性返回的是绝对URL。如果需要用到这两个网页元素的属性准确值,可以用this.getAttribute('value')和this.getAttibute('href')。

七、addClass方法

jQuery的addClass方法,用于为DOM元素添加一个class。

  $('body').addClass('hasJS');

DOM元素本身有一个可读写的className属性,可以用来操作class。

  document.body.className = 'hasJS';

  // or

  document.body.className += ' hasJS';

HTML 5还提供一个classList对象,功能更强大(IE 9不支持)。

  document.body.classList.add('hasJS');

  document.body.classList.remove('hasJS');

  document.body.classList.toggle('hasJS');

  document.body.classList.contains('hasJS');

八、CSS

jQuery的css方法,用来设置网页元素的样式。

  $(node).css( "color", "red" );

DOM元素有一个style属性,可以直接操作。

  element.style.color = "red";;

  // or

  element.style.cssText += 'color:red';

九、数据储存

jQuery对象可以储存数据。

  $("body").data("foo", 52);

HTML 5有一个dataset对象,也有类似的功能(IE 10不支持),不过只能保存字符串。

  element.dataset.user = JSON.stringify(user);

  element.dataset.score = score;

十、Ajax

jQuery的Ajax方法,用于异步操作。

  $.ajax({
    type: "POST",
    url: "some.php",
    data: { name: "John", location: "Boston" }
  }).done(function( msg ) {
    alert( "Data Saved: " + msg );
  });

我们可以定义一个request函数,模拟Ajax方法。

  function request(type, url, opts, callback) {

    var xhr = new XMLHttpRequest();

    if (typeof opts === 'function') {
      callback = opts;
      opts = null;
    }

    xhr.open(type, url);

    var fd = new FormData();

    if (type === 'POST' && opts) {
      for (var key in opts) {
        fd.append(key, JSON.stringify(opts[key]));
      }
    }

    xhr.onload = function () {
      callback(JSON.parse(xhr.response));
    };

    xhr.send(opts ? fd : null);

  }

然后,基于request函数,模拟jQuery的get和post方法。

  var get = request.bind(this, 'GET');

  var post = request.bind(this, 'POST');

十一、动画

jQuery的animate方法,用于生成动画效果。

  $foo.animate('slow', { x: '+=10px' });

jQuery的动画效果,很大部分基于DOM。但是目前,CSS 3的动画远比DOM强大,所以可以把动画效果写进CSS,然后通过操作DOM元素的class,来展示动画。

  foo.classList.add('animate');

如果需要对动画使用回调函数,CSS 3也定义了相应的事件。

  el.addEventListener("webkitTransitionEnd", transitionEnded);

  el.addEventListener("transitionend", transitionEnded);

十二、替代方案

由于jQuery体积过大,替代方案层出不穷。

其中,最有名的是zepto.js。它的设计目标是以最小的体积,做到最大兼容jQuery的API。zepto.js 1.0版的原始大小是55KB,优化后是29KB,gzip压缩后为10KB。

如果不求最大兼容,只希望模拟jQuery的基本功能,那么,min.js优化后只有200字节,而dolla优化后是1.7KB。

此外,jQuery本身采用模块设计,可以只选择使用自己需要的模块。具体做法参见它的github网站,或者使用专用的Web界面

十三、参考链接

  - Remy Sharp,I know jQuery. Now what?
  - Hemanth.HM,Power of Vanilla JS
  - Burke Holland,5 Things You Should Stop Doing With jQuery

(完)

文档信息

2013年5月3日星期五

阮一峰的网络日志

阮一峰的网络日志


字符串匹配的Boyer-Moore算法

Posted: 02 May 2013 10:47 PM PDT

上一篇文章,我介绍了KMP算法

但是,它并不是效率最高的算法,实际采用并不多。各种文本编辑器的"查找"功能(Ctrl+F),大多采用Boyer-Moore算法

Boyer-Moore算法不仅效率高,而且构思巧妙,容易理解。1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了这种算法。

下面,我根据Moore教授自己的例子来解释这种算法。

1.

假定字符串为"HERE IS A SIMPLE EXAMPLE",搜索词为"EXAMPLE"。

2.

首先,"字符串"与"搜索词"头部对齐,从尾部开始比较。

这是一个很聪明的想法,因为如果尾部字符不匹配,那么只要一次比较,就可以知道前7个字符肯定不是要找的结果。

我们看到,"S"与"E"不匹配。这时,"S"就被称为"坏字符"(bad character),即不匹配的字符。我们还发现,"S"不包含在搜索词"EXAMPLE"之中,这意味着可以把搜索词直接移到"S"的后一位。

3.

依然从尾部开始比较,发现"P"与"E"不匹配,所以"P"是"坏字符"。但是,"P"包含在搜索词"EXAMPLE"之中。所以,将搜索词后移两位,两个"P"对齐。

4.

我们由此总结出"坏字符规则"

  后移位数 = 坏字符的位置 - 搜索词中的上一次出现位置

如果"坏字符"不包含在搜索词之中,则上一次出现位置为 -1。

以"P"为例,它作为"坏字符",出现在搜索词的第6位(从0开始编号),在搜索词中的上一次出现位置为4,所以后移 6 - 4 = 2位。再以前面第二步的"S"为例,它出现在第6位,上一次出现位置是 -1(即未出现),则整个搜索词后移 6 - (-1) = 7位。

5.

依然从尾部开始比较,"E"与"E"匹配。

6.

比较前面一位,"LE"与"LE"匹配。

7.

比较前面一位,"PLE"与"PLE"匹配。

8.

比较前面一位,"MPLE"与"MPLE"匹配。我们把这种情况称为"好后缀"(good suffix),即所有尾部匹配的字符串。注意,"MPLE"、"PLE"、"LE"、"E"都是好后缀。

9.

比较前一位,发现"I"与"A"不匹配。所以,"I"是"坏字符"。

10.

根据"坏字符规则",此时搜索词应该后移 2 - (-1)= 3 位。问题是,此时有没有更好的移法?

11.

我们知道,此时存在"好后缀"。所以,可以采用"好后缀规则"

  后移位数 = 好后缀的位置 - 搜索词中的上一次出现位置

计算时,位置的取值以"好后缀"的最后一个字符为准。如果"好后缀"在搜索词中没有重复出现,则它的上一次出现位置为 -1。

举例来说,如果字符串"ABCDAB"的后一个"AB"是"好后缀"。那么它的位置是5(从0开始计算,取最后的"B"的值),在"搜索词中的上一次出现位置"是1(第一个"B"的位置),所以后移 5 - 1 = 4位,前一个"AB"移到后一个"AB"的位置。

再举一个例子,如果字符串"ABCDEF"的"EF"是好后缀,则"EF"的位置是5 ,上一次出现的位置是 -1(即未出现),所以后移 5 - (-1) = 6位,即整个字符串移到"F"的后一位。

回到上文的这个例子。此时,所有的"好后缀"(MPLE、PLE、LE、E)之中,只有"E"在"EXAMPLE"之中出现两次,所以后移 6 - 0 = 6位。

12.

可以看到,"坏字符规则"只能移3位,"好后缀规则"可以移6位。所以,Boyer-Moore算法的基本思想是,每次后移这两个规则之中的较大值。

更巧妙的是,这两个规则的移动位数,只与搜索词有关,与原字符串无关。因此,可以预先计算生成《坏字符规则表》和《好后缀规则表》。使用时,只要查表比较一下就可以了。

13.

继续从尾部开始比较,"P"与"E"不匹配,因此"P"是"坏字符"。根据"坏字符规则",后移 6 - 4 = 2位。

14.

从尾部开始逐位比较,发现全部匹配,于是搜索结束。如果还要继续查找(即找出全部匹配),则根据"好后缀规则",后移 6 - 0 = 6位,即头部的"E"移到尾部的"E"的位置。

(完)

文档信息

2013年5月1日星期三

阮一峰的网络日志

阮一峰的网络日志


字符串匹配的KMP算法

Posted: 01 May 2013 03:46 AM PDT

字符串匹配是计算机的基本任务之一。

举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD"?

许多算法可以完成这个任务,Knuth-Morris-Pratt算法(简称KMP)是最常用的之一。它以三个发明者命名,起头的那个K就是著名科学家Donald Knuth。

这种算法不太容易理解,网上有很多解释,但读起来都很费劲。直到读到Jake Boxer的文章,我才真正理解这种算法。下面,我用自己的语言,试图写一篇比较好懂的KMP算法解释。

1.

首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较。因为B与A不匹配,所以搜索词后移一位。

2.

因为B与A不匹配,搜索词再往后移。

3.

就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。

4.

接着比较字符串和搜索词的下一个字符,还是相同。

5.

直到字符串有一个字符,与搜索词对应的字符不相同为止。

6.

这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。

7.

一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。

8.

怎么做到这一点呢?可以针对搜索词,算出一张《部分匹配表》(Partial Match Table)。这张表是如何产生的,后面再介绍,这里只要会用就可以了。

9.

已知空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。查表可知,最后一个匹配字符B对应的"部分匹配值"为2,因此按照下面的公式算出向后移动的位数:

  移动位数 = 已匹配的字符数 - 对应的部分匹配值

因为 6 - 2 等于4,所以将搜索词向后移动4位。

10.

因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2("AB"),对应的"部分匹配值"为0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移2位。

11.

因为空格与A不匹配,继续后移一位。

12.

逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动4位。

13.

逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 - 0,再将搜索词向后移动7位,这里就不再重复了。

14.

下面介绍《部分匹配表》是如何产生的。

首先,要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

15.

"部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。以"ABCDABD"为例,

  - "A"的前缀和后缀都为空集,共有元素的长度为0;

  - "AB"的前缀为[A],后缀为[B],共有元素的长度为0;

  - "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;

  - "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;

  - "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;

  - "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;

  - "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

16.

"部分匹配"的实质是,有时候,字符串内部会有重复。比如,"ABCDAB"之中有两个"AB",那么它的"部分匹配值"就是2("AB"的长度)。搜索词移动的时候,第一个"AB"向后移动4位(字符串长度-部分匹配值),就可以来到第二个"AB"的位置。

(完)

文档信息