ラベル programming language theory の投稿を表示しています。 すべての投稿を表示
ラベル programming language theory の投稿を表示しています。 すべての投稿を表示

2008-02-03

演算式の解析と処理




オワターーーーーーーーーーーー!!



ヤターーーーーーーーーーーーーーーー!!




あと期限が迫ってるのは数学だけど、数学はもうほとんど終わったようなもんだから、

ここからは無謀にも発展問題に挑戦してみるテスト。
右結合性の解析とか若干無理ポ感が否めないが、でもとりあえず挑戦。
ってかコメントの書き込み具合が異常だなw。ちょっと整理しないと・・・




>>ふっくん
めちゃくちゃ遅レスになってスマンorz
テスト終わっても、今週は最終レポートやら英語の手続きとかあって
それなりに忙しいから無理だけど、来週か再来週あたりに飲みにいこうかと密かに画策中。。。
ふっくんは来週あたり都合のいい日とかある?
あれば、アトレティコ新年会でもやろうかと。

2008-01-16

Notation

あ~、思ったより教科書が頭に入ってないことにショックorz

とりあえず、帰ったら言語論のpdfを一から読み直そう。

課題に関しては、一応ここまでにしておいて、
あとは期末試験後に回そう。



最近、dragon ashの古いアルバム(Viva la Revolutionとか)にまた嵌まってしまった。


[必要な作業@memo]
【全般】
PDFの熟読

【関数toTokens】
・ノードが初期ノードかどうかを調べる関数or方法
・ノードを単純なstring型にする関数or方法

・トークン数列に文字列を加える方法
・トークン数列の文頭に付加する関数or方法
・トークン数列の末尾に付加する関数or方法

・ノードを演算子ノードかどうかを調べる関数or方法
・演算子ノードの最初の子を見つける方法or関数
・演算時ノードの残りの子ノード達を探す方法or関数

・開き丸括弧が必要とされているかどうかを見極める方法or関数(←多分ない)
・閉じ括弧が必要とされているかどうかを調べる方法or関数(←コレは多分無い)

【関数isParenthesisRequired】
・丸括弧が必要とされているかどうかを調べる方法or関数
・演算子の優先度と結合性を調べる関数
・↑の結果を比較する関数orアルゴリズム



