2018年11月25日 星期日

香港保衛戰中的加拿大特遣队

多年前公司派我到溫尼伯去洽談一項軟件生意, 在一个晚宴中坐在我左边的是主方的一位经理哥顿,知道我以前是從香港來的時候, 这位六十多岁的財務長说他也想到香港去一趟。

“為什麼呢?”  我問。

“ 我伯父在二次大戰中戰死在香港,我想到他坆前致意。”

我明白了, 在日军攻打香港的时侯加拿大的確有一支由1,975名士兵组成的特遣队在香港与日军 战斗,其中有 290名士兵在战斗中丧生,264名士兵死于战俘营。

我答应他下一次我回香港的时候会到加拿大 军人墓地看一看,如果找到他伯父的坟一定会拍照用电邮寄给他。

我没有食言,在2008年回港之时真的到港岛西灣國殤紀念墳場去,那座白色的纪念碑上刻着2000多人的名字,其中228人是加拿大人。不过我并没找到哥顿伯父的名字,这可能是下葬时已经不能辨认,也可能哥顿的伯父是葬在别的地方,比如赤柱军人墳場。

下面是我拍的几张照片:








二战中当日军迫近华南的时候,英国因为自顾不暇,并没有派兵增援香港的打算。直到在1941年9月丘吉尔才改变了主意,他要向蒋介石表示英国真的要保卫香港,就打算派一两个营的兵力来作作模样,也算是安定人心。可惜當時英國在遠東連這一兩個營的兵力也没法派遣,於是丘吉尔就请加拿大出兵。
1941年10月,温尼伯榴彈炮兵团和加拿大皇家步枪队被派来香港。这个特遣队由1,975名士兵组成,其中还包括两名医务人员,两名护士,两名牙医,三名牧师。当时加拿大还未加入欧洲战场,这些加拿大人都是只受过初步训练的新兵, 而他们的对手却是训练有素,久经战火的日本38師團。

加拿大人于11月16日抵达香港。然而分配给他们由货船Don Jose运送的212辆军车就从未抵达香港。当这艘船抵达马尼拉时,美国军队获准扣下加拿大的装备来保卫菲律宾。


1941年12月8日,日本陸軍第38師團越過深圳河進攻新界。在9日晚及10日早上攻破城門碉堡及金山一带的醉酒湾防線,迫使駐港英軍在10日下午起撤回港島。雙方在維多利亞港連日隔海砲戰。

由于判断错误,特遣队一部份的兵力被调去防守港岛南岸,结果日军在18日晚上从鲤鱼门那边向港岛东面渡海进攻时只遭到少数特遣队和駐港英軍的抵抗,很快日军就攻入港島。

这时特遣队的希望就是守住黃泥涌峽,特遣队和日军在黃泥涌峽激戰連場,最終以特遣队司令部失陷、加拿大羅遜准將陣亡告終,但日本的230聯隊亦遭受嚴重傷亡。


这里值得一提的是溫尼伯榴彈兵A連的軍士長約翰·奧斯本。这位45岁、经历过一战的老兵帶領着65名榴彈兵團A連成員沿山路退到渣甸山守軍碉堡時,遭敵軍的手榴彈襲擊。奧斯本將手榴彈一一反拋回敵軍處,但有一枚卻來不及撿起。在危急之際,奧斯本大聲叫喊向其他隊員示警,然後縱身覆蓋手榴彈,不惜自我犧牲以保住其他人的性命,奧斯本死後獲追頒維多利亞十字勳章。


12月20日後,駐港英加联軍的防線逐步瓦解。日本軍隊在當日凌晨沿紫羅蘭山南下淺水灣、深水灣及壽臣山,又在港島北的聶高信山打開缺口。人數占优势的日本軍隊因為不熟地形而遭到特遣队残余部队的頑抗,尤其在淺水灣、壽臣山及紅山半島等地方。

然而駐港英軍和特遣队已經折損嚴重,逐漸被日本軍隊分割為東西兩部,再加上多次反攻失敗,促使港督楊慕琦及駐港英軍司令莫德庇少將考慮投降。不過在倫敦的命令下,駐港英軍繼續抵抗。22日至25日,英軍在黃竹坑一帶接連敗退,楊慕琦最終於25日下午決定投降。至於死守赤柱半島的英加联軍,因為通訊中斷之故,至26日凌晨才向日本軍隊投降。至此,日本成功佔據香港,香港開始了三年零八個月的苦难日子。


温尼伯榴彈炮兵团和加拿大步枪队虽然是新兵,但面对强大的敌人非但没有退缩,而是給它予严重的杀伤,值得后人尊敬。

这是一个介绍这场战役的视频(英语):




 




 




 






 

2018年9月3日 星期一

三个老头论生死

林: 以前有人把人生當作一盞油燈, 當油尽燈枯之時就是人一生的完結。現在有一個更好的比喻, 人生就像電腦內的一個程序,人的出生就相当于程序开始運作,當程序終止之时就是就是人的死亡。

