关于作者

用户名:zhafn
笔名:zhafn
地区:
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言



朋友的BLOG

原码站

访问统计:
文章个数:303
评论个数:62
留言条数:17




Powered by BlogDriver 2.1

水木尤寒的博客

 

欢迎访问水木尤寒的博客

文章

大队培训

      我是一名返培员工,常常从同事和朋友们处得到华为大队培训的一些消息,有些很真实,有些很夸张。“华为企业文化培训”,她到底是什么样的培训?我常常自问却总不得而知,同时心中常常在想,自己一天没有经历过公司的企业文化培训,一天还算不上一名“真正意义”上的公司员工,尽管我是直接转正的。
      直到有一天清晨,嘹亮的集合哨声,整齐划一的踏步声,震耳欲聋的“1,2,3,4”声,终于拉开了我的大队培训生活。虽然曾经在几家公司工作过,但现在的感觉却如同又一次回到久别的校园,如同再一次重新走向社会。
      培训的每一天都让我印像深刻,都让我有所领悟。第一天的跑操更是让我记忆深刻,由于最近几年都忙于工作,锻炼的时间也是越来越少,身体也不像校园时期的我了,开始有些不听话了,第一天的跑操居然就晕倒了一次,当时的感觉真的就是眼前一黑,也许是早上没喝水的原因,而且出汗又太多了,我连我向哪个方向倒下去的都不知道,只知道这时候旁边好像有很多同学都跑了过来,赶快将我扶起来,教官也跑过来给我掐“人中”。说实话,当时心中很感动,身边的同学都是刚认识的,彼此的印像还只是一个名字,犹如一个符号,但是我突然有种很强烈的“家”的感觉。耳朵里朦胧听到教官在问:“感觉怎么样,感觉怎么样?”,当时他的语气很温和。不!应该是很温暖,一点也没有印像中那个严厉教官的影子(其实后来发现,他也很幽默)。从那时开始,我暗暗决定,应该和班上的学员更多的去沟通,因为我们是一个家,我们就是兄弟姐妹。从那时候开始,我暗暗决心,明天的跑操再不能掉队了,要努力,要坚持,要坚强,要做烧不死的凤凰鸟。因为我在华为不是孤单的,我有兄弟,我有姐妹,因为我在华为有个家。
      四天的文化培训,四天的户外活动,感受最深的就是团队协作,其实只要一声呐喊,一声加油,那就是团队协作。很羡慕2组的同学,每次都能拿第一,每次都有苹果吃,羡慕的同时我们也要向他们学习,真的就像我们在讨论发表中的歌一样,“一组的创意多,二组的力气大,三组的声音响,四组的爱学习”。
      其实我们4组也不差,拿了3次第2。还记得最后一次户外活动,边打篮球边放乒乓球,边打篮球边拾乒乓球,我在拾乒乓球的那队,所有女生都在这队,我们的战略是对面的兄弟有失误,就由男生来跑步,没有失误,就由女生来完成。还记得对面有个兄弟失误了6次,让我们前面的兄弟跑了6个来回,尽管6个来回很累,气喘吁吁,但是他的脸上却洋溢着一种笑容,那是种团结互助的笑容,这种笑容让我感到他是无怨无悔的,那种笑容给我很深的触动,也许我会记住一辈子。不!我应该记住一辈子,他是我人生中收获的唯数不多的有意义的几张笑容,那已经不在只是一张笑脸,那是种暗示,那是种鼓励,是我年老后值得回忆的宝贵财富。是的,我会记住一辈子的!在这里我听不到怨言,听到的只有鼓励,感受着随处可见的鼓励。谁能不需要鼓励呢?多给我们周围的同事一些鼓励吧!如同前面的兄弟一样,对面的兄弟又失误了,不过没关系,该我了!该我来接过篮球了!该我来跑了,兄弟!就交给我吧!一个来回,两个来回,三个来回,兄弟,放心吧,你的失误我们来扛。三个来回下来,是的,的确很累,真的很累,但是我的心好像在喊:“兄弟,我不累!”,我望着我身后的兄弟,我想我那时是笑了,我想那张笑脸不是我大脑神经发出的指令,也许是我的心在喊:“兄弟,我不累!”。不!那的确是我的心在喊:“兄弟,我不累!”。胜者举杯相庆,败这拼死相救。也许我此刻还不能有更深的体会,但是我却收获了一张笑脸,一张对我一生都有着积极意义的笑脸,我同样也输出了一张同样有意义的笑脸,也许我的那张笑容也会让他记住一辈子,是他人生中收获的唯数不多的有意义的几张笑容,是他年老后值得回忆的宝贵财富。或许多年后,他早已不记得我是谁,不记得我这张笑容,但这种文化早已渗入到你我的血脉,你我都已是这文化浪潮中的一滴水,这种“文化”贵如油,润物却细无声。
      四天的文化培训课,每天的课程都很精彩。《服从组织规则》、《团结合作与集体奋斗》、《责任心与敬业精神》、《诚实守信》、《自我批判与不断进步》、《以客户为中心》每门课都给我人生一些新的启示。以前我是一名到华为来的外包员工,在华为办公有快一年的时间了,无时无刻都感受着华为企业文化。但是这些课程又给了我对华为文化更清晰认识学习的会。
      在上《以客户为中心》这门课时,让我想起了很多。印像最深的就是春节前的那段经历。记得当时我们做的是全国春晚12530的彩铃项目,负责全国19个省的12530彩铃专区开发,保证春节期间全国的移动彩铃用户都能下载到春晚上出现的歌曲作为彩铃,这个项目很紧,我们连元旦都在加班做这个项目。记得当时已经到了补丁上线时期,每天晚上两三点钟都能接到全国各地现场维护工程师的电话,向我在一次确认补丁上线的全过程,将整个上线过程复述一遍(其实当时补丁文档中已经写的非常清楚了),但是正是这种责任心让我现在回想起来非常感动。现场维护工程师在进行补丁升级的时候都是在晚上12点以后,牺牲的都是自己的休息时间,但他们没有一点怠慢,放松,懈怠。19省的现场维护工程师的行动是如此的整齐划一,就像我们早操中的踏步声一样整齐划一。他们每一次的复述升级过程,就像我们早操中的“1,2,3,4”声。当时的感觉是他们很敬业,很有责任感,是工作狂,不过现在回想起来,正是那种以客户为中心的思想,辅以他们的敬业精神,责任心才能保证春晚项目的顺利上马。如果当时有一个省出先问题,那这个项目就可以算是失败了,但是没有。这么大面积的上线工作,如果没有这种文化,这种精神,我想是很难做到的。昨天,也许我只知道这是敬业,是责任感,还不知道这就是以客户为中心,但是,今天,我想说,我真的理解了,其实客户满意就是我们的生命,这不是唱高调,喊口号,这是很深刻很实际的一句话,因为客户就是我们的衣食父母啊。其实看看整个社会,我们在为别人服务的同时,别人也在为我们服务啊,那我们为什么不为别人更好的服务呢?其实那同时也是为我们自己更好的服务啊!
      这是我的培训心得,但它不仅仅是我的培训心得,更是我的一段心路历程,一段真实的心路历程,一段在我人生中有意义的心路历程,即使培训期间没有这个要求我也会自己写一篇心灵感悟发布在我的博客上作为人生的一份珍藏,工作累时拿出来读读,取得成绩时拿出来读读,生活平淡时拿出来读读。不光我自己读,我还要读给我的父母听,让他们分享我的成长快乐,我还要读给我的未来老婆听,我有义务与她分享我的成长喜悦,我还要读给我的后代听,我有责任指引他们找到人生的方向。我很庆幸我来到了华为,找到了志同道合的同事,找到了我事业上的家。

- 作者: zhafn 2006年10月29日, 星期日 13:14  回复(1) |  引用(1) 加入博采

J2SE中 QUEUES & 延时处理
J2SE5.0为我们在Collections Framework里全新添加了一个top-level的queues接口, 这样几种数据构造的基础类型(其它类型比如Map, List)更加完善的出现在api中(不用自己写到头疼喽!).
Queues是很典型的先进先出的数据结构(也有例外,比如带有优先权处理特点的priority queues, 组成元素不一定在queue的尾端添加), 所谓先进先出, 比如银行柜台工作人员接待排队的顾客, 工作人员依次为排头第一个顾客服务, 服务完毕, 该顾客离开. 新来的顾客在排尾排队.
添加了Queue的同时, J2SE5.0 也增添了其他的queues处理方案. 其中比较典型的一个就是DelayQueue. 在DelayQueue中, 组成元素要等到Delay(延时)发生过才会被处理.
Queue interface继承了Collection, 同时它拥有自己的5个办法,
E element()
boolean offer(E o)
E peek()
E poll()
E remove()


