糯米

TI DaVinci, gstreamer, ffmpeg
随笔 - 167, 文章 - 0, 评论 - 47, 引用 - 0
数据加载中……

HDU 2817 A sequence of numbers 数论

最近又开始做ACM了。原因是,我在学校里面找了份兼职,做ACM兴趣班的助教。
任务是给大一的新生讲一些ACM的入门知识。
虽然讲的东西很基础很简单,但我觉得他们的潜力巨大,很快就能达到跟我一样的水平。。
所以我自己也得补下课啦。

这次我下决心,坚决不做一道水题!
我放弃了原来在POJ账号,转战HOJ,因为这样我才不会为了题目数量而刷水题。
我觉得以前真的很傻逼,老是挑着做一些自己已经会了的题目,还总是抱怨自己的水平一直没提高。
整天刷水题,水平他妈的能提高么。
人都是很懒的,懒得动脑子,只是享受敲完一道水题后ac的感觉,还很有成就感。
但这样子有什么用,遇到难题的时候,始终还是想不出来,还以为是自己脑子不好使。
实际上不是的,只要克服惰性,多思考问题,每个人的脑子都是很好使的。

我挑战的第一道难题,呵呵,对我来说比较难的题,就是这道。
想了一下发现了这个规律:
(A*B)%M = ((A%M)*(B%M))%M
演算一下就可以得出这个的。
对与输入中的一个数字,可以分别计算出:
A%M
A^2 %M
A^4 %M
...
然后再相乘取模就行了。
这样二分一下,就很好办了。

这是我自己克服了懒惰动了脑子想出来的!我他妈的很自豪!

#include <stdio.h>

__int64 N, K, A, B, C, d, i, n, ans;
#define M 200907