汤:那样说来程序的 Abend (1)就代表人的病死或其它的意外死亡。

黄: 这个比喻是比油燈好, 人和電腦都有记亿,现在的AI程序更像人腦,可以有一定的学习能力。

林:可是以目前的技术而言AI 程序还不能有真正的自主思维。

汤:那重要吗?

黄:我觉得那很重要。如果能有自主思维,那就更接近一个生物体了。 不过那对人是福还是祸呢?

林:现在不是有人说要用这些技术来延续人死后的思维活动吗?有些亿万富豪去资助一些基因工程和AI 研究,幻想着去达到某种意义下的长生,如果AI 连自主思维也没法达到,这就不用说了。

黄: 那些掌握著社會財富的人在不同時代对追求長生就有不同的做法,以前是练丹吃藥,現在是搞 AI 和基因工程结果都会是一场空。

林:如果这些专家真的做出一个延续某人思维活动的机器,不管它是大量输入死者的资料或者索性连死者的大脑,就算它能够以死者生前的方式与你交谈或者讲述过去发生的事,你也无法证实这是死者本人的意识。

汤:我曾经问过一个搞 AI 的朋友,我问他能否输入这个观念到他那个模拟人类思维的AI程序去。 他想了好久都未能给我一个回答。

黄:你这是强人所难。你看婴孩出生时只会吃奶,哭,拉和撒,这个慨念应该是后来慢慢形成的。他用手指去摸椅子,感觉到椅子不是他的一部份而手指是他的一部份。最粗浅的概念是生物体和周围环境无数次的对比而产生的。说到“我的情感”和“我的自尊”那就更难去追查其形成的过程,我很难想像AI能重朔这些过程。

 林:我想AI 专家会避重就轻的去搞那种拟人化的程序。

 汤:现在很多所谓 Futurist 都是在混饭吃的,有人还
混进大学里面去呢。我觉得这些富豪应该多研究一下苏格拉底这句话: Death may be the greatest of all human blessings.


黄:这是下面这段话中的一句: “To fear death is nothing other than to think oneself wise when one is not. For it is to think one knows what one does not know. No one knows whether death may not even turn out to be one of the greatest blessings of human beings. And yet people fear it as if they knew for certain it is the greatest evil.”

林:我是这样去翻译这一句的:害怕死亡只是我们假装聪明之过,其实我们对死亡真的一无所知。没有人知道死亡是否会成为人类最大的祝福; 然而人们却害怕它,好像他们确实知道这是最大的恶魔。


汤:对整个人类的繁衍而言死亡是人类最大的祝福是对的,因为细胞的不停分裂,到五六十岁以后已经出现这样和那样的问题,倒不如把资源交付给新的一代。但对个人来说恋栈是难免的。

:这是你的解读,不一定是苏格拉底的原意。

:既然我们是哭着来,走的时候就体面一点,到时我会轻轻的说一句 “我走了”,不带走一片云彩。
  


******************************

1 Abend 是以前IBM 的术语 Abnormal end of task, 意思是電腦程序遇到它不能解决的问题而終止运作。

2018年8月13日 星期一

The SQL Processes - by Ling

Introduction
Most batch processes for business applications today are still implemented by procedural languages. The typical approach is to process a record (or a set of related records) at a time until all the input data is exhausted. This record orientated processing method has been with us since the dawn of data processing. It has served us well for so many years and is still the only processing method used by a lot of programmers. However, it starts to reveal its weakness at the arrival of large-scale relational databases.

Business data in a relational database is stored in tables. Data from these tables are retrieved by SQL statements and stored as variables within the program. Sometimes data from dozens of tables are required to perform a single business operation. Under that situation assembling the input data is itself a major effort, and the coding becomes more tedious as the number of input tables increases. It is not unusual for a programmer to spend more time assembling the input data than implementing the business logic.

To make the matter worse, not all the data can be retrieved at the beginning of the program. This is because some of these data retrievals are conditional upon the outcomes of certain computations at a later stage. The debugging process will also become more difficult when the number of  IF THEN ELSE conditional commands grows.

Experienced programmers have noticed that the number of variables for their input data can be substantially reduced if they do more screening and checking in the SQL SELECT statements before passing the data to the host program. In practice this means retrieving input data by a more complex SQL statement that joints several input tables. In this way SQL is in fact playing a more important role because part of the processing logic is actually handled by its WHERE clause. This is true that such an approach can cause a performance problem when the programmer doesn't have the knowledge to properly tune the SQL statement, or when the existing index structure can't efficiently support the operations performed by such a complex  SELECT.

The work table approach
We do like the idea of letting SQL do more in the process. In fact, we want to let SQL do everything. Our approach is to create one or more work tables to house all the required data, which includes data retrieved from the database and the temporary data required to carry out the computation. At the beginning of the process this work table will be populated by a series of Insert and Update SQL statements that draw data from the database. These are relatively simple SQL statements because each of them will only copy data from one or two database tables. The operation is done within the SQL environment and no procedural language commands will be involved. At the end of this step all the required data is available in these work tables.