(说明一下:由于J2SE5.0对generics(通用型)的支持, E类可以是任何数据类型.) 因此我们还可以使用Collection interface里的办法对queue里面的数据元素进行操作,比如add()添加一个新元素,但是Collection里的add()和上面的offer()是不同的:当超过queue限定的大小,再用add()添加必然会导致add的失败,同时产生Exception;而offer()会返回一个false,这样可以降低不必要的(比如未经核查runtime Exception)Exception控制。

Queue的其它4个办法:remove()和poll(),都是清除第一个元素。不同点是当清除一个已经是空的Queue的head元素时候,remove()会throw Exception,而poll()会返回一个null; element()和peek()都是查看当前head(queue的第一个)元素。当查看一个空的queue的head的时候,element()会throw Exception,而peek()会返回null。 因为理论上queue可以是空,而且实际操作的过程中很多情况要使用的空的queue,大多数情况下没有必要为了一个空的queue而产生太多的Exception, 所以poll()和peek()是经常使用的。

下面是一个例子:--来自sun技术论坛 class Producer implements Runnable {
private final Queue queue;
Producer(Queue q) { queue = q; }
public void run() {
try {
while(true) { queue.offer(produce()); }
} catch (InterruptedException ex) { ... handle ...}
}
Object produce() { ... }
}
class Consumer implements Runnable {
private final Queue queue;
Consumer(Queue q) { queue = q; }
public void run() {
try {
Object o;
while((o = queue.poll()) != null) { consume(o); }
} catch (InterruptedException ex) { ... handle ...}
}
void consume(Object x) { ... }
}

现在产生了另一个问题,如何处置添加一个元素到已经达到了限制大小的queue和从一个是空的queue中清除head元素。事实上我们可以直接使用BlockingQueue中的put()和take()办法(名字和stack如此之像)来添加和清除元素,这两个办法会调用线程来在适当的条件下(比如queue已经达到最大size或者已经清空)阻止办法的运行。
下面是一个BlockingQueue的例子: -- 来自sun技术论坛

class Producer implements Runnable {
private final BlockingQueue queue;
Producer(BlockingQueue q) { queue = q; }
public void run() {
try {
while(true) { queue.put(produce()); }
} catch (InterruptedException ex) { ... handle ...}
}
Object produce() { ... }
}
class Consumer implements Runnable {
private final BlockingQueue queue;
Consumer(BlockingQueue q) { queue = q; }
public void run() {
try {
while(true) { consume(queue.take()); }
} catch (InterruptedException ex) { ... handle ...}
}
void consume(Object x) { ... }
}


下面是queue的延时处理,这里要用到nanosecond的定义, 一个nanosecond是10亿秒. 注意DelayQueue中元素是运行了Delay Interface的

import java.util.Random;
import java.util.concurrent.Delayed;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;

public class DelayTest {
public static long BILLION = 1000000000;
static class SecondsDelayed implements Delayed {
long trigger;
String name;
SecondsDelayed(String name, long i) {
this.name = name;
trigger = System.nanoTime() + (i * BILLION);
}
public int compareTo(Delayed d) {
long i = trigger;
long j = ((SecondsDelayed)d).trigger;
int returnValue;
if (i < j) {
returnValue = -1;
} else if (i > j) {
returnValue = 1;
} else {
returnValue = 0;
}
return returnValue;
}
public boolean equals(Object other) {
return ((SecondsDelayed)other).trigger == trigger;
}
public long getDelay(TimeUnit unit) {
long n = trigger - System.nanoTime();
return unit.convert(n, TimeUnit.NANOSECONDS);
}
public long getTriggerTime() {
return trigger;
}
public String getName() {
return name;
}
public String toString() {
return name + " / " + String.valueOf(trigger);
}
}
public static void main(String args[])
throws InterruptedException {
Random random = new Random();
DelayQueue qeue =
new DelayQueue();
for (int i=0; i < 10; i++) {
int delay = random.nextInt(10);
System.out.println("Delaying: " +
delay + " for loop " + i); queue.add(new SecondsDelayed("loop " + i, delay));
}
long last = 0;
for (int i=0; i < 10; i++) {
SecondsDelayed delay = (SecondsDelayed)(queue.take());
String name = delay.getName();
long tt = delay.getTriggerTime();
if (i != 0) {
System.out.println("Delta: " +
(tt - last) / (double)BILLION);
}
System.out.println(name + " / Trigger time: " + tt);
last = tt;
}
}
}

下面是输出情况:

Delaying: 8 for loop 0
Delaying: 7 for loop 1
Delaying: 2 for loop 2
Delaying: 4 for loop 3
Delaying: 0 for loop 4
Delaying: 9 for loop 5
Delaying: 3 for loop 6
Delaying: 4 for loop 7
Delaying: 6 for loop 8
Delaying: 2 for loop 9
loop 4 / Trigger time: 1883173869520000
Delta: 1.9995545
loop 2 / Trigger time: 1883175869074500
Delta: 0.0012475
loop 9 / Trigger time: 1883175870322000
Delta: 0.9995177
loop 6 / Trigger time: 1883176869839700
Delta: 0.9995187
loop 3 / Trigger time: 1883177869358400
Delta: 6.408E-4
loop 7 / Trigger time: 1883177869999200
Delta: 2.0001667
loop 8 / Trigger time: 1883179870165900
Delta: 0.9986953
loop 1 / Trigger time: 1883180868861200
Delta: 0.9995595
loop 0 / Trigger time: 1883181868420700
Delta: 1.001262
loop 5 / Trigger time: 1883182869682700


- 作者: zhafn 2005年11月19日, 星期六 13:35  回复(1) |  引用(1) 加入博采

使用Thread Pooling处理简短任务
使用Thread Pooling处理简短任务
当java程序中要用线程处理多个简短任务的时候,使用一种叫做Thread Pooling的技术是很明智的选择。为了不对单个任务逐个创建(并在任务结束时结束)线程,过去我们经常自己编写适合不同运行环境需要的线程池。虽然这些线程池的具体属性参数不尽相同,但是大致都追寻着如下的应用套式:

把任务组引入到线程池里面,
如果线程池中某个线程满足执行条件,立刻执行
任务执行完毕,线程返回线程池
不满足执行条件(比如线程池大小条件),等待执行

J2SE5.0现在提供了全新的java.util.concurrent包,其中有几经构建好的线程池的框架。
例如:Executor接口提供了单独的一个办法execute来接收一个Runnable的实例对象:
public interface Executor {
public void execute(Runnable command);
}

所以我们可以创建一个Executor对象 然后把runnable任务引入:

Executor executor = ...; //可以完成implements后创建...
executor.execute(aRunnable1);
executor.execute(aRunnable2);
例如:

class MyExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}
在concurrency中还包括一个ThreadPoolExecutor类来提供一般通用用途的线程池操作.下面是4种该类的构造器.我们可以确定该类的某些属性例如: 池的大小,活动时间,线程工厂和被拒绝运行线程控制器.
  public ThreadPoolExecutor(int corePoolSize,
                             int maximumPoolSize,
                             long keepAliveTime,
                             TimeUnit unit,
                             BlockingQueue workQueue)
   public ThreadPoolExecutor(int corePoolSize,
                             int maximumPoolSize,
                             long keepAliveTime,
                             TimeUnit unit,
                             BlockingQueue workQueue,
                             ThreadFactory threadFactory)
   public ThreadPoolExecutor(int corePoolSize,
                             int maximumPoolSize,
                             long keepAliveTime,
                             TimeUnit unit,
                             BlockingQueue workQueue,
                             RejectedExecutionHandler handler)
   public ThreadPoolExecutor(int corePoolSize,
                             int maximumPoolSize,
                             long keepAliveTime,
                             TimeUnit unit,
                             BlockingQueue workQueue,
                             ThreadFactory threadFactory,
                             RejectedExecutionHandler handler)