[メソッド一覧]
・Infix.java :中置記法
protected void parse(final String[] tokens) throws InvalidExpressionException {
計算式であるトークンの数列を構文解析する。
加算とか減算とか平均の演算子と、乗算とか除算とかmodの演算子を解析する。
限りなくメインメソッドに近い。
* 引数tokensは計算式をトークン化した表現


private String[] getSubExpression(final String[] tokens, final int indexStart) {
* 丸括弧の中にあるトークンのサブ数列を取り戻す。
* 引数tokensとは、計算式のトークンの数列である。
* 引数indexStartとは丸括弧を探し始めるindexのことである。
* 丸括弧の中にあるトークンのサブ数列を返す。


private void setChildNodeAtFreeBranch(IOperatorNode nodeOp, INode node) throws InvalidExpressionException {
* どのノードも存在していない位置にある演算子ノードの子ノードとして、設置する。
* 引数nodeOpとは、演算子ノードのこと。
* 引数nodeとは、演算子ノードの子ノードになるはずのノードのこと。


private boolean isFilledOperator(final IOperatorNode nodeOp) {
* 演算子ノードが全ての子ノードを持っているかどうかを返す(まだ設置できるかどうかを確かめるため?)
* nodeOpとは、演算子ノードのことである。
* もしも、その演算子ノードが全ての子ノードを持っていたら、trueを返す。そうでなければ、falseを返す。


protected Collection toTokens(INode node) {
* 計算式の抽象構文ツリーからのトークンの訂正を返す。
* 引数nodeは計算式の抽象構文木の根ノードのことである。
* 計算式のトークンの訂正を返す。


//↓ココが必修訂正箇所
private Collection toTokens(INode node, IOperatorNode opParentNode) {
* 計算式の抽象構文木からトークンの訂正値を返す。
* 引数nodeは計算式の抽象構文木の根ノードを返す
* 引数opParentNodeとは、根ノードの親ノードとなる、演算子ノードのことである。
* 計算式のトークンの訂正値を返す。

// もし、そのノードが初期ノードだったら
// そのノードを単純にstring型にして、トークンの数列に文字列を加える。
// もし、そのノードが演算子ノードだったら、
// もし、開き丸括弧が必要とされていたら、開き丸括弧を最初のトークンの数列に追加して、
// 演算子ノードの最初の子を、トークンの数列に加えて、
// その後、演算子記号を、トークンの数列に追加して、
// そして、演算子ノードの子ノード達の残りを、トークンの文字列に加える。
// もし、閉じ括弧が必要とされていたら、トークンの末尾に閉じ括弧を加える。


//↓ココが必修訂正箇所
private boolean isParenthesisRequired(final IOperatorNode opCur, final IOperatorNode opChild) {
* 丸括弧が必要とされているかどうかを返す。
* 演算子の優先度と結合性を元にして、
* 演算子ノードとその子演算子ノードを比較して、
* 引数opCurとは、現在フォーカスされている演算子ノードのこと。
* 引数opChildとは、現在の演算子ノードの子ノードである、演算子ノードのこと。
* もし、子ノードのために丸括弧が必要とされていたらtrueを返す。そうでなければfalseを返す。


private boolean hasHigherPrecedenceThan(final IOperatorNode opNodeA, final IOperatorNode opNodeB) {
* 演算子が他の演算子より優位かどうかを返す
* 引数opNodeAは、現在フォーカスされている演算子ノードのこと。
* 引数opNodeBとは、現在の演算子ノードと比較する演算子ノードのこと。
* もし現在の演算子ノードがもう一方の演算子ノードより
高い優先度をもっていたらtrueを返す。そうでなければfalseを返す。


private boolean equalsPrecedence(final IOperatorNode opNodeA, final IOperatorNode opNodeB) {
* 演算子がもう一方の演算子と同じ優先度を持っているかどうかを返す。
* 引数opNodeAとは、現在フォーカスされている演算子ノードのこと
* 引数opNodeBとは、現在の演算子ノードと比較する演算子ノードのこと
* もし現在の演算子ノードがもう一方の演算子ノードと
同じ優先度をもっていたらtrueを返す。そうでなければfalseを返す。


private int getPrecedence(final IOperatorNode opNode) {
* 演算子の優先度を返す。
* 低い値は優先度が高いことを意味する。
* マイナスの値はエラーを返す。
* 引数opNodeとは、現在フォーカスされている演算子ノード。
* 演算子の優先度をint型の数値で返す(-1,0,1)多分?


private int getPrecedence(final int opID) {
* 演算子の優先度を返す。
* 低い値は優先度が高いことを意味する。
* マイナスの値はエラーを返す。
* 引数opNodeとは、現在フォーカスされている演算子ノード。
* 演算子の優先度をint型の数値で返す


private int getAssociativity(final IOperatorNode opNode) {
* 演算子の結合性を返す
* 負の値はエラーを表示する。
* 引数opNodeとは、現在フォーカスされている演算子ノード。
* int型の数値として、演算子の結合性を返す。


private int getAssociativity(final int precedence) {
* 演算子の結合性を返す。
* マイナスの値はエラーを表示させる。
* 引数opNodeとは、現在フォーカスされている演算子ノード。
* int型の数値として、演算子の結合性を返す。


public String getNotation() {
* 計算式のための表記法を返す。


protected boolean isSpaceSeparatorNeeded() {
* もし空白文字が、stringの表現をセパレータとして必要とする場合
* 計算式の表記法に向けたトークンの数列から生成された
* もしも空白文字が必要とされたらTRUEを返す、そうでなければFALSEを返す。


・IOperatorNode.java:演算子ノードのインターフェース

* 文字列として演算子記号を返す。
public String getOperator();

* 演算子の識別子(演算子ID)を返す
public int getOperatorID();

* 演算子のアリティを返す。
public int getArity();


・OperatorNode.java
private static int operatorString2operatorIndex(final String strOp) {

private static String operatorIndex2operatorString(final int index) {

/* package */
static boolean isOperator(final String strOp) {

private static int operatorID2operatorIndex(final int id) {

private static int operatorIndex2operatorID(final int index) {

private static int operatorString2operatorID(final String strOp) {

private static String operatorID2operatorString(final int id) {

private static int getArity(final int id) {

-Constructor-
protected OperatorNode(final int id) {

protected OperatorNode(final String strOp) {

-method-
*演算子のstring表現を返す
public String getOperator() {

*演算子の識別子を返す
public int getOperatorID() {

*子ノード達の数を返す
public int getNumberOfChildren() {


* indexの位置にある子ノードを返す。
* 引数indexとは、子ノードのindexのこと。
* indexの位置になる子ノードを返す。
public INode getChildNode(final int index) {


* indexの位置にある子ノードとして、そのノードをセットする。
* 引数indexとは、子ノードのindexのこと。
* 引数childNodeとは、indexの位置にセットされるノードのこと。
public void setChildNode(final int index, final INode childNode) {


* 子ノードのindexを返す。
* 引数nodeとは、子ノードとしてチェックされるノードのこと。
* もし、そのノードが子ノードであれば、indexの値を返す。そうでなければfalse(NOT_FOUND)を返す。
public int getChildIndex(final INode node) {


* このノードのコピーを作る。
* 引数bDeepとは、もしもtrueならば、deepcopyをつくり、
そうでなければshallowcopyを作る。
public INode cloneNode(final boolean bDeep) {


* このオブジェクトのコピーを生成する。
* コピーされたオブジェクトを返す。
protected Object clone() {


* ノードを評価して、評価した結果を返す。
public int evaluate() throws EvaluationException,InvalidVariableException {


* ノードを評価して、評価した結果を、環境と共に返す。
* 引数envとは、環境のこと
* 評価した結果をint型の数値で返す。
public int evaluate(final Environment env) throws EvaluationException,InvalidVariableException {


* このノードのストリング型の表現を返す。
public String toString() {




[演算子識別子]
0=no operator
1=addition
2=subtraction
3=multiplication
4=division




precedence
優先、優位

denote
~を意味する、示す、~を表示する、~の名称である

operator
演算子

arithmetical
演算の、算数の

arithmetic expression
計算式
r
associativity
結合性

identifier
識別子、一意名

sequence
数列、列

representation
表示、表現、説明

modulo
剰余演算子modのこと

token
しるし、証拠、表象
<コ>トークン信号、、優先権信号

recursively
再帰的に

whitespace
空白スペース

parse
構文解析をする

partial
部分的な、一部の

notation
表記法


[Arity]
アリティ (arity) とは、
関数や演算子に対し、それらが取る引数 (オペランド) の
個数を意味するのに、代数学、論理学、計算機科学などにおいて
用いられる用語である。

項数のような訳語が当てられる場合もあるが、
arity と英単語のまま用いられることも多い。

この用語は、ラテン語起源の英単語において単項の演算を
unary (operation), 2 項を binary, 3 項を ternary,
さらには一般に n 項を n-ary というように
接尾辞 -ary をつけた形容詞で引数の個数を表すことから来ている。


[Shallow copy]
オブジェクトをコピーする際に、
参照先をそのままにして、
そのオブジェクト自体だけをコピーすること。

反対に、
そのオブジェクトが参照しているオブジェクトまでコピーして、
参照 (ポインタ) を書き換えることを deep copy と言う。


[Deep copy]
オブジェクトをコピーする際に、
そのオブジェクトが参照しているオブジェクトまでコピーして、
参照 (ポインタ) を書き換えること。

反対に、
参照先をそのままにして対象オブジェクトのみを
コピーすることを shallow copy と言う。

2008-01-08

Call-by-Need, 関数ポインタについて

発展問題のCall-by-Needが完成!

しかし、今日はフーリエ懐石のマスマティカまで手が回らなかった。
やっぱ学校始まると、授業の文だけ課題をこなす時間が少なくなるからか。
それに授業に集中する分の体力も減ってしまうし。。。

残り一ヶ月の追い込みスケジュールに支障が無いように、そこらへんは要チェックしないとな。



[Call-by-Need]
Call-by-Nameを改良(?)してできた引数受渡機構。
Call-by-Nameが毎回借り引数が来るたびに式を評価するのと違い、
一度仮引数を評価したら、その評価値をその後の式に適応させていく。

>>外側の関数適用を先に評価
>>同じ式は1回だけ評価、結果を使い回す
>> let f x = x * x in f (5 + 3)
>>→ f (5 + 3)
>>→ (5 + 3) * (5 + 3) # 2つの (5+3) は同一
>>→ 8 * 8
>>→ 64
>> cf. “Haskell” (Call by need な関数型言語)

„ 利点
„ 計算能力が高い (call by name と同じ)
„ 1つの式の評価は1回で済む
„ 欠点
„ 実装が遅くなる
„ 式の評価タイミングは依然制御困難
„ Sharing があるのでさらに複雑に

(refer to the lectue "ML" Univ of Tokyo)

よって、例えば
int x=3, y=5, i;
for(i=0; i<100 ; i++) numlist[index] = index;
func(x, numlist[x*y]);
echo 「"numlist[X] != X"となっている部分を出力する」;

func(int a, int b){
a += b;
i++;
j++;
b += 30;
}

※処理系は脳内コンパイラ。

とすると、出力結果は
numlist[15] = 45
となる。



[関数ポインタについて]
関数も変数と同様に名前を持ち、その実体(命令列/データ)は
メモリ上に格納され、その格納領域を指し示すための参照が、
関数名/変数名に割り当てられる
「フォン・ノイマン・アーキテクチャ」において、
データと命令はどちらもメモリ上に格納されるバイト列になる。

したがって、関数/変数の実体の格納位置をポインタで
表現するCでは、関数のポインタを扱うことができる。
つまり、関数ポインタ値を代入できる変数が宣言できる

int型の変数mと、int型のデータへのポインタ変数
nの宣言
intを返す関数fと、intへのポインタを返す関数gと
intを返す関数へのポインタ変数hの宣言
int m;
int *n;
int f();
int *g();
int (*h)();

[reference: the Lecture of "PLT", Waseda University]

2008-01-07

Call-by-*

冬休みは今日で最後です。結局数学Bが…orz

まぁ期限はまだまだ先なんで、期限にさえ間に合えば別に評価が変わることはないんですが、

なんか自分に負けた気分だ...orz orz orz



というか、実際は各教科の予習復習も、電子回路/プログラミング言語論の過去問も

手を付けられなかったわけだし…駄目人間過ぎるわ。

さらなる時間圧縮をするしかない。





[前提]
#define NUMLISTSIZE 100
for (index = 0; index i = 5;
j = 6;
k = 3;
func(k, i, numlist[i*j]);


[Call-by-Value]
・実引数をcallerの環境で評価
・その評価結果のコピーを仮引数に代入してcalleeを実行
・仮引数の値が変更されても、対応する実引数には影 響しない
・大域変数など、引数で渡されないcalleeのスコープ外の変数を変更すれば、
callerの環境に影響する

func関数の引数には、各引数の値を呼び、別々の変数として扱う。
一般的な使い方。


[Call-by-Value/Result]
・実引数をcallerの環境で評価
・その評価結果のコピーを仮引数に代入してcalleeを実行
・calleeから復帰時に仮引数の値を実引数に代入
・仮引数の値が変更されると、対応する実引数に影響する
・復帰時におけるコピーは、
RHVの評価にはcalleeの環境を、
LHVの評価にはcallerの環境を用いる
ex.入力時にs[i*j(=3)]のとき
× s[3]=n
○ s[i*j]n
となる。また、×の復帰方法を、Call-by-Copyと呼ぶ。

*問題点
Call-by-Value/ResultはALGOL Wなどで採用されたが、
機構が複雑になるため処理系の実行効率が悪いため、
他の言語で採用されることはほとんどなかった

func関数の引数には、各引数の値を呼び、別々の変数として扱う。
ただし、func関数の動作後、引数から得た局所変数の結果を、引数に返す。

*func関数動作後の式の評価は、その瞬間の変数によって左右されることに注意する。
→cf. numlist[i*j] = Ncopy;


[Call-by-Reference]
・実引数をcallerの環境で評価
・その評価結果の参照(reference)を仮引数に代入してcalleeを実行
・仮引数の値が変更されると、対応する実引数に影響する

引数には変数のメモリ番地を呼び出して使う
→func関数内で引数が書き換えられた場合、そのメモリ番地を書き換えてしまう
→func関数に引数として与えた変数の値が変わる可能性がある。
cf.ポインタ


[Call-by-Name]
・実引数を評価せずに、式のまま仮引数に代入する
・実引数とともに、その実引数を評価するのに十分な、
callerの環境の部分集合も受け渡す
・式とその評価に用いる環境の組を、閉包(closure)と呼ぶ
・calleeの中で仮引数を評価するたびに、仮引数に代 入された実引数を、
閉包に与えられた環境を用いて評価する


printf("N=%d\n", (N));\
// N==numlist[i*j]
例えば、上式の場合、式が呼び出された瞬間のiとjの値が呼び出される。
このとき、i=11, j=6とすれば、numlist[11×6]=66が呼び出される。
つまり、call-by-Name → 呼び出される瞬間の変数を参照する使い方となる。

擬似言語について基本的な構造はcall-by-textに似ているが、
局所変数j_renameを用いるかどうかが違う。
call-by-name → j_renameを使う
call-by-text → jをそのまま使う


[Call-by-Copy]
・実引数の参照を取得
・参照に格納されている値のコピーを仮引数に代入してcalleeを実行
・calleeから復帰時に仮引数の値を取得した実引数の参照の格納領域に代入
・仮引数の値が変更されると、対応する実引数に影響する

funcの関数を行う前に事前に、各引数のメモリ番地をコピーする。
funcの関数内では別々の変数として扱う。
func関数が終わったら、各引数の値を適切なメモリ番地に格納する


[Call-by-Text]
・実引数を評価せずに、式のまま仮引数に代入する
・仮引数に代入された式(実引数)は、calleeの環境で
定義されている変数はcalleeの環境を用いて、
それ以外はcallerの環境を用いて評価する
・意味としては、以下のマクロ展開をすることと同じ
・callee中で仮引数が出現する箇所を実引数で置き換える

プリプロセッサで擬似関数として動かした関数。
実際にはfunc関数の引数は存在しない。
つまり、main関数でi,j,kに関する各式を書いているに過ぎない。


evaluate=評価する

*thunkについて
1.《コ》サンク◆16ビットのメモリアドレスを32ビットに変換すること。
またはその逆。転じて別アーキテクチャのコードを読み出すこと。
16ビットと32ビットのプログラムではメモリアドレスの振り方が異なるので、
アドレスの変換をして呼び出す必要がある。

2.メモリアドレス(ポインタ)を返す(引数を持たない)関数 T *thunk()
なぜ “thunk” という名称なのかは諸説ある
ある式が実引数として関数に与えられるとき、
その式 の参照(つまりLHV)が必要な場合、その式を使った
thunkを定義しておき、それを呼び出すことで、その 式の参照が取得できる
int *n_thunk() { return &(s[i*j]); }
thunkを定義し、その関数へのポインタを受け渡すことができれば、
閉包に関係する情報をcalleeに与えることができる


[方針]
1、reference, name, value/referenceを順に適用
2、値が正しいか確認する。
3、適宜必要ならば修正する。

[refer to the lecture of PLT ]

2008-01-05

2次元配列とインクリメントの順序関係


初めて知った。自分的には大発見。 さっそくメモ。

C言語で
以下の5×5の2次元配列src,dstに対して、
[src]
0 3 3 2 9
0 8 2 6 6
9 1 1 3 5
8 3 0 6 9
2 7 7 2 8

[dst]
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

以下の動作を行うと
int a,b
a=0, b=0
dst[++a][++b] = src[++a][++b]; ※1
a=2, b=2
dst[a][b] = 0
dst[++a][++b] = src[++a][++b]; ※2
a=4, b=4
dst[a][b] = 0

※1→ dst[1][1] = src[2][2]となる 
同様にして
※2→ dst[3][3] = src[4][4]となる 

式は、左から右に評価していくので、
インクリメント順番も評価の順番に従って行われる

よって、dstの内容は
0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
0 0 0 8 0
0 0 0 0 0
となる。