The next step is to continue our computation on the work tables to produce all the data for the output. This is also done by Update or Insert SQL statements.

When all the output data is available the additional Insert or Update statements will be used to update the database. If necessary, the procedural language can take over the control at this stage to produce a report from this work table.

In such an approach we are not trying to reduce the number of SQL statements. On the contrary we will have more SQL statements in the program. But each SQL statement will only be responsible for a simple task and the result is visible from the work table. To debug such a program you simply execute these statements one by one and inspect the content of the work table at each step.

An example
The following example is for illustration purposes only.
Suppose budgets for a given year have been determined manually for each department and the amounts have to be distributed to all the expense accounts within the same department proportional to last year's actual expenses.

Assuming input budget data for each department has been created in a table BUDGET_INPUT, a program is required to insert into the LEDGER table the budget figures for each expense account within these departments. The work table for this program will be called BUDGET_WORK_FILE.

Tables and columns used in this process:

1. BUDGET_INPUT table - the table that contains the budget figure for each department.

BUDGET_INPUT (year, department, record_status, department_budget)

2. LEDGER table - the database table for the account balance by year, department, and ledger_type (Actual or Budget)

LEDGER (account, department, year, ledger_type, amount)

3. ACCOUNT_TYPE table

ACCOUNT_TYPE (account, account_type)

4. BUDGET_WORK_FILE table - Temporary work table created for this process

BUDGET_WORK_FILE (account, department, budget_amount, last_year_actual_amt, last_year_department_total, process_step)


**** The Program ****

/* Statement 1: To delete the old data in the work file */

delete from budget_work_file;

/* Statement 2: To copy to work table the actual expense from last year by department and account */

insert into budget_work_file
(account, department, last_year_actual_amt, process_step)
select a.account, a.department, sum(a.amount), 'LAST_YR_ACTUAL'
from account_type b, ledger a
where a.account = b.account and
         b.account_type = 'EXPENSE' and
         a.ledger_type = 'ACTUAL' and
         a.year = 2011 - 1
group by a.account, a.department;

/* Remark: We use 2011 - 1 instead of 2010 because the year entry can be replaced by a bind variable later. */

/* Statement 3: To calculate last year's total expense by department */

update budget_work_file a
set last_year_department_total
= (select sum(last_year_actual_amt)
     from budget_work_file b
     where b.department = a.department and
                          b.process_step = 'LAST_YR_ACTUAL')
where a.process_step = 'LAST_YR_ACTUAL';

/* Statement 4: To distribute the budget figures to each department based on last year's expense */

insert into budget_work_file
(account, department, budget_amount, process_step)
select a.account, a.department,
b.department_budget * a.last_year_actual_amt / a.last_year_department_total,
'BUDGET_OUTPUT'
from budget_input b, budget_work_file a
where b.department = a.department and
          a.process_step = 'LAST_YR_ACTUAL' and
          b.record_status = 'APPROVED' and
          b.year = 2011 and
          a.last_year_department_total <> 0;

/* Remark: We assume that there is only one row in budget_input per department for the same year */

/* Statement 5: To copy the budget by account and department to the LEDGER table */

insert into LEDGER
(account, department, year, ledger_type, amount)
select account, department, 2011, 'BUDGET', budget_amount
from budget_work_file
where process_step = 'BUDGET_OUTPUT';

Please note that statement 4 and 5 can be combined to insert budget figures into LEDGER directly from the two source tables in statement 4. But keeping an audit trail for the output data in the work table is not a bad thing.

The main difference between this approach and the procedural approach is that each INSERT or UPDATE operation will generate or update so many rows in the table, whereas the procedural in general can only update one row at a time.

The procedural language can still have a minor role in this approach. For example, these SQL statements can be embedded in a procedural language program so that the year entry (2011) is replaced by a bind variable. The value of this bind variable will come from the input parameters.

We have to emphasize that the example is constructed solely for demonstration. It does not take into account all the details. For example, there could be new accounts set up in 2011 that were not in the 2010 ledger

The Control Column
You may have already noticed that the column PROCESS_STEP plays an important role to identify the set of data to be processed. This is what we call the Control Column. This control column should have a distinct value for each Insert statement. It should also be reassigned a new value in any important Update statement to identify those rows that have actually participated in the update.
The work table should have sufficient columns to support all the computations. Since it will only be used for the process itself, we should be allowed to do anything necessary to provide maximum efficiency for the process, including building the suitable indexes to support our operation. For example, statement 3 will need an index on the DEPARTMENT column. We may even want to break the rule of normalization and keep some redundant data if that can make the program run faster.

Some programmers may like to delete all the rows of the work table at the end of the process, others would prefer to keep the work table data as an audit trail until the next run.