其实我们实际使用线程池的时候并不需要用构造器来专门建立上面的ThreadPoolExecutor的对象.Executors类中已经创建了默认的线程池.例如我们可以在Executors中调用newFixedThreadPool办法规定你需要的线程池的大小.可以使用一个继承了Executor的ExectuorService类来运行或者提交Runnalbe任务组. ExectuorService的提交办法 -- submit 允许得到一个结果F, 而且返回FUTURE OBJECT,可以用来检查是否任务已经执行完毕.
下面是一个具体的应用实例: -- 摘自sun技术论坛
   
     public class NamePrinter implements Runnable {
     private final String name;
     private final int delay;
     public NamePrinter(String name, int delay) {
       this.name = name;
       this.delay = delay;
     }
     public void run() {
       System.out.println("Starting: " + name);
       try {
         Thread.sleep(delay);
       } catch (InterruptedException ignored) {
       }
       System.out.println("Done with: " + name);
     }
   }

----------------------------------------------------------------------------------

   import java.util.concurrent.*;
   import java.util.Random;


   public class UsePool {
     public static void main(String args[]) {
       Random random = new Random();
       ExecutorService executor =
               Executors.newFixedThreadPool(3); //设定线程池容量为3
       // Sum up wait times to know when to shutdown
       int waitTime = 500;
       for (int i=0; i<10; i++) {
         String name = "NamePrinter " + i;
         int time = random.nextInt(1000);
         waitTime += time;
         Runnable runner = new NamePrinter(name, time);
         System.out.println("Adding: " + name + " / " + time);
         executor.execute(runner);
       }
       try {
         Thread.sleep(waitTime);
         executor.shutdown();
         executor.awaitTermination
                 (waitTime, TimeUnit.MILLISECONDS);
       } catch (InterruptedExceptin ignored) {
       }
       System.exit(0);
     }
    }

----------------------------------------------------------------------------------
可能输出: 


unique with the random sleeps present: 

   Adding: NamePrinter 0 / 30
   Adding: NamePrinter 1 / 727
   Adding: NamePrinter 2 / 980  //前3个添加进行的明显比较快 
   Starting: NamePrinter 0
   Starting: NamePrinter 1
   Starting: NamePrinter 2
   Adding: NamePrinter 3 / 409
   Adding: NamePrinter 4 / 49
   Adding: NamePrinter 5 / 802
   Adding: NamePrinter 6 / 211
   Adding: NamePrinter 7 / 459
   Adding: NamePrinter 8 / 994
   Adding: NamePrinter 9 / 459
   Done with: NamePrinter 0    虽然任务全部已经添加到线程池, 
   Starting: NamePrinter 3     但是因为线程池容量为3个,
   Done with: NamePrinter 3    前3个任务又都在执行,
   Starting: NamePrinter 4     所以任务3一直等到任务0结束才开始执行
   Done with: NamePrinter 4
   Starting: NamePrinter 5
   Done with: NamePrinter 1
   Starting: NamePrinter 6
   Done with: NamePrinter 6
   Starting: NamePrinter 7
   Done with: NamePrinter 2
   Starting: NamePrinter 8
   Done with: NamePrinter 5
   Starting: NamePrinter 9
   Done with: NamePrinter 7
   Done with: NamePrinter 9
   Done with: NamePrinter 8


线程池框架中还有很多其它的内容,请参考api. _

- 作者: zhafn 2005年11月19日, 星期六 13:35  回复(0) |  引用(1) 加入博采

HttpSessionListener统计在线人数
/** 
* 编写以下SessionCounter.java 
* 并编译为SessiionCounter.class 
* 然后放到你的网站的classpath的 
* SessionCount(自己建立此目录)下面 
*/ 

package SessionCount; 
import javax.servlet.*; 
import javax.servlet.http.*; 

public class SessionCounter implements HttpSessionListener { 

private static int activeSessions = 0; 

public void sessionCreated(HttpSessionEvent se) { 
activeSessions++; 
} 

public void sessionDestroyed(HttpSessionEvent se) { 
if(activeSessions > 0) 
activeSessions--; 
} 

public static int getActiveSessions() { 
return activeSessions; 
} 
} 

接着建立online.jsp文件用于显示在线人数 
 
在线: 

然后需要在你的网站的WEB-INF中建立web.xml 
文件内容如下: 


然后重新启动你的应用服务器,访问online.jsp检 _

- 作者: zhafn 2005年11月19日, 星期六 13:34  回复(0) |  引用(1) 加入博采

Flash Lite 1.1 FSCommand2 用法列表
尽管 Flash Lite 2.0 已经就快出来了,但是,掌握一些 Flash Lite 1.1 的基础知识还是很必要的,对手机 Flash 应用的开发者来说,等到 Flash Lite 2.0 再动手并不是一个最好的选择,能掌握先机是很重要的。

  这些资料其实是从 Flash Lite 1.1 的文档翻译来的,是为了今后查找的时候更方便一些而已。这些功能并不是所有手机都会支持,需要上机测试才能了解是否支持。

  Flash Lite 1.1 目前支持功能相对较强的 FSCommand2,这在普通的 FLash 播放器中是不支持的。相对 FSCommand,有如下的不同:

  1. FSCommand2 可以使用任意多的参数;
  2. FSCommand2 在动画中被立即执行,而 FSCommand 则是在一帧结束后才执行;
  3. FSCommand2 可以有返回值,以便判断是否成功。

  FSCommand2 主要分成三类:普通指令、控制影片播放的指令、平台相关指令。下面是详细内容:

普通指令

普通指令::URL 编码相关

Escape

status = FSCommand2( “Escape”, original, encoded )
URL 编码指令,将 original 编码成 encoded。
返回 0 失败,返回 1 成功。

Example:
original_string = “hello, how are you?”;
status = fscommand2(”Escape”, original_string, “encoded_string”);

Unescape

status = FSCommand2( “Unescape”, encoded, original )
URL 解码指令,将 encoded 解码成 original。
返回 0 失败,返回 1 成功。

Example:
string2 = “Hello%7B%5BWorld%5D%7D”;
status = fscommand2(”Unescape”, string2, “normal_string”);

普通指令::输入框相关

SetInputTextType()

status = FSCommand2( “SetInputTextType”, variableName, type )
设定文本框可输入文本的类型,variableName 是对应的文本框内的值,type 则由以下内容控制:
Numeric: [0-9];
Alpha: [A-Z, a-z];
Alphanumeric: [0-9, A-Z, a-z];
Latin: 拉丁符号;
NonLatin: 非拉丁符号;
NoRestriction: 默认模式。
返回 0 失败,返回 1 成功。

Example:
status = fscommand2(”SetInputTextType”, “input1″, “Numeric”);

控制影片播放的指令

控制影片播放的指令::显示

FullScreen()

status = FSCommand2( “FullScreen”, size )
设置全屏播放模式,size 应该为 true 或者 false
返回 -1 为不支持,0 则为支持。
某些手机不支持全屏播放模式。

SetQuality()

status = FSCommand2( “SetQuality”, quality )
设置动画播放的质量,quality 的值为 high,medium 或 low。
返回 -1 为不支持,0 则为支持。

控制影片播放的指令::键盘设置

SetSoftKeys()

status = FSCommand2( “SetSoftkeys”, left, right )
设置手机上左右两个软键的功能显示内容,left 和 right 可以是变量或者字符串。按下左键的时候激活的是 PageUp 事件,而右键则是 PageDown 事件。
返回 -1 为不支持,0 则为支持。

ResetSoftKeys()

status = FSCommand2( “ResetSoftKeys” )
重置左右软键。
返回 -1 为不支持,0 则为支持。

播放器操作指令

GetFreePlayerMemory()

status = FSCommand2( “GetFreePlayerMemory” )
获得剩余内存数,以 K 为单位。
返回 -1 为不支持,其它值则为剩余的内存,以 K 为单位。

GetTotalPlayerMemory()

status = FSCommand2( “GetTotalPlayerMemory” )
获得 Flash Lite 可使用的内存总数,以 K 为单位。
返回 -1 为不支持,其它值则为内存数,以 K 为单位。

Launch()

status = FSCommand( “Launch”, “application-path,arg1,arg2,…,argn” )
执行手机上的其它应用程序(操作系统无关)。

Quit()

status = FSCommand2( “Quit” )
退出当前的 Flash Lite 程序。
返回 -1 为不支持。

平台相关指令

平台相关指令::日期和时间

GetDateDay()