int main()
{
    scanf(
"%I64d"&N);
    
while (N--{
        scanf(
"%I64d%I64d%I64d%I64d"&A, &B, &C, &K);
        
if (B - A == C - B) {
            ans 
= ((A%M) + (((K-1)%M)*((B-A)%M)%M))%M;
        }
 else {
            d 
= (B/A)%M;
            n 
= K - 1;
            ans 
= A%M;
            
for (i = 0; (1LL << i) <= n; i++{
                
if (n & (1LL << i))
                    ans 
= (ans*d)%M;
                d 
= (d*d)%M;
            }

        }

        printf(
"%I64d\n", ans);
    }


    
return 0;
}

posted @ 2010-10-25 21:29 糯米 阅读(331) | 评论 (0)编辑 收藏

ubuntu下安装精简而实用的桌面环境

apt-get install xfwm4
apt-get install scim-chinese
apt-get install roxterm
cat >~/.xinitrc <<E
roxterm &
scim &
xfwm4
E
xinit

posted @ 2010-10-08 20:49 糯米 阅读(709) | 评论 (0)编辑 收藏

dell m1330 xps笔记本linux下的无线网卡驱动的安装

插入买机的时候送的驱动光盘,灰色的那张。
mount /dev/cdrom /media/cdrom
cd /tmp
find /media/cdrom -name '*.exe' | while read i; do unzip -ao $i; done
cd DRIVER_US
ndiswrapper -i bcmwl5.inf
rmmod b43
rmmod ssb
modprobe ndiswrapper
iwconfg .....

posted @ 2010-10-08 20:43 糯米 阅读(521) | 评论 (0)编辑 收藏

POJ 2362 Square 搜索

题意:
给出N条木棍的长度,问能不能用这些木棍拼成一个正方形。


思路:
这题一开始想复杂了,后来发现只要2个最简单的剪枝题目就能79ms通过了。
再加上一个比较屌的剪枝就可以到16ms。
参考了网上的代码,很多份代码如出一辙。思想十分牛逼。



剪枝1:
所有木棍的长度必须能被4整除

剪枝2:
最长的木棍不能长于正方形的边长

这两个是最容易想到的,用上这两个可以79ms通过

剪枝3:
同样长度的木棍的同样数量的组合只搜索一次。
这个剪枝需要将木棍从大到小排列,在搜索的时候加一句代码就行了,代码很巧妙。
由于数据问题,这个剪枝貌似不管用。

剪枝4:
每条边的木棍按照从大到小的顺序拼接
如果某条木棍不能够作为某边的第一条木棍,那比它短的也不行
想一想还是可以理解,后面的边始终会用到这条长的棍子,那时候可供选择的木棍更少
所以在前面的边拼不成,在后面的边更加拼不成
这个剪枝非常牛逼,不知道谁想出来的,代码只需要一句。太牛逼了。
由于数据的N比较小,这个剪枝相当管用。

无法实现的理想化剪枝:
如果在枚举中,发现用一条长的棍子枚举失败,那么几条短的棍子组成同样长度的棍子也必然不用试验。
这个很理想,但是实现的代价很大。

#include <stdio.h>
#include 
<stdlib.h>
#include 
<string.h>

// 1+2 : 79ms
// 1+2+3 : 79ms
// 1+2+4 : 16ms
// 1+2+3+4: 16ms

int part, N;
int len[128];
char used[128];

int cmp(const void *a, const void *b)
{
    
return *(int *)b - *(int *)a;
}


int dfs(int side, int start, int left)
{
    
int i;

    
if (!left) {
        left 
= part;
        side
++;
        start 
= 0;
    }


    
if (side == 4)
        
return 1;

    
for (i = start; i < N; i++{
        
if (len[i] > left)
            
continue;
        
if (used[i])
            
continue;

        
// 3
        if (i && !used[i - 1&& len[i] == len[i - 1])
            
continue;

        used[i] 
= 1;
        
if (dfs(side, i + 1, left - len[i]))
            
return 1;
        used[i] 
= 0;

        
// 4
        if (left == part)
            
return 0;
    }


    
return 0;
}


int solve()
{
    
int i, sum;

    scanf(
"%d"&N);
    sum 
= 0;
    
for (i = 0; i < N; i++{
        scanf(
"%d"&len[i]);
        sum 
+= len[i];
    }


    
// 1
    if (sum % 4)
        
return 0;

    part 
= sum / 4;
    qsort(len, N, 
sizeof(len[0]), cmp);
    
// 2
    if (len[0> part)
        
return 0;

    memset(used, 
0, N);
    
return dfs(00, part);
}


int main()
{
    
int t;

    scanf(
"%d"&t);
    
while (t--
        printf(
"%s\n", solve() ? "yes" : "no");

    
return 0;
}




posted @ 2010-10-04 13:08 糯米 阅读(960) | 评论 (0)编辑 收藏

POJ 1920 Towers of Hanoi 汉诺塔问题

题目大意:
给出一个玩到一半的汉诺塔。叫你把所有的盘子归到任意一根针上,求最少步数。

由于结果很大,输出它和100000的模。

最基本的思路是动态规划,跟一般的汉诺塔问题类似
位于任意一个状态的汉诺塔。必然有位于最底层的最大的盘子。
最终的目的是把这个盘子移动到某根针上。
所以必然会出现的一个场景就是:
最大的盘子单独在一根针上,其他的盘子在另外一根针上

假设:
f(n, idx) = { 将初始状态下盘子 1~n 集中到第idx根针上所需要的最小步数 }
g(n) = { 将位于一根针上的盘子 1~n 移动到另外一根针所需要的最小步数 }

那么转移方程就是:
f(n, idx) = {
如果盘子n位于idx:f(n - 1, idx)
否则:f(n - 1, idx)
}

从普通的汉诺塔问题上可以得出:
g(n) = 2^n - 1

posted @ 2010-09-27 14:58 糯米 阅读(787) | 评论 (0)编辑 收藏

POJ 1934 Trip 打印出所有的最长公共子序列

这题很难,我只写出了一个TLE的版本。
后来找了解题报告,只找到了一个,就是这位alpc43大牛的版本:
http://hi.baidu.com/alpc43/blog/item/95184e03a5fef4e209fa932d.html

这个代码很牛逼!
它的思路是:

1)首先按照常规的方法求出最长公共子序列的长度
也就是用O(MN)的那个动态规划,结果放在二维数组dp里
dp[i][j] = { 字串a的1~i部分与字串b的1~j部分的最长公共子序列的长度 }
2)求辅助数组
last1[i][j] = { 到下标i为止,字符j在字串a中最后一次出现的下标 }
last2[i][j] = { 到下标i为止,字符j在字串b中最后一次出现的下标 }
3)枚举最长公共字串的每一个字符
从最后一个字符开始枚举
比如说现在枚举最后一个字符是'C'的情况。
那么 'CDCD' 与 'FUCKC' 这两个字串。
一共有 (0, 2) (0, 4)  (2, 2)  (2. 4) 这四种可能。
很明显前三个是可以舍弃的,因为第四个优于前三个,为后续的枚举提供了更大的空间。
last数组正好是用来做这个的。
4)排序输出
代码里用了stl的set。
注意,由于刚刚的枚举过程是针对每个字符,所以是不用判重的。

这个思路非常之牛逼!

#include <stdio.h>
#include 
<string.h>
#include 
<math.h>
#include 
<string>
#include 
<set>;

using namespace std;

const int MAXLEN=100;
char s1[MAXLEN];
char s2[MAXLEN];
int len1,len2;
int dp[MAXLEN][MAXLEN];
int last1[MAXLEN][27];
int last2[MAXLEN][27];
int longest;
char temp[MAXLEN];
set<string> SET;
void input()
{
    scanf(
"%s %s",&s1[1],&s2[1]);

}

inline 
int maxab(int a,int b)
{
    
if(a>b) return a;
    
return b;
}


inline 
void find(int x,int y,int len)
{
    
if(len<=0)
    
{
        
//printf("%s\n",&temp[1]);
        SET.insert(&temp[1]);
        
return ;
    }

    
int i,j;
    
if(x>0 && y>0)
    
{
        
for(i=0;i<26;i++)
        
{
            
int t1=last1[x][i];
            
int t2=last2[y][i];
            
if(dp[t1][t2]==len)
            
{
                temp[len]
='a'+i;
                find(t1
-1,t2-1,len-1);
            }

        }

    }

}

void solve()
{
    
int i,j,k;
    len1
=strlen(&s1[1]);
    len2
=strlen(&s2[1]);
    
for(i=0;i<=len1;i++)
        dp[i][
0]=0;
    
for(i=0;i<=len2;i++)
        dp[
0][i]=0;
    
for(i=1;i<=len1;i++)
        
for(j=1;j<=len2;j++)
        
{
            
if(s1[i]==s2[j])
                dp[i][j]
=dp[i-1][j-1]+1;
            
else dp[i][j]=maxab(dp[i-1][j],dp[i][j-1]);
        }

        longest
=dp[len1][len2];

        
for(j=0;j<26;j++)
            
for(i=0;i<=len1;i++)
                last1[i][j]
=0;
        
for(j=0;j<26;j++)
            
for(i=0;i<=len2;i++)
                last2[i][j]
=0;
        
for(i=1;i<=len1;i++)
        
{
            
for(j=0;j<26;j++)
            
{
                
if(s1[i]=='a'+j)
                    last1[i][j]
=i;
                
else last1[i][j]=last1[i-1][j];
            }

        }

        
for(i=1;i<=len2;i++)
        
{
            
for(j=0;j<26;j++)
            
{
                
if(s2[i]=='a'+j)
                    last2[i][j]
=i;
                
else last2[i][j]=last2[i-1][j];
            }

        }

        temp[longest
+1]='\0';
        find(len1,len2,longest);
        
set<string>::iterator it;
        
for(it=SET.begin();it!=SET.end();it++)
        
{
            printf(
"%s\n",(*it).c_str());
        }

}

int main()
{
    freopen(
"e:\\in.txt""r", stdin);

    input();
    solve();
    
return 0;
}

posted @ 2010-09-27 14:30 糯米 阅读(1939) | 评论 (0)编辑 收藏

别问这有什么用---蔡康永

大学毕业时,爸说:“你一定要念一个硕士学位。不用念博士,可是硕士是一定要的。” 

为什么“硕士是一定要的”?我没问。爸爸对我的要求非常少,所以一旦他开口了,我都很“上道”的照单全收,当然,也因为硕士大都很容易念,选个容易的科目,常常可以在九个月内就拿到硕士。 

博士就麻烦得多,要是不幸遇上贪图廉价人工的指导教授,想把研究生一直留在身边帮忙,那一个博士学位耗掉你十年以上,也是常有的事。 
所以我就很安然的接受了爸的指示。 

“没问题,一个硕士。”我很有精神的覆诵一次,好像柜台后的日本料理师傅。 

“而且要念一流的学校。”爸进行第二阶段的指示。 

“没问题,一流学校。”师傅覆诵客人点的第二道菜。 

我当然很同意“念一流学校”的想法。我在大学四年,整天听我有学问的好友阿笔,不断告诉我西方最厉害的几间大学,到底都厉害在什么地方:柏克莱待了多少个得过诺贝尔奖的物理学家、约翰霍普金斯大学的医学院又完成了什么手术、德国的法学博士和美国的有何不同、牛津的研究生吃晚饭时要穿什么、康乃尔的研究生为什么自杀比例最高……聊的都是这一类的事情。 

对于在台湾各种烂学校混了十几年的我们来说,没事就把这些知识神殿的名字,在牙齿之间盘弄一番,实在是个方便又悲伤的娱乐。 
就像两个台湾的初中男生,翻看着“花花公子”杂志拉页上的金发兔女郎。夹杂着向往和民族的自卑。 

爸对学位的指示,已经清楚收到。“一流学校、硕士就好”。 

轮到我对爸开出条件了。 

有风格的料理师傅,是不会任凭客人想点什么、就做什么的。客人可以要求吃生鱼片,可是有风格的师夫,会决定此刻最适合做生鱼片的,是哪一种鱼。也就是说,你点归你点,未必吃得到。 

“爸,我只念我想念的东西喔。” 

“可以,不要念太多就好。” 

爽快。这是爸跟我随着岁月培养出来的默契。各取所需,互蒙其利。 


不过,老实说,“我取我需”的状况,似乎比“爸取爸需”的状况,要多那么一两百次吧。 

我想念的东西,对一般的台湾爸妈来说,似乎有点怪。 

我想学“舞台剧”。 

还好我爸不是“一般的台湾爸妈”。 

从小到大,爸从来没问过我:“这有什么用?” 

“这有什么用?”几乎是我们这个岛上,最受欢迎的一个问题。每个人都好像上好发条的娃娃,你只要拍他的后脑一下,他就理直气壮的问:“这有什么用?” 

“我想学舞台剧。”“这有什么用?” 

“我正在读《追忆似水年华》。”“这有什么用?” 

“我会弹巴哈了。”“这有什么用?” 

“我会辨认楝树了。”“这有什么用?” 

这是我最不习惯回答的问题,因为我没被我爸问过这个问题。 

从小,我就眼睁睁看着爸妈做很多“一点用也没有”的事情。爸买回家里一件又一件动不动就摔破的瓷器水晶;妈叫裁缝来家里量制一件又一件繁复的旗袍;一桌又一桌吃完就没有的大菜;一圈又一圈堆倒又砌好的麻将,从来没有半个人会问:“这有什么用?” 

“漂不漂亮?”“喜不喜欢?”“好不好吃?”这些才是整天会被问到的问题。 

长大以后,越来越常被别人问:“这有什么用?”才忽然领悟很多人,是随着这个问题一起长大的。 

我不大确定——这是不是值得庆幸的事。一直到,反复确认了“人生最重要的东西,其实都没有什么用”时,才觉得自己运气真好。 

人生,并不是拿来用的。 

爱情,光荣,正义,尊严,文明,这些一再在灰黯时刻拯救我、安慰我的力量,对很多人来讲“没有用”,我却坚持相信这才都是人生的珍宝,才禁得起反复追求。

posted @ 2010-09-25 09:13 糯米 阅读(352) | 评论 (0)编辑 收藏

Dinic 算法模板

Dinic算法是一种比较容易实现的,相对比较快的最大流算法。
今天看了一下它的原理,发现的确很牛逼。

求最大流的本质,就是不停的寻找增广路径。直到找不到增广路径为止。
对于这个一般性的过程,Dinic算法的优化如下:

(1)
Dinic算法首先对图进行一次BFS,然后在BFS生成的层次图中进行多次DFS。
层次图的意思就是,只有在BFS树中深度相差1的节点才是连接的。
这就切断了原有的图中的许多不必要的连接。很牛逼!
这是需要证明的,估计证明也很复杂。

(2)
除此之外,每次DFS完后,会找到路径中容量最小的一条边。
在这条边之前的路径的容量是大于等于这条边的容量的。
那么从这条边之前的点,可能引发出别的增广路径。
比如说 S -> b -> c -> d -> T 是一条增广路径,容量最小的边是 b -> c。
可能存在一条 S -> b -> e -> f -> g -> T 这样的增广路径。
这样的话,在找到第一条增广路径后,只需要回溯到 b 点,就可以继续找下去了。
这样做的好处是,避免了找到一条路径就从头开始寻找另外一条的开销。
也就是再次从 S 寻找到 b 的开销。
这个过程看似复杂,但是代码实现起来很优雅,因为它的本质就是回溯!


(3)
在同一次 DFS 中。如果从一个点引发不出任何的增广路径,就将这个点在层次图中抹去。

而这样一个算法,实现起来居然只需要100行。太吊了。
我的代码是参考别人的代码写的。可以用 POJ 1273 测试。

#include <stdio.h>
#include 
<stdlib.h>
#include 
<string.h>
#include 
<limits.h>

#define MAX_VETXS 1024
#define MAX_EDGES 1024

int E[MAX_EDGES], next[MAX_EDGES], C[MAX_EDGES], M;
int V[MAX_VETXS], L[MAX_VETXS], Q[MAX_VETXS], N;
int S, T;

void __insert(int from, int to, int cap)
{
    M
++;
    C[M] 
= cap;
    E[M] 
= to;
    next[M] 
= V[from];
    V[from] 
= M;
}


void insert(int from, int to, int cap)
{
    __insert(from, to, cap);
    __insert(to, from, 
0);
}


int bfs()
{
    
int h, t, e, u, v;

    h 
= t = 0;
    Q[t
++= S;
    memset(L, 
0, N*sizeof(L[0]));
    L[S] 
= 1;
    
while (h != t) {
        u 
= Q[h++];
        
for (e = V[u]; e; e = next[e]) {
            v 
= E[e];
            
if (!L[v] && C[e] > 0{
                L[v] 
= L[u] + 1;
                Q[t
++= v;
            }

        }

    }


    
return L[T];
}


int dfs()
{
    
int t, u, v, e, i, f, r, back;

    t 
= 1;
    r 
= 0;

    
while (t) {
        u 
= (t == 1? S : E[Q[t - 1]];
        
if (u == T) {
            f 
= INT_MAX;
            
for (i = 1; i < t; i++{
                e 
= Q[i];
                
if (C[e] < f) {
                    f 
= C[e];
                    back 
= i;
                }

            }

            
for (i = 1; i < t; i++{
                e 
= Q[i];
                C[e] 
-= f;
                C[e
^1+= f;
            }

            r 
+= f;
            t 
= back;
        }
 else {
            
for (e = V[u]; e; e = next[e]) {
                v 
= E[e];
                
if (L[v] == L[u] + 1 && C[e] > 0)
                    
break;
            }

            
if (e)
                Q[t
++= e;
            
else {
                t
--;
                L[u] 
= 0;
            }

        }

    }


    
return r;
}


int dinic()
{
    
int f = 0;

    
while (bfs())
        f 
+= dfs();

    
return f;
}


int main()
{
    
int n, m, a, b, c, i;

    freopen(
"d:\\in.txt""r", stdin);

    
while (scanf("%d%d"&n, &m) != EOF) {
        S 
= 0;
        T 
= m - 1;
        N 
= m;
        memset(V, 
0, N*sizeof(V[0]));
        M 
= 2;
        
for (i = 0; i < n; i++{
            scanf(
"%d%d%d"&a, &b, &c);
            insert(a 
- 1, b - 1, c);
        }

        printf(
"%d\n", dinic());
    }


    
return 0;
}


posted @ 2010-08-12 14:33 糯米 阅读(2496) | 评论 (4)编辑 收藏

[bash源码分析] 4 语法分析 - 后台运行、管道、重定向


语法分析 - 后台运行、管道、重定向

--- 后台运行
    我们从上一节提到的入口点 inputunit 看起。

inputunit:    simple_list simple_list_terminator
    ...
    ;

simple_list:    simple_list1
    |    simple_list1 '&'
    |    simple_list1 ';'
    ;

simple_list1:    simple_list1 AND_AND newline_list simple_list1
    |    simple_list1 OR_OR newline_list simple_list1
    |    simple_list1 '&' simple_list1
    |    simple_list1 ';' simple_list1
    |    pipeline_command
    ;

    这几句语法的功能,就是平时很常用的:
    check_ok && do_sth
    file_exists || create_it
    firefox &
    do_a; do_b; do_c; do_d

--- 管道
    来看一下 pipe_command

pipeline_command: pipeline
    |    BANG pipeline
    ...
    ;

pipeline:
        pipeline '|' newline_list pipeline
    |    command
    ;

newline_list:
    |    newline_list '\n'
    ;

    BANG 对应的符号是 '!'
    这里把 BANG 和 pipeline 放到一起并不是说明 '!' 和管道有什么关系。
    只是在这里实现 '!' 这个符号的功能而已。


--- command_connect()
    我们注意到,在语法的处理函数中,command_connect 这个函数被经常使用。

COMMAND *
command_connect (com1, com2, connector)
     COMMAND *com1, *com2;
     int connector;
{
  CONNECTION *temp;

  temp = (CONNECTION *)xmalloc (sizeof (CONNECTION));
  temp->connector = connector;
  temp->first = com1;
  temp->second = com2;
  return (make_command (cm_connection, (SIMPLE_COM *)temp));
}
    这个函数的作用就是把两个相关的语法树节点连接起来,并构成一个新的节点。
    而 COMMAND 这个数据结构,里面就包含了指向两个孩子的指针,以及跟连接相关的属性。
    这里我们先不去详细的看它。

--- 重定向
    从 pipeline 引出了 command 。

command:    simple_command
    |    shell_command
    |    shell_command redirection_list
            {
              COMMAND *tc;

              tc = $1;
              if (tc->redirects)
                {
                  register REDIRECT *t;
                  for (t = tc->redirects; t->next; t = t->next)
                ;
                  t->next = $2;
                }
              else
                tc->redirects = $2;
              $$ = $1;
            }
    |    function_def
    ;

redirection_list: redirection
    |    redirection_list redirection
    ;


    这个项应该就是传说中的,单项命令的实体了。
    我们暂时不去理会其他的东西,先看一看 redirection_list。
    那一段处理函数可以看出,它把一系列的重定向操作加入到 shell_command 的 redirects 链表尾部。
    而 redirection_list 包含的内容就比较多了,也就是重定向的所有语法啦。

redirection:    '>' WORD    // > xxx
    |    '<' WORD    // < xxx
    |    NUMBER '>' WORD        // 1> xxx
    |    NUMBER '<' WORD        // 0< xxx
    |    GREATER_GREATER WORD    // >> xxx
    |    NUMBER GREATER_GREATER WORD        // 2>> xxx
    |    LESS_LESS WORD        // << xxx
    |    NUMBER LESS_LESS WORD    // 0<< xxx
    |    LESS_LESS_LESS WORD        // <<< xxx
    |    NUMBER LESS_LESS_LESS WORD    // 0<<< xxx
    |    LESS_AND NUMBER        // <&2
    |    NUMBER LESS_AND NUMBER    // 1<&2
    |    GREATER_AND NUMBER    // >&1
    |    NUMBER GREATER_AND NUMBER    // 2>&1
    |    LESS_AND WORD    // <& xxx
    |    NUMBER LESS_AND WORD    // 1<& xxx
    |    GREATER_AND WORD    // >& xxx
    |    NUMBER GREATER_AND WORD        // 1>& xxx
    |    LESS_LESS_MINUS WORD    // <<- xxx
    |    NUMBER LESS_LESS_MINUS WORD        // 1 <<- xxx
    |    GREATER_AND '-'        // >&-
    |    NUMBER GREATER_AND '-'    // 1>&-
    |    LESS_AND '-'    // <&-
    |    NUMBER LESS_AND '-'        // 1<&-
    |    AND_GREATER WORD    // &> xxx
    |    NUMBER LESS_GREATER WORD    // 1<> xxx
    |    LESS_GREATER WORD    // <> xxx
    |    GREATER_BAR WORD    // >| xxx
    |    NUMBER GREATER_BAR WORD        // 1>| xxx
    ;

    可见,真的是十分之多阿,每一条后面我都加了注释。
    平时常用的基本只有几种了,有一部分是《bash高级编程》里面提到的,
    有些就是根本没提到,完全没见过的用法。。
    现在我们先不去深究这些用法。
   



posted @ 2010-07-25 10:20 糯米 阅读(891) | 评论 (0)编辑 收藏

[bash源码分析] 3 语法分析 - 入口点


语法分析 - 入口点


--- main()
    我们打开shell.c的main函数,大概300来行,其主题都是围绕这xxx_init,做各种初始化操作。
    我们可以略过不看,等遇到问题的时候再说。把目光放到最后一句 reader_loop()。这是一个循环读
    入并执行命令的函数。

--- reader_loop()
    位于eval.c的reader_loop()函数,其中仿佛只有调用read_command()是重点。

--- read_command()
    同样位于eval.c的read_command()函数。一开始那一段ALARM信号的处理让人觉得很费解,难道
    在bash输入命令还要有时间限制吗?无论如何,这种看似偏门的、非关键性的东西,在代码分析的初期
    是不能理会的,如果太深究这些东西,没有把握代码的主线,则会走入死胡同,而且会失去源码分析
    的乐趣。
    代码主线走入parse_command()函数。

--- parse_command()
    同样位于eval.c的parse_command()函数。它调用的yyparse()函数是语法分析的开始。
    用过yacc的人很明白这一点了。一开始我们看到文件列表中有y.tab.c这样的文件,就能意识到bash也是
    利用yacc生成的代码来完成语法分析的。

--- Yacc的作用
    你只要告诉yacc三样东西:语法、每一条语法的处理函数、负责词法分析的函数
    yacc就会为你生成y.tab.c文件,只要调用这个文件中的yyparse()函数,就可以完成编译器的
    词法分析和语法分析的部分了。在分析的过程中,你刚刚指定的每一条语法对应的处理函数也会
    被调用。关于yacc的具体介绍,可以在网上搜搜,很多的。

    例子:
    告诉yacc:语法和对应的处理函数。
    expr : expr '+' expr { $$ = add($1, $3) }
         | expr '*' expr { $$ = mul($1, $3) }
         | expr '-' expr { $$ = sub($1, $3) }
         | NUMBER
          ;
    调用yyparse(),输入 1 + 2
    add(1, 2) 就会被回调了
    在处理函数中 $$ 代表着处理函数的返回值
    $1 代表着该条语法中的第一个元素(expr)
    $2 代表着该条语法中的第二个元素('+')
    $3 代表着该条语法中的第三个元素(expr)
    至于说这些元素的类型,则会在前面定义。比如 %type<char *> expr 之类。
    具体的还是找篇文章看看吧。

--- parse.y
    观察Makefile可以发现:
    y.tab.c y.tab.h: parse.y
        $(YACC) -d $(srcdir)/parse.y
    y.tab.c是由parse.y生成的。而parse.y中包含了语法和对应的处理函数,它是语法分析的核心文件。

    首先是一个%union定义
    %union {
        WORD_DESC *word;        /* the word that we read. */
        int number;            /* the number that we read. */
        WORD_LIST *word_list;
        COMMAND *command;
        REDIRECT *redirect;
        ELEMENT element;
        PATTERN_LIST *pattern;
    }

    然后是一系列的token定义:

/* Reserved words.  Members of the first group are only recognized
   in the case that they are preceded by a list_terminator.  Members
   of the second group are for [[...]] commands.  Members of the
   third group are recognized only under special circumstances. */
%token IF THEN ELSE ELIF FI CASE ESAC FOR SELECT WHILE UNTIL DO DONE FUNCTION
%token COND_START COND_END COND_ERROR
%token IN BANG TIME TIMEOPT

/* More general tokens. yylex () knows how to make these. */
%token <word> WORD ASSIGNMENT_WORD
%token <number> NUMBER
%token <word_list> ARITH_CMD ARITH_FOR_EXPRS
%token <command> COND_CMD
%token AND_AND OR_OR GREATER_GREATER LESS_LESS LESS_AND LESS_LESS_LESS
%token GREATER_AND SEMI_SEMI LESS_LESS_MINUS AND_GREATER LESS_GREATER
%token GREATER_BAR

    读入字符串流,返回token是词法分析函数的责任。
    以%token定义,表明返回值是int类型
    以%token <word>定义,表明返回值是%union中对应的类型

    词法分析函数是lex生成的,但这个工程好像把原始的
    .lex文件删除了。我们只能看到生成后的yylex()函数。
    但有一个表,可以看出token对应的字串内容:

/* Reserved words.  These are only recognized as the first word of a
   command. */
STRING_INT_ALIST word_token_alist[] = {
  { "if", IF },
  { "then", THEN },
  { "else", ELSE },
  { "elif", ELIF },
  { "fi", FI },
  { "case", CASE },
  { "esac", ESAC },
  { "for", FOR },
#if defined (SELECT_COMMAND)
  { "select", SELECT },
#endif
  { "while", WHILE },
  { "until", UNTIL },
  { "do", DO },
  { "done", DONE },
  { "in", IN },
  { "function", FUNCTION },
#if defined (COMMAND_TIMING)
  { "time", TIME },
#endif
  { "{", '{' },
  { "}", '}' },
  { "!", BANG },
#if defined (COND_COMMAND)
  { "[[", COND_START },
  { "]]", COND_END },
#endif
  { (char *)NULL, 0}
};

/* other tokens that can be returned by read_token() */
STRING_INT_ALIST other_token_alist[] = {
  /* Multiple-character tokens with special values */
  { "-p", TIMEOPT },
  { "&&", AND_AND },
  { "||", OR_OR },
  { ">>", GREATER_GREATER },
  { "<<", LESS_LESS },
  { "<&", LESS_AND },
  { ">&", GREATER_AND },
  { ";;", SEMI_SEMI },
  { "<<-", LESS_LESS_MINUS },
  { "<<<", LESS_LESS_LESS },
  { "&>", AND_GREATER },
  { "<>", LESS_GREATER },
  { ">|", GREATER_BAR },
  { "EOF", yacc_EOF },
  /* Tokens whose value is the character itself */
  { ">", '>' },
  { "<", '<' },
  { "-", '-' },
  { "{", '{' },
  { "}", '}' },
  { ";", ';' },
  { "(", '(' },
  { ")", ')' },
  { "|", '|' },
  { "&", '&' },
  { "newline", '\n' },
  { (char *)NULL, 0}
};

/* others not listed here:
    WORD            look at yylval.word
    ASSIGNMENT_WORD        look at yylval.word
    NUMBER            look at yylval.number
    ARITH_CMD        look at yylval.word_list
    ARITH_FOR_EXPRS        look at yylval.word_list
    COND_CMD        look at yylval.command
*/

    这些token在语法中会遇到的。

    接下来是对语法中每一项内容(编译原理没学好,不知道这个术语叫什么。。)的定义:

/* The types that the various syntactical units return. */

%type <command> inputunit command pipeline pipeline_command
%type <command> list list0 list1 compound_list simple_list simple_list1
%type <command> simple_command shell_command
%type <command> for_command select_command case_command group_command
%type <command> arith_command
%type <command> cond_command
%type <command> arith_for_command
%type <command> function_def function_body if_command elif_clause subshell
%type <redirect> redirection redirection_list
%type <element> simple_command_element
%type <word_list> word_list pattern
%type <pattern> pattern_list case_clause_sequence case_clause
%type <number> timespec
%type <number> list_terminator

%start inputunit

    从名字上来看,大概能知道是作什么的。
    %start 表示整个语法分析的入口是 inputunit 那一项。
    接着就是语法了,内容就比较多,不直接贴了。
    语法是我比较感兴趣的地方,无论看哪本关于bash的书,都不如看代码来的直接,呵呵。
    我们以后慢慢看。





posted @ 2010-07-25 10:19 糯米 阅读(1165) | 评论 (0)编辑 收藏

仅列出标题
共17页: 1 2 3 4 5 6 7 8 9 Last