Concurrent processing
Some applications may have to allow two or more sessions of the same program to be run at the same time. In that case additional effort is required to structure our work table. Normally the business requirement will give us hints to avoid potential contention. For example, if the process is to update the LEGDER with a batch of journal entries identified by the batch number, then two different sessions can work simultaneously if all the operations in the program are applied to work table data with its own batch number.
If you can not find a key item such as the batch number to "partition" your work table, there is always a way to generate a unique number (e.g. a session counter) for your session to serve that purpose. How to support concurrent runs in a batch process is always an issue no matter what approach you take. But in this approach the issue is more noticeable.

In the more recent versions of  Oracle DBMS  you can create temporary tables which are accessible only within your own session. That could be a good vehicle for your work tables.

For a complicated batch process you may need more than one work table. Suppose we are writing a payroll program that calculates the incomes, benefits, deductions, taxes, and net pay for each employee. We may need to have a "pointer" work table to identify all the employees that will be included in this run. We may have another work table to house the related data for their incomes, benefits, and deductions. If the salary data such as hourly rates are not stored in a suitable format for our SQL operation, we may  need another work file to store the related hourly rates in exactly the format we want. The execution time to populate such a temporary hourly rate table is negligible in comparison to the benefit it provides.

When to Commit
The next issue that we want to discuss is when to apply the database commit. In the example above we update the database table LEDGER once at the end of the process. If there is any internal or external condition that causes the program to terminate before that step we can always restart from the beginning because no permanent database table would have  been updated. Therefore abnormal termination of this process will not cause problems to the database.

However, if there are two updates on two permanent tables in the program then we should try not to Commit the database transaction between these two update statements. Otherwise when the system abnormally terminates between these two steps the integrity of the database will be violated. If you can you should place all the database table update statements at the end of the program and place no Commit between them.

We understand that there are situations where we need to Commit database transactions between two database updates. In that case sufficient data should be captured in your work table to make these updates reversible.

When to place your database commit is also an issue for the other approaches. But it is more important in the work table approach because each update or insert statement could change a lot of data in that table. If possible, we should keep enough data to make the entire process reversible.

Beyond batch processes
In this article we use the term Batch Process casually. In fact, this work table approach is not restricted to Batch Processes in the technical sense. In my previous job an on-line program which gathers data from 7 tables in an Asset Management System to produce transactions for mass transfer of assets was successfully implemented by this work table approach.  

The strength
In this approach overhead is reduced to an absolute minimum. Everything you need to know for a statement can normally be displayed on the same page of your screen. There is no need for you to go elsewhere in the program to do cross referencing. The highly independent nature of the SQL statement and its compact format not only simplifies the debugging process, but also makes program modification a much easier task.

A friend of mine said the predicate logic in the WHERE clause of a SQL statement is one step closer to human thinking as compared with the If-Then-Else types of logic. I totally agree after I implemented a large batch program in a project costing system. I remember my user gave me the following list of instructions:

  1. Identify all the capital projects

  2. Copy the total outstanding amounts in the transaction file for these projects by account, department,
      and cost type (labor or material)
      Remark: There is no account code in the transaction file for labor cost type.

   3. For each project and department prorate the total labor cost by material accounts.

   4. .......

I was amazed to find out later that after setting up the work table I was able to translate her instructions into SQL statements one by one.



The weakness
The work table approach can not handle any recursive formula or recursive process directly. Recursive formula is normally implemented by iteration commands in a procedural language. However, we can still use the work table method inside the procedural loop if that is beneficial.

We also noticed that the work table approach demands an above average skills in SQL programming. Sometimes knowledge beyond the core SQL will come in handy to solve sophisticated problems. For example, we often use functions like DECODE, TO_CHAR, TO_DATE, ADD_MONTHS in Oracle SQL. Programmers that are not well versed in SQL programming language may not be able to take full advantage of this approach.

Conclusion
What we are talking about here is not a new approach. A lot of programs have been written in similar fashions. Sometimes the term Set Processing is used for this kind of program. But I haven't seen any systematic discussion on the work tables. Actually most of the Set Processing programs are doing direct Update and Insert on the permanent tables. Without using work tables and the control columns the method will suffer from a severe handicap.
I have been using the work table approach for many years and I have reduced my development cost and maintenance cost substantially. I will not go back to the procedure approach.

2018年8月6日 星期一

關達那摩的姑娘

相信你也听过 Guantanamera 这首歌,一直以来我只把它当做南美洲的民歌。后来有人告诉我 Guantanamera 是来自Guantanamo 的意思。提起 Guantanamo 人们就会想到美國在古巴那个臭名昭彰的監獄。其实那个地方在美國基地以外还是古巴的领土。