status = FSCommand2( “GetDateDay” )
获得当前的日期(1-31)。
返回 -1 为不支持,支持则返回 1-31 之间的数字。

GetDateMonth()

status = FSCommand2( “GetDateMonth” )
获得当前的月份(1-12)。
返回 -1 为不支持,支持则返回 1-12 之间的数字。

GetDateWeekday()

status = FSCommand2( “GetDateWeekday” )
获得当前的月份(0-6)。
返回 -1 为不支持,支持则返回 0-6 之间的数字。

GetDateYear()

status = FSCommand2( “GetDateYear” )
获得当前的年份。
返回 -1 为不支持,支持则返回数字,如:2005。

GetLocaleLongDate()

status = FSCommand2( “GetLocaleLongDate”, “longdate” )
获取当前的长日期串。日期格式根据手机本身以及 Locale 来确定。日期值指向到一个字串。
返回 -1 为不支持,0 则为支持。
以下是两个返回的 longdate 的例子:
October 16, 2004
16 October 2004

GetLocaleShortDate()

status = FSCommand2( “GetLocaleShortDate”, “shortdate” )
获取当前的短日期串。日期格式根据手机本身以及 Locale 来确定。日期值指向到一个字串。
返回 -1 为不支持,0 则为支持。
以下是两个返回的 shortdate 的例子:
10/16/2004
16-10-2004

GetLocaleTime()

status = FSCommand2( “GetLocalTime”, “time” )
获取当前的时间串。时间格式根据手机本身以及 Locale 来确定。时间值指向到一个字串。
返回 -1 为不支持,0 则为支持。
以下是两个返回的 time 的例子:
6:10:44 PM
18:10:44

GetTimeHours()

status = FSCommand2( “GetTimeHours” )
获得当前的小时(0-23)。
返回 -1 为不支持,支持则返回 0-23 之间的数字。

GetTimeMinutes()

status = FSCommand2( “GetTimeMinutes” )
获得当前的分钟(0-59)。
返回 -1 为不支持,支持则返回 0-59 之间的数字。

GetTimeSeconds()

status = FSCommand2( “GetTimeSeconds” )
获得当前的秒(0-59)。
返回 -1 为不支持,支持则返回 0-59 之间的数字。

GetTimeZoneOffset()

status = FSCommand2( “GetTimeZoneOffset”, “timezoneoffset” )
获取当前的时差数。时差值指向到一个数字,单位为秒。
返回 -1 为不支持,0 则为支持。
以下是两个返回的 timezoneoffset 的例子:
540: Japan standard time
-420: Pacific daylight savings time

平台相关指令::音量

GetMaxVolumeLevel()

status = FSCommand2( “GetMaxVolumeLevel” )
获取系统最大音量。
返回 -1 为不支持,支持则返回一个数字。

GetVolumeLevel()

status = FSCommand2( “GetVolumeLevel” )
获取系统当前音量。
返回 -1 为不支持,支持则返回一个数字。

平台相关指令::震动

StartVibrate()

status = FSCommand2( “StartVibrate”, time_on, time_off, repeat )
设定手机震动,time_on 为开始时间,time_off 为停止时间, repeat 为重复次数。time_on 和 time_off 均为百分秒,并且不超过 5 秒。
返回 -1 为不支持,0 为目前正在震动,1 为出现错误。

StopVibrate()

status = FSCommand2( “StopVibrate” )
停止震动。
返回 -1 为不支持,0 为停止成功。

平台相关指令::电源

GetBatteryLevel()

status = FSCommand2( “GetBatteryLevel” )
获得电量。
返回 -1 为不支持,支持则返回一个数字。

GetMaxBatteryLevel()

status = FSCommand2( “GetMaxBatteryLevel” )
获得最大电量。
返回 -1 为不支持,支持则返回一个数字。

GetPowerSource()

status = FSCommand2( “GetPowerSource” )
获取供电方式。
返回 -1 为不支持,0 为目前在使用电池供电,1 为目前在使用外部电源供电。

平台相关指令::网络信息

GetMaxSignalLevel()

status = FSCommand2( “GetMaxSignalLevel” )
获取最大信号强度值。
返回 -1 为不支持,支持则返回一个数字。

GetNetworkConnectStatus()

status = FSCommand2( “GetNetworkConnectStatus” )
获取当前网络状态。
返回 -1 为不支持,其它则为
0:在网络连接状态;
1:正在试图连接网络;
2:无可用网络连接;
3:网络连接暂停状态;
4:无法判断状态。

GetNetworkName()

status = FSCommand2( “GetNetworkName”, “networkname” )
获取连接网络的名称,返回值指向一个字串。
返回 -1 为不支持,其它则为
0:未连接网络;
1:连接网络,但是无法获取名称;
2:成功。
返回的 networkname 的例子:
AT&T Wireless
KPN Mobile

GetNetworkRequestStatus()

status = FSCommand2( “GetNetworkRequestStatus” )
获取网络请求状态。
返回 -1 为不支持,其它则为
0:等待中,网络连接已经成功,主机名已经解析,服务器也已经连接;
1:等待中,网络连接已经成功;
2:等待中,但是网络连接尚未成功;
3:等待中,连接已经成功,主机名已经解析;
4:失败,网络连接错误;
5:失败,连接服务器失败;
6:服务器返回 404 错误;
7:失败,无法连接 DNS 服务器或者无法解析主机名称;
8:请求成功完成;
9:请求超时;
10:尚未执行请求。

GetNetworkStatus()

status = FSCommand2( “GetNetworkStatus” )
获取网络状态。
返回 -1 为不支持,其它则为
0:无网络;
1:在本地网络;
2:在本地扩展网络;
3:漫游。

GetSignalLevel()

status = FSCommand2( “GetSignalLevel” )
获取当前信号强度值。
返回 -1 为不支持,支持则返回一个数字。

平台相关指令::用户设备信息

GetLanguage()

status = FSCommand2( “GetLanguage”, “language”)
获取用户设备使用的语言,返回值指向一个字串。
返回 -1 为不支持,0 则为支持。
返回的 language 值:
cs: Czech.
da: Danish.
de: German.
en-UK: UK or international English.
en: USA English.
es: Spanish.
fi: Finnish.
fr: French.
hu: Hungarian.
it: Italian.
jp: Japanese.
ko: Korean.
nl: Dutch.
no: Norwegian.
pl: Polish.
pt: Portuguese.
ru: Russian.
Platform integration commands 45
sv: Swedish.
tr: Turkish.
xu: The language cannot be determined.
zh-CN: Simplified Chinese.
zh-TW: Traditional Chinese.

平台相关指令::用户设备及播放器标识

GetDeviceID()

status = FSCommand2( “GetDeviceID”, “id” )
获取备的 ID,返回值指向一个字串。
返回 -1 为不支持,0 则为支持。

GetPlatform()

status = FSCommand2( “GetPlatform”, “platform” )
获取设备的操作系统平台,返回值指向一个字串。
返回 -1 为不支持,0 则为支持。
返回的 paltform 例子:
506i
FOMA1

GetDevice()

status = FSCommand2( “GetDevice”, “device” )
获取设备的型号,返回值指向一个字串。
返回 -1 为不支持,0 则为支持。

- 作者: zhafn 2005年11月19日, 星期六 13:27  回复(0) |  引用(4) 加入博采

关于ActionScript 1.0、ActionScript 2.0、java的区别与联系

问题:创建两个位置大小颜色各不同的可拖拽的矩形。

方法一:method1.fla //ActionScript 1.0
//Flash Player 6(含)以上
//ActionScript 1.0或ActionScript 2.0均可

createEmptyMovieClip ("rectangle1_mc", 1);
rectangle1_mc._x = 10;
rectangle1_mc._y = 10;
with (rectangle1_mc) {
beginFill (0xff0000, 100);
moveTo (0, 0);
lineTo (200, 0);
lineTo (200, 200);
lineTo (0, 200);
endFill ();
}
rectangle1_mc.onPress = startDrag;
rectangle1_mc.onRelease = rectangle1_mc.onReleaseOutside = stopDrag;
//
createEmptyMovieClip ("rectangle2_mc", 2);
rectangle2_mc._x = 50;
rectangle2_mc._y = 50;
with (rectangle2_mc) {
beginFill (0xffcc00, 100);
moveTo (0, 0);
lineTo (400, 0);
lineTo (400, 100);
lineTo (0, 100);
endFill ();
}
rectangle2_mc.onPress = startDrag;
rectangle2_mc.onRelease = rectangle2_mc.onReleaseOutside = stopDrag;

