實作時碰到了一個問題,
這個問題可以獨立出來成為一個有趣的題目。
[問題描述]
假設有一段程式碼如下,
而我們想要知道某行位在哪個(最內層的)class裡面。 (假設所有的class名稱都不重複)
例如第7行在class C裡面,第11行在class A裡面。
(雖然第7行也同時位在class B與class A裡,但我們要找的是最內層的。)
// ......
class A {
class B {
class C {
}
}
}
class D {
}
這個問題等同於把每個class擺在一條正整數X軸上,每個class的區間有起始與結束的值。
給一個正整數N,我們想找出N位在哪個區間裡。
A B C C B A D D 2 4 6 8 10 12 15 17在看參考答案之前,
大家可以先自己想一下這個問題應該怎麼解決。
[正式題目I/O]
(前情提要如上所述)
<input>
line 1: 有N個class
line 2~N+1: 每行包括class的名稱、起始位置與結束位置
line N+2~eof: 每行包括要測試的行數X
<output>
X位在哪個區間之內,
若不位於任何區間則沒有任何輸出。
(每一行輸出之後都需要換行)
<範例input>
4 A 2 12 B 4 10 C 6 8 D 15 17 3 7 9 13 16<範例output>
A C B D
這邊我提供一個想法,
compiler在處理chain of function calls時,
是怎麼知道目前到第幾層了呢?
Ans: 「stack」
雖然stack是個很大的關鍵提示,
不過要實作起來並不會很簡單哦 XD
因為還是有一些case要考慮。
大概的想法是,
由上而下visit每個class,
每visit到一個class的開頭,就把他push到stack裡,
visit到class的結尾時,就把他pop掉。
由此一來,我們就可以保證stack頂端的class就是答案。
例如,visit到第3行時,此時stack頂端是A,
代表第3行就位在class A的範圍裡面。
visit到第7行時,此時stack頂端是C;
visit到第8行結束時,C被pop掉,
因此visit到第9行時,此時stack頂端是B,
代表第9行位於class B裡面。
以下是我用C++寫的參考解答。
#include <iostream>
#include <string>
#include <vector>
#include <stack>
using namespace std;
class Block {
public:
string name;
int start;
int end;
Block(string blockName, int start, int end) {
this->name = blockName;
this->start = start;
this->end = end;
}
};
class ScopeFinder {
private:
vector<Block> blockList;
public:
// 初始化blockList
void init() {
int numOfBlocks;
string blockName;
int start, end;
cin >> numOfBlocks;
while (numOfBlocks-- > 0) {
cin >> blockName >> start >> end;
blockList.push_back( Block(blockName,start,end) );
}
}
// 輸入一個整數,找出該整數位在哪個區間之內
string getBlockName(int num) {
stack<Block> blockStack;
// Iterate through blockList
for (vector<Block>::iterator it=blockList.begin(); it!=blockList.end(); it++) {
Block block = *it;
// 忽略所有num之前的blocks (不重要,反正就算留著之後還是會被pop掉)
if (num > block.end)
continue;
// num在某個block之前,可能有兩種case:
// case1: "A*B B A" 或 "A*A B B" (*是num)
// case2: "A A*B B" 或 "*A A" (case2的stack會是空的)
if (num < block.start)
return blockStack.empty() ? "" : blockStack.top().name;
// 把目前iteration的block push進stack
blockStack.push(block);
}
// iteration結束後,分成兩種case:
// case1: "A*A" 或 "A B*B A" 或 "A A B*B" (意思其實都一樣,num位在最後一個block之間)
// case2: "A A*" (case2的stack會是空的)
return blockStack.empty() ? "" : blockStack.top().name;
}
};
int main(int argc, char **argv) {
ScopeFinder scopeFinder;
scopeFinder.init();
int num;
while (cin >> num)
cout << scopeFinder.getBlockName(num) << endl;
return 0;
}
接著,來提一個進階版:
若X不位於任何class的scope之內,
則回傳最接近的class。
例如: X=13時回傳A,X=14時回傳D。
這邊我就不貼實作程式碼了,
大家可以自己想想看。
我這邊提供一個參考解法,
每次的iteration去記錄「在X之前、最靠近X且最外層的class」。
上面我貼的程式碼中,
有stack可能是空的情況,代表X不在任何class的scope之內。
在這些情況下,若還在iteration中,
就去比較X與記錄的class名稱以及目前iteration的class之間的距離;
若iteration已經結束了,就代表X位在所有class之下,
就直接回傳剛剛記錄的那個class即可。
沒有留言:
張貼留言