1898年美西戰爭接近尾聲時美國從西班牙手中奪走了古巴,並在關塔那摩灣建立了海軍基地, 这就是现在的  Guantanamo 监狱。 古巴独立后美国從古巴第一任總統帕爾馬手中獲得一份永久租借那基地的契约。可是卡斯特羅上臺後就拒絕去兑现美国的租金支票,他認為這個基地是非法的。


这里要谈的 Guantanamera 是一首带着謙卑和浪漫的, 充满對苦難人們同情歌曲。 歌詞中的 "Guajira " 有好幾個意思, "Guajira" 是位於哥倫比亞的一個地方, "Una guajira" 可以指一個女孩。 这里 "Guajira Guantanamera" 就翻译成“來自關達那摩的姑娘”。


此曲由古巴鄉土音樂家Joseíto Fernández編寫,歌詞取自一位古巴的獨立英雄、自由主義詩人 José Marti 的遺作 "Versos Sencillos" , 後來更演變成為古巴的愛國歌曲。下面 video 显示的就是Joseíto Fernández 的头像。


Guantanamera  -- 來自關達那摩的姑娘





Guantanamera, guajira Guantanamera
Guantanamera, guajira Guantanamera
Yo soy un hombre sincero
De donde crece la palma
Y antes de morirme quiero
Echar mis versos del alma


來自關達那摩的姑娘
來自關達那摩的姑娘
我是個樸實的好人
來自棕櫚樹生長之地
在我死亡之前
我要唱出靈魂裡的詩歌


Guantanamera, guajira Guantanamera
Guantanamera, guajira Guantanamera
Mi verso es de un verde claro
Y de un carmi­n encendido
Mi verso es un ciervo herido
Que busca en el monte amparo


來自關達那摩的姑娘
來自關達那摩的姑娘
我的詩歌帶著憂愁
也帶著炙熱
我的詩就像一隻受傷的小鹿
在森林裡尋求庇護



Guantanamera, guajira Guantanamera
Guantanamera, guajira Guantanamera
Con los pobres de la tierra
Quiero yo mi suerte echar
El arroyo de la sierra
Me complace mas que el mar


來自關達那摩的姑娘
來自關達那摩的姑娘
世上的窮苦人呀
我愿與他們共命運
山之中的小溪
大海更能撫慰我心

2018年7月19日 星期四

路易斯·阿姆斯特朗 (Louis Armstrong)


1901年8月4日阿姆斯特朗生于路易斯安那州新奥尔良市,当他还是个婴儿的时候父亲就抛弃了家庭,他在贫民窟度过他的童年。

生活在贫民窟中的阿姆斯特朗从小就喜欢唱歌,但是贫穷使他没有学习正规声乐的机会,他有时参加少年合唱队在街头演唱,挣些零花钱,有时会在教堂唱诗班中一展歌喉,唱歌是他童年最快乐的一件事情。

一年除夕之夜,十岁的阿姆斯特朗从小伙伴借来一把手枪,那不是玩具,而是一把真枪。好奇的阿姆斯特朗感到前所未有的兴奋,他朝天鸣枪,希望以此送走过去一年的不幸,迎接新的一年的到来。然而在寂静的夜晚枪声显得格外的响亮,不但吓坏了周围的小伙伴,而且惊动了警察。警察注意到了阿姆斯特朗的肤色,他们不由分说逮捕了阿姆斯特朗,并以“非法持有枪支”的罪名将他送进了感化院。


感化院内有音乐训练班和乐队, 阿姆斯特朗由於他出色的表演成为了乐队领队。这乐队在新奥尔良演出时阿姆斯特朗的小号演奏吸引了人们的注意,这就开始了他的音乐生涯。十四岁时他被允许回家。


离开感化院后姆斯特朗加入了一支乐队,该乐队中有当时著名的小号演奏家 Joe Oliver。Oliver从一开始就非常欣赏阿姆斯特朗的才华,后来他请阿姆斯特朗加入他在芝加哥的乐队。在乐队的录音室里录音师不得不将他的和其它乐手分开十多米,因为他的音色音量实在太惊人了,即便Oliver也无法盖住他的声音。

1925年,阿姆斯特朗已被认为是世界上最伟大的小号演奏家之一。1931年,他带领他的乐团远赴英国和欧洲,在那里他们的演出取得了巨大的成功。奠定了他世界上最出色爵士乐演奏家的地位。

在1967年阿姆斯特朗录制了这首 What A Wonderful World。就算今天,在美国和加拿大婚宴上还常听到他唱这首歌的沙哑声调,做父亲的往往要带着将出嫁的女儿在Wonderful World 的乐声中走进舞池。




路易斯·阿姆斯特朗於 1971年7月6日 逝世,现在新奥尔良有他的纪念公园, 在纽约有纪念他的博物馆。

2018年6月29日 星期五

绑架了几代青年的武侠小说

谢天谢地,武侠小说的浪潮总算过去了,就算武侠电影的声势也大不如前了。为什么这些如此虚假的情节,这些从来没有发生过的,来也不会发生的“故事”会吸引这么多人呢?说起来就话长。