方法二:method2.fla //ActionScript 2.0
//Flash Player 6(含)以上
//必须为ActionScript 2.0

var rect1:Rectangle = new Rectangle (this, 10, 10, 200, 200, 0xff0000, 1);
var rect2:Rectangle = new Rectangle (this, 50, 50, 400, 100, 0xffcc00, 2);
delete rect1;
delete rect2;
delete Rectangle;

此方法必须得有一个Rectangle.as与之同路径。Rectangle.as内容如下:

class Rectangle extends MovieClip {
    private var rectangle_mc:MovieClip;
    public function Rectangle (target:MovieClip, x:Number, y:Number, w:Number, 
	h:Number, color:Number, depth:Number) {
        rectangle_mc = target.createEmptyMovieClip ("rectangle_mc" + depth, depth);
        rectangle_mc._x = x;
        rectangle_mc._y = y;
        with (rectangle_mc) {
            beginFill (color, 100);
            moveTo (0, 0);
            lineTo (w, 0);
            lineTo (w, h);
            lineTo (0, h);
            endFill ();
        }
        rectangle_mc.onPress = startDrag;
        rectangle_mc.onRelease = rectangle_mc.onReleaseOutside = stopDrag;
    }
}

方法三:method3.java //如果.java也可以直接编译.swf的话

public class method3{
    public static void main(String[] args){
        Rectangle rect1 = new Rectangle (this, 10, 10, 200, 200, 0xff0000, 1);
        Rectangle rect2 = new Rectangle (this, 50, 50, 400, 100, 0xffcc00, 2);
        //这里就不需要手动delete了,因为java有垃圾回收器,它会自动清除。
    }
}
class Rectangle extends MovieClip {
    private MovieClip rectangle_mc;
    public Rectangle (MovieClip target, Double x, Double y, Double w, Double h, 
    Double color, Double depth) {
        rectangle_mc = target.createEmptyMovieClip ("rectangle_mc" + depth, depth);
        rectangle_mc._x = x;
        rectangle_mc._y = y;
        with (rectangle_mc) {
            beginFill (color, 100);
            moveTo (0, 0);
            lineTo (w, 0);
            lineTo (w, h);
            lineTo (0, h);
            endFill ();
        }
        rectangle_mc.onPress = startDrag;
        rectangle_mc.onRelease = rectangle_mc.onReleaseOutside = stopDrag;
    }
}

通过比较不难发现,ActionScript 1.0最灵活、最简单、最容易理解。但重复类似的操作时也会最繁琐。如果要画1000个位置大小颜色各不同的矩形的话,不累死也会被笑死! ActionScript 2.0则非常系统,单独用Rectangle.as定义了一个矩形类,使用时new即可,爽!有点小小的遗憾的是有几行delete,当然你也可以不加,让它永远霸占你的内存。再看看java,看完了又看,看完了再看,呵呵! 啥也不说了,谁老大肯定有人知道。

编辑:卡其色

- 作者: zhafn 2005年11月19日, 星期六 13:24  回复(0) |  引用(1) 加入博采

给flash加一个trim()函数
flash没有自带trim()函数,只有自己加一个,下面是函数代码
var myS:String="      前面后面含有全角空格\" \"和普通空格\" \",现在把他们去掉    ";

trace("原字符串:"+myS);
trace("处理后的:"+trim(myS));

function trim(myString:String){ //过滤前后空格
 if(myString.indexOf(" ")==-1 && myString.indexOf(" ")==-1){  //如果字符串内没有空格直接返回
  return myString;
 }
 var omyString:String=myString; //把原始字符串保存下来
 myString=replace(myString," "," "); //把全角空格替换成普通空格,方便处理
 
 var lblanknum:Number=0;//开始位置的空格数量
 var rblanknum:Number=0;//结束位置的空格数量
 
 //计算开始位置的空格数量
 for(var i:Number=0;i<myString.length;i++){
  if(myString.charAt(i)!=" "){
   break;
  }else{
   lblanknum++;
  }
 } 
 //计算结束位置的空格数量
 for(var i:Number=(myString.length-1);i>0;i--){
  if(myString.charAt(i)!=" "){
   break;
  }else{
   rblanknum++;
  }
 } 
 return omyString.substring(lblanknum,(omyString.length-rblanknum));
}

function replace(s:String,o:String,n:String){ //字符替换函数 把s中o替换成n
 return s=s.split(o).join(n);
}
输出部分
原字符串:      前面后面含有全角空格" "和普通空格" ",现在把他们去掉    
处理后的:前面后面含有全角空格" "和普通空格" ",现在把他们去掉

- 作者: zhafn 2005年11月19日, 星期六 13:14  回复(1) |  引用(1) 加入博采

AS3.0概要–了解AS3.0的改变

随着一些相关资料对as3.0的介绍,有人可能认为它是另一种语言.它的根本改变在哪呢,它是什么呢?现在我们要放松一点.如果你熟知as2.0,那么它的变化并不是很大,甚至增加了一些命令你可以使用。

从我们第一眼看as3.0,它并不是一个全新的语言,它的架构要好于AS2.0,你将从FLASH8开始发现这些.任何东西都有自已的类和整洁的子类.类的继承关系看起来很复杂,但是它却是很容易理解。

主要的改变:

1、不在有_global范围了,但是你可以通过在预先的public,private和internal里,使用”namespace”来创建你自已的命名。

2、int/uint. 新的数据类型来描述非浮点数,这项增加可以使flash与其它程序语言同步,并且解决一些使用java和AMF/Flash Remoting令人头痛的问题。

3、你不能在在时间线上使用命令play()或stop().MovieClip不在是在global的范围内了,你必须通过flash.display.MovieClip来使用它的属性。

4、正规标准表达式—-快速搜索操作字符串。

5、新的更简单的委派(delegate)。

6、DOM3 事件模型——个新的,但是不被熟知的生成和操作事件信息的方法。

7、显示API列表——图像根据新的或更多的逻辑基于类别如Sprites精灵和Shapes形体被细分。

8、在也不需要指定depth深度数值给对象. Depth管理类现在会自动控制(基于API列表)并内建于flash player内.新的方法提供了对对象z-order也就是Z轴的操作。

9、Final/protected关键词.—防止你的类或函数被覆写。

10、新的简单的XML元素及属性使用E4X。

11、ArgumentError 类.——使用此类可以避免函数调用了不相符合的参数时所产生的错误。

12、Package 关键诩—-如果你不知道pagckage是什么,不要担心,当你在做大的项目时你会慢慢喜欢上它。

13、被用在数据输入输出接口的ByteArray提供方法和属性来优化读,写,和二进制数据。

低级别—异常

异常处理对于FLASH开发人员来说路还较长,如果一个数值超出新的int/unit数据类型,一个错误就会被显示出来.有些类似于JAVA.你必须要想办法解决这些问题,否则FLASH程序可能就会垮台.如果一些东西在AS1和AS2中失效,flash player可能会很友好的忽略掉,在FLASH8中,你可能已经通过使用file upload看的了新的究错能力,它需要一系列事件来显示上载进程和缓存错.使用AS3,这些是工作在一个低级别的状态。

异常可能会发生几个地方,例如:使用As3.0,你需要查看你的内存的最近使用状况,MemoryError是一个新的异常,当内存寻址失败时,它就会通过AVM2虚拟机显示错识.其它的异常为EOFError,illegalOperatinError,IOError,ScriptTimeoutError和StackOverflowError。

想一下上面这些内容,之前许多是JAVA等其它语言专用的,你可以通过StringBuilder类看到,它来自java类StringBuffer可以允许我们很容易的操作字符.终上所述,我必须说新的AS3类的结构看起来非常好.有不同背景的程序员看到FLASH就会说我知道它是怎么做的,我认为我们可以说actionscript已经过去了,它的童年过去了,青年时代刚刚到来。

新的命令

可能有更多的新命令,这里面例出一些发现到的。

Sound(声音)

leftPeak / rightPeak Property——当前声音的右声道振幅,从0到1

isBuffering : Boolean [read-only]——返回外缓冲MP3的状态

soundBufferTime : uint——声音在流式播放前缓冲的秒数

System(系统)

vmVersion : String [read-only]——当前安装的ActionScript虚拟机的版本

totalMemory : uint [read-only]——报告当前使用的内存数量:System.html