如果你问我有没有看过武侠小说?有!上课时偷偷的低着头看, 回家后躺在床上看,我的近视就是那样练成的。至于我在什么时候为了什么原因不再看武侠小说,我下面会交代。

所谓新派武侠小说据说是在上世纪五十年代开始的。当时香港报纸左右派壁垒分明,左派的商报,大公报,新晚报有相当多读者,替这些报纸副刊写连载小说的都是文笔很好的作家。可是从五十年代中期开始,不管你才高八斗,现实题材的小说是很难写了,很容易踏进“雷区”。其中有一两个脑筋转得快的就开始写起这些从来没有发生过的,将来也不会发生的“故事”,新派武侠小说就这样诞生了。

第一部新派武侠小说应该是梁羽生的《龙虎斗京华》。这部武侠小说还摆脱不了旧武侠小说框架,也过份卖弄了年青人不太感冒的诗词。对太极功夫的描述也过度“借鉴”了1937年白羽的《十二金钱镖》。

梁后来的《七剑下天山》就走出旧派武侠小说的影子了。不过有人质疑《七剑》是抄英国小说《牛虻》的情节。网上的一些资料指出当年梁在回答读者“柳青”的来信时也说《七剑》的主角凌未风就是《牛虻》里的主角牛虻的化身。

另一位起步晚一点的是金庸,据说他的《射雕英雄传》是在商报连载的。金没有像梁那样去卖弄诗词,一开始就远离了旧派武侠
小说的式样, 情节也很紧凑,得到大量读者的追捧。

当两位大师的大作分别在报纸上连载时
,人们发现大师之间也有“借鉴”的现象了。当《射雕英雄传》里的郭靖,黄蓉在密室里运功疗伤后,梁的小说也有相似的情节。

我开始对武侠小说失去兴趣是我学物理以后的事。
   
梁羽生在《云海玉弓缘》里有这样的一段:

“忽见冯琳右脚在左脚脚背一踏,突然间身形又凭空拔起三丈,这样三起三落,终于是赞密法师先落到地面,冯琳这才跟着脚尖沽地,登时掌声雷动。”

那是不是牛顿力学第三定律的反例呢?

在另一本武侠小说
冰川天女传他写道:

“原来提摩达多用的乃是“阴阳五行掌力”,是观察天体星辰的运行法则,从“万有引力”中所参悟出来的一门奇功。要知用任何一种力量打击对方,有正作用必有反作用,提摩达多练到两股掌力互相激撞,再与敌人所发的力量汇合,敌人的力量就反而为我所用,和几股浪潮相碰之时,卷起漩涡的道理,正好相同。”

我记不起提摩达是什么时代的人,怎样会知道“万有引力”。在地球上的万有引力就是重力,提摩达能动用的万有引力也只有他个人的体重,他这一门“奇功”是每一个玩摔跤的孩子都懂的。 梁所讲的 “有正作用必有反作用”正是冯琳不能右脚踏左脚凭空拔起三丈的原因。

别的我不管,物理是不能胡扯的。既然不懂的也乱说乱抄,难保别的东西也是用这种态度处理。于是我对武侠小说的兴趣就大减了。

当时武侠小说的风行是有理由的。

首先是这些作者的文笔好,故事里面有琴棋书画,诗词歌赋,武功秘岌,历史故事。经过曲折离奇的情节我们出身卑微主人翁(也有例外的)终於练成绝世武功。除了消灭邪魔伸张正义外还(此点至关重要)得到多位风华绝代的美女所垂青。

旧派武侠小说描写爱情是相当土的,“小鸳鸯”的
山盟海誓是那样千遍一律。而这些新派武侠小说却借鉴了外国小说对爱情的描绘,对心理的变化都着意的落笔。这一切,经过代入程序后会使千万个光棍读者非常受用。

另一方面,当时年轻人也乐意看到一些中国人比洋人优胜的东西,武侠小说作者吹捧下的中国功夫恰巧填补了这个空缺。不过,李小龙的电影在这一方面更为成功。

武侠小说有什么不妥呢?

武侠小说大行其道之时是什么时代?是世界电子工业起飞的时代。当乔布斯和他的搭档在他父亲的车房里研制第一代萍果电脑的时候,我们这一辈正躺在床上看《射雕英雄传》和《七剑下天山》。

武侠小说除了占据了年轻人太多的时间,还把以往中国社会里的一些封建思想和恶习不知不觉的从上一辈传递下去。

第一是中国人还不太敏感的大男子主义。 你看武侠小说里的主角百分之九十都是男的,都有众多美女围绕, 那是多么天经地义呀。这些美女是为主角而存在的,留给主角挑的。

第二是走捷径的意愿,想得到特殊际遇突然练成绝世武功。这种看不起按部就班而要速成的想法现在也很普遍,也做成很多悲剧。不过这不一定都是受武侠小说影响的,武侠小说是加深了人们这种意愿。

第三是有意无意地鼓励寻仇。有仇必报,不知宽恕为何物。

第四是在延续旧社会崇拜权威漠视普通人的恶习。你看当上教主,掌门人多么风光,有权有势,对“下人”可以随便呼喝。作为喽罗和兵卒的一点个人尊严也没有。对,这可能是合乎当时的历史情况,但既然你描写儿女私情可以来得这么现代,为何不可让人想到普通人也应该有其尊严呢?就算间接提到这问题也不错呀! 作为影响力这么大的通俗小说,这些作者本来是有机会去注入一些积极元素,可惜他们没有这样做。

武侠小说这一些东西后来
网上的“架空小说”再发扬光大,不过这里没时间再谈了。

2018年6月18日 星期一

中文里的日語詞匯

很久以来我有这样一个疑问:为什么汉语词汇在晚清后会有这样大的变化?在明未清初以前像“民主”、 “科学”、“杂志”、“防疫” 、“否定”、“假设”、“感性”、“主观”、“客观”、“进化” 、“象征”、“艺术”、“自由” 和 “民权” 这样的词语是没有人会懂的。

很容易看出来这些词语和现代生活有关,特别是和源出于西方的现代生活有关。西方文化对东方诸国影响这么大,以至后者都要扩充自己的词汇来适应现代生活。有拼音字母的国家往往就干脆用音译的方法来创造新词。日本也曾经这样做,后来他们发现用两个汉字凑拼起来的词句更容易为大众接受。所以就有了以上这些新词语。说到这里我们要提起“东洋卢梭”中江兆民。中江兆民用汉文译出了卢梭的“社会契约论” (当时他用“民约译解”这名称)。很多日本汉字新词,包括上面所说的“象征”、“艺术”、“自由”和“民权”等都出自他手。

当时中国的文人却分为两派,有的还主张用音译,所以就有了“德律风” (telephone) 、“赛因斯” (science) 和“德谟克拉西” (democracy)。由严复为代表的另一批人却主张从传统文化中找出相当的词句,于是就有了“计学” (economy)、“天演” (evolution) 和 “庶建”(democracy)。

不过后来大家都把日本人创造的新词搬过来用。这里当然有留日学生的推动,但两个汉字组成的新词简单又传神也是不争的事实。日本人说中国人常用的3000个单词中,就有1000个是从日本搬过来的。 他们说以下这些也是现代汉语中的日语借词:

“劳动”、“服务”、“组织”、“纪律”、“政治”、“革命”、“物理”、“宇宙 ”、“化学”、“代数”、 “政府”、“政党”、“方针”、“政策”、“申请”、“解释”、“理论”、“哲学”、“原则”、“经济”、“商业”、“干部”、“健康”、“法律”、“封建”、“共和”、“美学”、“文学”、“美术”、“抽象”、“取缔”、“取消”、“引渡”、“手续”、“积极”、“消极”、“具体”、“目的”、“宗旨”、“权利”、“义务”、必要”、“律师”、“代价”、“亲属”、“继承”、“债权人”、“债务人”、“原素”、“要素”、“偶素”、“常素”、“条件”、“契约”、“卫生”、“文凭”、“盲从”、“同化”、“代表”、“压力”、“排外”、“野蛮”、“公敌”、“派出所”、“警察”、“宪兵”、“检察官”。

我不同意“革命”和“宇宙 ”是日本人发明的说法。 南朝梁(502年─549年)时代的《千字文》一开头就是“天地玄黄,宇宙洪荒”。《淮南子》里说“上下四方叫作宇,古往今来 叫作宙”。所以"宇宙”并不是日本人创造的新词。

另一个有争议的是“革命”。《周易·革卦·彖传》中已经有:“汤武革命,顺乎天而应乎人” 的说法。

我想当年的日本学者也从中国古书里找他们想表达的词语。像“宇宙 ” 和“革命”这两个单词他们的功劳只在于将其推广、普及而已。

“代数”这名称也不见得是日本首创。1859年中国数学家李善兰首次把 “algebra” 译成“代数”。后来清代学者华蘅芳和英国人傅兰雅合译英国瓦里斯的《代数学》,卷首就有 “代数之法,无论何数,皆可以任何记号代之” 的说法,说明了所谓“代数”,就是用符号来代表数的一种方法。

来华传教士韦廉臣 (Rev.Alexander Williamson) 在1856年在华编写的《格物探原》 一书就用了“化学”一词。后来这一词流传到日本,取代了日本人从德文 Chemie 音译过来的“舍密”。

可见当时中日两国在接受西方文化的造词过程中有一定的互动,并不是从日本输往中国的单线行动。当然,我们可以肯定现代汉语中有很多来自日本的借词。至于有没有达到三份一就不知道了。

木心语录


木心是谁?

如果你已经知道,我就不该多嘴。如果你还不知道,我更加不该多嘴。因为在网上寻找他,看他的文章,听他的故事,就是一种乐趣。