Debugging(调试)

getClassByName(name:String):Class——返回指定名称类对象的引用

describeType(value:Object):XML——xml对象产物,用来描述actionscript对象参数的方法。

ps:看来As3.0并没有想象中的变化那么大,不过先是FLASH8,接着FLEX2,AS3.0,macroemdia的脚步是越走越快。

- 作者: zhafn 2005年11月19日, 星期六 13:12  回复(0) |  引用(2) 加入博采

在oracle中实现自动增长的列号(例子)
这是一个简单的例子,不过已经能够表示如何实现这类功能了。

1、在scott用户下建立表dept(其实oracle缺省安装就已经生成了)
create table DEPT
(
  DEPTNO NUMBER(2) not null,
  DNAME VARCHAR2(14),
  LOC VARCHAR2(13)
)

2、建立一个sequence
create sequence seq_dept
minvalue 1
start with 1
increment by 1
nocache;

3、为dept表建立一个insert trigger
create or replace trigger insert_dept
  before insert on dept   
  for each row
declare
  -- local variables here
begin
   select seq_dept.nextval into :new.deptno from dual;
end insert_dept;

4、用insert语句测试

insert into dept(dname,loc) values('aaaaaa','bbbbb');

用select * from dept察看结果

- 作者: zhafn 2005年11月19日, 星期六 13:11  回复(0) |  引用(1) 加入博采

第29届奥林匹克运动会吉祥物!

                              

第29届奥林匹克运动会吉祥物——福娃
 
   福娃是北京2008年第29届奥运会吉祥物,其色彩与灵感来源于奥林匹克五环、来源于中国辽阔的山川大地、江河湖海和人们喜爱的动物形象。福娃向世界各地的孩子们传递友谊、和平、积极进取的精神和人与自然和谐相处的美好愿望。 福娃是五个可爱的亲密小伙伴,他们的造型融入了鱼、大熊猫、藏羚羊、燕子以及奥林匹克圣火的形象。
  每个娃娃都有一个琅琅上口的名字:“贝贝”、“晶晶”、“欢欢”、“迎迎”和“妮妮”,在中国,叠音名字是对孩子表达喜爱的一种传统方式。当把五个娃娃的名字连在一起,你会读出北京对世界的盛情邀请“北京欢迎您”......
详细信息可以阅读:http://www.beijing2008.com/48/01/column211990148.shtml

- 作者: zhafn 2005年11月13日, 星期日 20:36  回复(0) |  引用(1) 加入博采

试用Zoundry: 离线blog发布工具

以下是工作界面截图:
blog_writer_zoundry.png

http://www.zoundry.com/

- 作者: zhafn 2005年11月13日, 星期日 20:14  回复(1) |  引用(1) 加入博采

Inside Lucene/超人气搜索引擎学习(1)-查询机制

何用户, 包括系统开发者, 使用搜索引擎的共同方式只有一个: 查询(query). 整个搜索过程的目的是为了满足查询要求, 搜索过程是由查询贯穿的. 若没有指定查询, 而是从索引(index)的内容出发, "搜索"将是漫无目的且毫无意义的过程. 搜索过程的起点只能是索引.

下引用自Lucene in Action 的入门章节, 在其中能看到Query是如何用来启动查询的.

public class BasicSearchingTest extends LiaTestCase {
public void testTerm() throws Exception {
IndexSearcher searcher = new IndexSearcher(directory);
Term t = new Term(“subject”, “ant”);
Query query = new TermQuery(t);
Hits hits = searcher.search(query);

assertEquals(“JDwA”, 1, hits.length());

t = new Term(“subject”, “junit”);
hits = searcher.search(new TermQuery(t));

assertEquals(2, hits.length());

searcher.close();

}
}

称为面向对象设计经典范例的Lucene, 其架构确实能反映"查询"行为在真实世界模式, 这不愧是OOA/OOD方法带来的成熟设计. 这也让我对Lucene设计者的软件设计能力产生充足的敬佩, 要知道人家可是研究算法的. 通常搞学术和搞工程的人思想极不统一, 我同学在微软工作时曾经为研究院和工程院的代码差异头疼不已. Lucene的设计者们能把算法和代码集合得如此完美, 可以说是牛中之牛了.

真正关心的是搜索算法如何依据Query导出查询结果. 上面的代码给我一些启示, 我知道了起点, 从searcher.search(query)开始, 我可以一步步了解Query在搜索过程里的作用机制.
为了满足真实世界的语义, Lucene提供了众多Query. 上面代码中的TermQuery是最简单的Query, 日常搜索有许多直接或间就由TermQuery组合而成. search(termQuery)的构造最基础, 不经过繁琐的转换. 所以我从TermQuery出发, 一步步考察搜索的核心机制.

入search 方法前, 我了解了这些限制条件: TermQuery的语义中每一个Term指一个(field, keyword)对, 其描述的查询条件是: "在指定的字段(标题、作者、内容...)中出现指定的keyword"; 高级的search方法可以处理自定义的Filter, Sort, 此处的考察对象是不假这些自定义选项的最简单的search.

了以上的限制, 我考察的对象--TermQuery查询过程已经被彻底简化了. 但在他和更复杂的重载方法中, 开发人员应用了相同的思路, 举一反三是我们可以期望做到的.
public class IndexSearcher extends Searcher {
public TopDocs search(Query query, Filter filter, final int nDocs)
throws IOException {
Scorer scorer = query.weight(this).scorer(reader);
if (scorer == null)
return new TopDocs(0, new ScoreDoc[0]);

final BitSet bits = filter!=null?filter.bits(reader):null;
final HitQueue hq = new HitQueue(nDocs);
final int[] totalHits = new int[1];
scorer.score(new HitCollector() {
public final void collect(int doc, float score) {
if (score > 0.0f &&
(bits==null || bits.get(doc))) {
totalHits[0]++;
hq.insert(new ScoreDoc(doc, score));
}
}
});


ScoreDoc[] scoreDocs = new ScoreDoc[hq.size()];
for (int i = hq.size()-1; i >= 0; i--)
scoreDocs[i] = (ScoreDoc)hq.pop();

return new TopDocs(totalHits[0], scoreDocs);
}
...
}

主干流程是:1.获取scorer对象
 Scorer scorer = termQuery.weight.scorer(indexReader)

2.调用这个Scorer对象(此处是TermScorer)的score (Collector)方法.
 scorer.score(new HitCollector() {
...
});

这两步之后, collector就把hq用查询结果填满了, 而用户得到的结果就是从hq中一个一个取出的. 这段代码中, indexReader用于读取index数据, 以供查询使用, Scorer负责用查询结果把Collector填满. 问题是,scorer的'查询结果'从哪里来? 如果IndexReader向Scorer提供数据, 数据内容是如何从索引文件中选取的?

Scorer 用一个匿名类Collector来收集满足TermQuery的Doc, 但Scorer怎么能够知道哪些文档符合Query? 真实查询并非在score()方法中进行, 从数据提取的角度来说, 现代搜索引擎都是从inverted index中提取的满足Term的文档列表. 是个人都知道inverted index里有什么, 由指定的keyword得到所有对应的文档就是利用inverted index数据结构完成的, 这也是搜索过程的核心--一个延续了数百年的索引方法. 我要考察的是, 在这个新式的OO搜索引擎框架中, 谁(哪个对象/类)负责提取term对应的记录, 他怎样把结果交给Scorer?

取inverted index是IndexReader的责任, 这时已经提到过的. 关于这一点的知识来自于Lucene手册中星星点点的暗示和大量的代码阅读, 此处不纠缠这个问题, 现在关心的是Scorer和IndexReader怎样发生联系? 回忆一下上边的代码中TermQuery创建Scorer时用的参数, 正是IndexReader对象, 动动脚趾头也猜得出来TermQuery利用"创建"这种特权对score秘密动了手脚. 现在瞄一眼weight在构造scorer时玩了什么花样. 代码是这样写的
public scorer weight#scorer(IndexReader reader){
TermDocs termDocs = reader.termDocs(term);

if (termDocs == null)
return null;

return new TermScorer(this, termDocs, getSimilarity(searcher),
reader.norms(term.field()));
}