* * * * * * * *


岁月不饶人,我亦未曾饶过岁月。


所谓无底深渊,下去,也是前程万里。


 我追索人心的深度 却看到了人心的浅薄。


天才是被另一个天才发现的。 


给他们面子是我自己要面子 。


从前的那个我 如果来找现在的我 会得到很好的款待 


 傲慢是天生的,谦虚只在人工。


人自有了镜子才慢慢象样子起来。


最佳景观:难得有一位渺小的伟人,在肮脏的世界上,干净的活了几十年。


看清世界荒谬,是一个智者的基本水准。看清了,不是感到恶心,而是会心一笑。


才能,心肠,头脑,缺一不可,三者难平均;也好,也就此滋生风格。



一流的情人永远不必殉陨,永远不会失恋,因为“我爱你,与你何涉。”


 不谦而狂的人,狂不到哪里去;不狂而谦的人,真不知其在谦什么。



宇宙观决定世界观,世界观决定人生观,人生观决定艺术观、政治观、爱情观。


从前的日色变得慢。车,马,邮件都慢。一生只够爱一个人。


文字的简练来自内心的真诚。我十二万分的爱你,就不如我爱你。


做生活的导演,不成。次之,做演员。再次之,做观众。


一个爱我的人,如果爱得讲话结结巴巴,语无伦次,我就知道他爱我。


迷路于大道上的人嗤笑迷路于小径上的人,后者可怜,前者可怜且可耻。


人害怕寂寞,害怕到无耻的程度。换言之,人的某些无耻行径是由于害怕寂寞而做出来的。


各有各的音,各有各的知音。甲与乙斗,丙支持甲,丁支持乙。后来甲乙议和,第一条款:诛丙、丁。


艺术本来也只是一个梦,不过比权势的梦、财富的梦、情欲的梦,更美一些,更持久一些,艺术,是个最好的梦。


爱情,亦三种境界耳。少年出乎好奇,青年在于审美,中年归向求知。老之将至,义无反顾。


康德的判断:“对自然美抱有直接兴趣,永远是心地善良的标志。”此话可以反说,凡已不复善良者,乃对自然美丧失了直接的兴趣。


往过去看,一代比一代多情,往未来看,一代比一代无情。多情可以多到没际涯,无情则有限,无情而已。


很多人的失落,是违背了自己少年时的立志。自认为成熟,自认为练达,自认为精明,从前多幼稚,总算看透了,想穿了。于是,我们就此变成自己年少时最憎恶的那种人。













2018年6月16日 星期六

SHA256

如果你将下面这句话

陳女士欠鄧先生100元,立此為憑。

原封不动的抄下来(或者cut下来),然后 paste 到这个网站


你就会得到这一串符号:

B6FEB7D6C48B06A7D8A5F153998A1326FF4351891898EA853505037BA2422021

这是什么东东呢? 稍后再讲。

只要你改了一丁点,比如说鄧先生 变成 鄧生, 那整个output 都不同了:

陳女士欠鄧生100元,立此為憑。

7FBA4E1C06D88B373DB7C3B0323398C67603F4BA954E31005CBE17BAEC8B0E69

就算你输入两万言的一篇文章, 你也会得到同一长度的一串符号, 而且只要你改一个字,output 就会不同。此外,你无从去创造另一个 input 去产生同一个 output。

这个过程所得的这一串符号就叫 散列值, 英文叫 hash value. 那个SHA256 就是一个 散列函数 hash function。

 这一串符号实际上是一个很大的数字,因为太长所以用十六进来表示,所以除0到9外会有A 到 F。A就是十, B就是十一F就是十五,而10就是十六。

散列函数有什么用呢? 你上网去你的银行处理业务,所用的 password 并不会储存在 银行 的硬碟里,因为银行也怕员工或黑客偷看, 银行只会存你 password 的散列值。 当你上网登录时银行就把你 输入 的paasword 同样再算一次来比较两个 散列值 是否一样。这样银行 IT 员工或黑客偷看了这散列值也没用,他不可能倒过来去找到你的password。

当然创造这个散列函数的机构要有很高的声誉才能作这样的用途,否则当事人会宁愿自己创造一个。SHA256 只是众多常用的散列函数其中一个, 不过它来头不小,它是NSA ( 美国国家安全局 )所创 立的, 比特币的机制也用到它。

另一个应用:比如说A君有一个很重要很长的文件要发给B 君, 那A可以先把 文件 的 散列值 先发给 B,B 收到 文件后再算一次散列值去比较来保证 文件在传送中的完整和 没有被人改动过。

以前有钱人的遗嘱是交給律师保管,现在他们可以把 遗嘱 的 散列值 先告诉給他的儿女。日后他们只要把收到 遗嘱再算一次 散列值 来比较就可确定真伪。

散列函数还有其它的应用,不过那大部份都是在 IT 和密码学的范围。