class Scorer{
...
public void score(HitCollector hc) throws IOException {
while (next()) {
hc.collect(doc(), score());
}
}
...
public boolean next() throws IOException {
pointer++;
if (pointer >= pointerMax) {
pointerMax = termDocs.read(docs, freqs); // refill buffer
if (pointerMax != 0) {
pointer = 0;
} else {
termDocs.close(); // close stream
doc = Integer.MAX_VALUE; // set to sentinel value
return false;
}
}
doc = docs[pointer];
return true;
}
...
}


可以看出, 上面的问题唯一可能的答案是: weight在构造Scorer时已经为Scorer决定了查询内容就在那个termDocs里. Scorer的代码也表明, 它在遍历所有合法文档时,背后的查询动作是在穷举一个数组:doc[], 而这个数组的来源就是TermDocs. 剩下的问题是,TermDoc在整个查询中扮演何种角色--它是怎么读数据的

看看TermDoc是个虾米东东
reader 创建好TermDoc后调用TermDoc.seek(term). 这个方法在硬盘索引文件中找到term所对应的所有文档记录, 每条记录包含文档id和term在文档中出现的次数tf. 这些文档信息是编制索引时就建好的, 索引文件中每个term对应的文档记录按顺序紧密排列在一起, seek方法能找到这些记录在索引中的开始位置及满足term的文档总数. 以后, TermDoc在scorer中的作用就是读入每个符合term的文档及term在该文档中的tf, 由于建好了索引只需在索引文件中遍历即可, termDoc包含的df将用于此过程的遍历计数.就是说scorer接收到的那个termDoc是调用过seek的, 已经定位到了term对应的数据位置,这便让scorer能遍历termQuery中所有包含那个term的Doc.

scorer怎样遍历全部doc
数据又是另一门学问, 感兴趣的人也许研究过读一个100M文件,与读1000个1K byte文件有何区别,结果当然是震撼的.只要有可能,尽量读取整数据而不是零碎地读取小数总不会让人失望,然而同样没有人会在构造一个对象时就去对一个未知大小(可能真的包含100万个文档)的数据.只要没收到必要请求,任何人都会尽力避免这种冗长的操作. Lucene的设计者同样只是让在scorer在需要读入数据即第一次调用next()方法时调用termDoc的read方法读数据(代码中黑体部分).为了避免零碎读取降低硬盘效率,termDoc.read()会一次性读入所有合法文档(当然仅包括文档id和tf, 建立索引的过程中,这两个数据一组组的放在专门的文件中,每个term对应的全部文档在这个文件里连续排列以避免零碎读取),scorer调用next ()语句,遍历read()返回的文档id数组,整个遍历过程只需把读出来的doc[i]里的i进行++便万事大吉.

历过程中发生什么事情大家心里应该很清楚,无非是把这些doc(这就是搜索结果)一个个添加到Collector中. 查询结束后, 我们将得到一个int数组, 里面保存着每个结果文档的id. 要使用这些查询结果,用户还需要从按照每篇文档的id文档库中取出结果, 这些只需调用searcher.doc(id)即可完成的事务性过程不在本"技术"文章讨论范围中.

所谓搜索原来如此简单...

- 作者: zhafn 2005年11月13日, 星期日 20:09  回复(2) |  引用(1) 加入博采

Inside Lucene/超人气搜索引擎学习(0.5)-前传
论有多少精彩的应用, 这个印刷时代就诞生的公式都不会作废, 公式中最关键的成分是 1. 数据, 2.算法. 虽然二者中谁对结果质量更重要依然引起争论, 我的精力并未放在这两者上. 关于spider已有数本出名的专著, 算法原理的本质也早不是秘密, 这是搜索引擎必备的条件, 是基础设备而不是制胜的杀手锏. 面对一个活生生的搜索引擎, 研究这两者就像面对解剖台上的小白鼠, 却放下手术刀去查上课用的内脏图解. 我只注意实现, 实现是商业效率的体现, 这才是真刀真枪狼烟四起的地方.

开数据采集, 上边的周期分成两个相(phase), 1. Indexing(建立索引), 2.Searching(搜索). 还有一个现在看来显得古怪的词-检索, 图书馆系统大多用这个词. 中国人的词汇总是显得睿智, “索”本有求取寻找的意思, "索引"原本就是为搜索创造的. 那些题目是"倒排索引原理"大篇幅对索引和模式匹配进行比较的文章, 因此有些让人哭笑不得. 上述的两个phase正是lucene覆盖的范围(Lucene核心不提供crawler), 这两个phase一个用于生成索引(index), 一个用于从index取出数据, 可以看出Lucene的一切行为都和index有关. 幸好Lucene官方指南 Lucene in Action 在附录中用浅显的语言介绍了index的结构, 这让人了解Lucene怎么把一片片文档塞进去, 就像绞肉机把一片片五花肉里脊肉搅成肉酱. 但是有点遗憾的感觉: 搜索过程怎么把index中支离破碎的数据记录(index的结构用法着实很像数据库)恢复成检索结果? 就像主妇们怎么用屠夫的肉酱作出香喷喷的肉丸?

从Query 的构造开始, 详细检验了Lucene处理Query的过程. 这种努力使我了解到index中的记录(就是数据库中的record概念), 如何在检索过程中起作用. 这是深入把握Lucene实现的基础, 若要对Lucene作基础的调整和改动, 自然缺不了这一步(这是我的动机之一). 这种探索也帮助我了解原始文档资料的各种属性如何决定搜索结果, 进一步的研究可以揭示出文档各属性的重要性以及文档对查询条件的敏感性, 从这里出发可以提炼出更普遍的原理, 再结合通用的Search Engine原理, 把"Search Engine是什么, 长什么样"深深地刻在脑海中. 这对我是一次很好的搜索引擎进阶的途径. 接下来的就是所谓SE优化( Search Engine Optimizing ), 不敢讲这种天怒人怨的话题...

- 作者: zhafn 2005年11月13日, 星期日 20:08  回复(1) |  引用(1) 加入博采

Inside Lucene/超人气搜索引擎学习(0)-序
女落幕,"中国搜索"开始有样学样, 办起了第一届网络"超搜" 比赛. 先不说有没有技术含量, 某网站已经以"IT泡沫重来,中搜派发汽车"为题第一时间替大家惊讶了一把. 欧还记得几年前拿到一笔10000元IT比赛奖金的狂喜, 如今的IT浪潮更是金元遍地, 像中搜这样的老牌搜索引擎当然财大气粗拿汽车当奖品. 更有消息灵通人士说, 最近的概念火就火在"搜索引擎"了.

实有人烧钱未必不是好事, 至少这件事说明搜索引擎在网络使用者中间的普及度已经相当高. 受众的数量和素质提高, 才会有个性化服务和创新应用, 这对用户当然是好事, 对ITer来说, 多了口饭吃, 而对Lucene......他已经"在丛中笑"了.

使用Lucene时, 或多或少会感到他功能虽强大, 但毕竟为了大多数人的需要做了很高的泛化, 常常在某些细节让人感到尴尬. 有时找不到令自己彻底满意的方法,又不愿意从头构造. 这一是由于用的顺手了, 二是没工夫把Lucene吃透. 有时候ITer的确懒得让人伤心, 用了几次Lucene就高兴得以为自己也能做个Google出来. 事情没有那么简单, 搜一搜"Lucene", 满屏都是"如何使用/FAQ", 稍微有点意思的也不过是把Lucene手册,我是说Lucene in Action(看书名就知道是使用指南)节选翻译. 看这种文章真的好象看幼儿园的孩子讨论伊拉克战争, 既无聊还不好意思去打断. 最有意思的是一片用整版篇幅介绍什么叫倒派索引文章, 适合从没看过带索引的IT书的同学. 要做Google, 不是不可能, 但这种程度远远不够.

像人无法理解大脑如何工作, 对付搜索引擎不能指望Google给出答案. 我把Lucene的源代码翻了无数次, 总算有点领悟. 趁自己还记得这些, 干脆一点点记下来, 免得这几天的思维运动像黄粱梦样归于无有. 希望这次连载成功(处女连).
如果你是Lucene使用者/开发者, 并且同样对Search Engine的实现感兴趣(不讨论原理, 那过于泛化), 请和我联系, 我很期待得到任何帮助/指正.

- 作者: zhafn 2005年11月13日, 星期日 20:07  回复(0) |  引用(1) 加入博采

用Javascript和DHTML构建的矢量图形库
在 www.sitepoint.com 上看了 Simon 的一篇 BLOG ,引导我去了一个很棒的 Web 开发站点 http://www.walterzorn.com/ ,并在那里获得了一个仅用 DHTML 和 JavSCRIPT 技术开发的一个高性能的矢量图形库 wz_jsgraphics.js (感谢 Walter Zorn 开发了这个图形库),使用它就可以在你的网页上画出漂亮的矢量图形了。进入这个图形库的介绍页面 http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm ,映入眼帘的是折线、多边形、圆等常见的一些几何图形,起初我以为不过是一些图片罢了,当我看到这行小字 These are not image files. Shapes & text have been dynamically drawn with wz_jsgraphics.js 后,并细细阅读了关于它的说明后,才意识到除了VML 和 SVG可以在 网页上绘制显示矢量图形,仅用DHTML 和 JavaSCRIPT 同样可以做到这一切!是不是觉得很吃惊呢?(双击一下这个文档,看看放大后的矢量图形吧),ok!废话少说,下面我将详细介绍一下这个图形库。

- 作者: zhafn 2005年11月13日, 星期日 20:00  回复(0) |  引用(1) 加入博采

用Javascript和DHTML构建的矢量图形库
在 www.sitepoint.com 上看了 Simon 的一篇 BLOG ,引导我去了一个很棒的 Web 开发站点 http://www.walterzorn.com/ ,并在那里获得了一个仅用 DHTML 和 JavSCRIPT 技术开发的一个高性能的矢量图形库 wz_jsgraphics.js (感谢 Walter Zorn 开发了这个图形库),使用它就可以在你的网页上画出漂亮的矢量图形了。进入这个图形库的介绍页面 http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm ,映入眼帘的是折线、多边形、圆等常见的一些几何图形,起初我以为不过是一些图片罢了,当我看到这行小字 These are not image files. Shapes & text have been dynamically drawn with wz_jsgraphics.js 后,并细细阅读了关于它的说明后,才意识到除了VML 和 SVG可以在 网页上绘制显示矢量图形,仅用DHTML 和 JavaSCRIPT 同样可以做到这一切!是不是觉得很吃惊呢?(双击一下这个文档,看看放大后的矢量图形吧),ok!废话少说,下面我将详细介绍一下这个图形库。

- 作者: zhafn 2005年11月13日, 星期日 20:00  回复(0) |  引用(1) 加入博采

面向JSF开发者 Otrix 发布WebStudio 2005 Volume 3

Otrix 近日宣布发行WebStudio 2005 Volume 3,这是一个面向JSF开发开发人员的商业one-stop组件合集。

其中包括:

WebGrid, WebInput (DateChooser, Email, RegEx, Range validator ...), WebMenu, WebTab and WebTree Components.
Samples for J2EE 1.4 application servers (IBM Websphere 6, JBoss 4, Resin 3, Sun AS8, Tomcat 5, ..)
Samples for J2EE 1.3 application servers (IBM Websphere 5.1, JBoss 3, Resin 2, Sun AS7, Tomcat 4, Weblogic 7 & 8..)

下载地址:http://www.otrix.com/

- 作者: zhafn 2005年11月13日, 星期日 19:56  回复(0) |  引用(1) 加入博采

工作中八大最易被淘汰的人
以下八类人在求职过程中,很难找到理想的工作,希望你不要成为其中之一;假如你有如下所说的特征之一,也希望你能够及时改正,为今后职场发展打好基础。

  情商低下的人

  智商显示一个人做事的本领,情商反映一个人做人的表现。在未来的社会里,不仅要 会做事,更要会做人,做人有时比做事更重要。

  心理脆弱的人

  随着竞争的加剧,企业发展过程中不可避免的会有来自各方面的压力,尤其是竞争的压力,心理脆弱将使你挑不起企业的重担,积极锻炼强健的身心才能在未来是赢家。

  知识陈旧的人

  如今是资讯快速发达的时代,知识更新的速度越来越快,知识倍增的周期越来越短,要想靠早些时候学的知识“应付”一辈子,已完全不可能了。

  技能单一的人

  要想避免在职场中成为“积压物资”,唯一的办法就是多学几招,一专多能。这就是为什么复合型人才受欢迎的原因。

  反应迟钝的人

  “迟钝”就会“迟缓”,落后就要挨打,“敏捷”才是胜利的根本。

  单独打斗的人

  团队协作,沟通协调,合作共事是今后的必然形式,众人拾柴火焰高。

  目光短浅的人

  有句话说得好,“你能看多远,你便能走多远”。同样,一个人的成长需要不断地规划设计,要注重职业生涯规划,及早发现自己的职业锚,并尽可能匹配自己的工作。

  不善学习的人

  在当今学习型社会里,人与人之间的差异主要是学习能力的差异,人与人之间的“较量”关键在学习能力的“较量”,要做“学而知之”的人,学而不知或不知如何学的人是早晚会被淘汰的。

- 作者: zhafn 2005年11月13日, 星期日 19:54  回复(0) |  引用(1) 加入博采

工作中八大最易被淘汰的人
以下八类人在求职过程中,很难找到理想的工作,希望你不要成为其中之一;假如你有如下所说的特征之一,也希望你能够及时改正,为今后职场发展打好基础。

  情商低下的人

  智商显示一个人做事的本领,情商反映一个人做人的表现。在未来的社会里,不仅要 会做事,更要会做人,做人有时比做事更重要。

  心理脆弱的人

  随着竞争的加剧,企业发展过程中不可避免的会有来自各方面的压力,尤其是竞争的压力,心理脆弱将使你挑不起企业的重担,积极锻炼强健的身心才能在未来是赢家。

  知识陈旧的人

  如今是资讯快速发达的时代,知识更新的速度越来越快,知识倍增的周期越来越短,要想靠早些时候学的知识“应付”一辈子,已完全不可能了。

  技能单一的人

  要想避免在职场中成为“积压物资”,唯一的办法就是多学几招,一专多能。这就是为什么复合型人才受欢迎的原因。

  反应迟钝的人

  “迟钝”就会“迟缓”,落后就要挨打,“敏捷”才是胜利的根本。

  单独打斗的人

  团队协作,沟通协调,合作共事是今后的必然形式,众人拾柴火焰高。

  目光短浅的人

  有句话说得好,“你能看多远,你便能走多远”。同样,一个人的成长需要不断地规划设计,要注重职业生涯规划,及早发现自己的职业锚,并尽可能匹配自己的工作。

  不善学习的人

  在当今学习型社会里,人与人之间的差异主要是学习能力的差异,人与人之间的“较量”关键在学习能力的“较量”,要做“学而知之”的人,学而不知或不知如何学的人是早晚会被淘汰的。

- 作者: zhafn 2005年11月13日, 星期日 19:53  回复(0) |  引用(1) 加入博采

苹果iPhone出炉 超薄iPod Nano取代iPod mini
据外电报道,苹果给9月7日笼罩的神秘面纱在旧金山终于被揭开。在巨星麦当娜的“喝彩”声中,苹果CEO乔布斯向现场受邀出席的记者和嘉宾展示了两款最新的产品。正如此前媒体所猜测的那样,苹果一下推出了两种新品:分别是人们期盼以久的iTunes手机iPhone,以及一款名为Nano的超薄全新功能iPod音乐播放器。Nano将取代iPod mini。

  这款被业界称为iPhone的手机名字是拗口的ROKR,由摩托罗拉公司制造,它内置iTunes音乐播放软件,可以存储100首歌曲,带有彩屏和内置的数码相机。除了内置立体声扬声器,苹果还提供了立体声耳机,同时可以兼作二合一耳机。

  

  不过,手机用户无法直接通过移动网络下载音乐文件,他们必须从电脑中转移文件。这个功能和日本早已普及的“空中音乐”服务相比,稍微有点逊色。

  新的iPod播放器名为Nano,它将取代现有的mini系列。和使用硬盘的Mini相比,Nano将采用尚存,这使它重量更轻,也更加节电。Nano的体积仅有Mini的三分之一,重量只有1.5盎司,但苹果称它可以存储1000首歌曲和2.5万张照片。

  乔布斯面对嘉宾说:“Nano是自iPod诞生以来最大的革命,它的体积小到难以想像,比一支二号铅笔还薄。” 除了音乐功能,Nano还支持游戏、照片存储和日历,它还带有一个“锁屏”的功能,可以禁止主人以外的用户使用播放器。

- 作者: zhafn 2005年09月17日, 星期六 09:16  回复(0) |  引用(1) 加入博采