從零自制深度學(xué)習(xí)推理框架: 計算圖中的表達式講解(3)
我們提出這個例子是為了讓同學(xué)更加透徹的理解Expression Layer, 我們舉一個復(fù)雜點的例子:
add(mul(@0,@1),@2),我們將以人工分析的方式去還原詞法和語法分析的過程.
例子中的詞法分析我們將以上的這個輸入劃分為多個token,多個token分別為
add | left bracket| |mul|left bracket|@0|comma|@1|right bracket| @2 |right bracket
例子中的語法分析在ExpressionParser::Generate_函數(shù)對例子add(mul(@0,@1),@2),如下的列表為token 數(shù)組.
- add
- (
- mul
- (
- @0
- ,
- @1
- )
- ,
- @2
- )
index = 0, 當(dāng)前遇到的token為add, 調(diào)用層為1
index = 1, 根據(jù)以上的流程,我們期待add token之后的token為left bracket, 否則就報錯. 調(diào)用層為1
**開始遞歸調(diào)用,構(gòu)建add的左子樹.**從層1進入層2
index = 2, 遇到了mul token. 調(diào)用層為2.
index = 3, 根據(jù)以上的流程,我們期待mul token之后的token是第二個left bracket. 調(diào)用層為2.
開始遞歸調(diào)用用來構(gòu)建mul token的左子樹.
index = 4, 遇到@0,進入遞歸調(diào)用,進入層3, 但是因為操作數(shù)都是葉子節(jié)點,構(gòu)建好之后就直接返回了,得到mul token的左子節(jié)點.放在mul token的left 指針上.
index = 5, 我們希望遇到一個逗號,否則就報錯mul(@0,@1)中中間的逗號.調(diào)用層為2.
index = 6, 遇到@2,進入遞歸調(diào)用,進入層3, 但是因為操作數(shù)是葉子節(jié)點, 構(gòu)建好之后就直接返回到2,得到mul token的右子節(jié)點.
index = 7, 我們希望遇到一個右括號,就是mul(@1,@2)中的右括號.調(diào)用層為2.
到現(xiàn)在為止mul token已經(jīng)構(gòu)建完畢,返回形成add token的左子節(jié)點,add token的left指針指向構(gòu)建完畢的mul樹. 返回到調(diào)用層1.
...
add token開始構(gòu)建right token,但是因為@2是一個輸入操作數(shù),所以直接遞歸就返回了,至此得到add的右子樹,并用right指針指向.
所以構(gòu)建好的抽象語法樹如圖:
實驗部分需要完成test/tet_expression.cpp下的expression3函數(shù)
TEST(test_expression, expression3) {
using namespace kuiper_infer;
const std::string &statement = "add(@0,div(@1,@2))";
ExpressionParser parser(statement);
const auto &node_tokens = parser.Generate();
ShowNodes(node_tokens);
}
static void ShowNodes(const std::shared_ptr<kuiper_infer::TokenNode> &node) {
if (!node) {
return;
}
ShowNodes(node->left);
if (node->num_index < 0) {
if (node->num_index == -int(kuiper_infer::TokenType::TokenAdd)) {
LOG(INFO) << "ADD";
} else if (node->num_index == -int(kuiper_infer::TokenType::TokenMul)) {
LOG(INFO) << "MUL";
}
} else {
LOG(INFO) << "NUM: " << node->num_index;
}
ShowNodes(node->right);
}
TEST(test_expression, expression1) {
using namespace kuiper_infer;
const std::string &statement = "add(mul(@0,@1),@2)";
ExpressionParser parser(statement);
const auto &node_tokens = parser.Generate();
ShowNodes(node_tokens);
}
最后會打印抽象語法樹的中序遍歷:
Could not create logging file: No such file or directory
COULD NOT CREATE A LOGGINGFILE 20230115-223854.21496!I20230115 22:38:54.863226 21496 test_main.cpp:13] Start test...
I20230115 22:38:54.863480 21496 test_expression.cpp:23] NUM: 0
I20230115 22:38:54.863488 21496 test_expression.cpp:20] MUL
I20230115 22:38:54.863492 21496 test_expression.cpp:23] NUM: 1
I20230115 22:38:54.863497 21496 test_expression.cpp:18] ADD
I20230115 22:38:54.863502 21496 test_expression.cpp:23] NUM: 2
如果語句是一個更復(fù)雜的表達式 add(mul(@0,@1),mul(@2,@3))
image-20230115224350088
我們的單元測試輸出為:
I20230115 22:48:22.086627 23767 test_expression.cpp:23] NUM: 0
I20230115 22:48:22.086635 23767 test_expression.cpp:20] MUL
I20230115 22:48:22.086639 23767 test_expression.cpp:23] NUM: 1
I20230115 22:48:22.086644 23767 test_expression.cpp:18] ADD
I20230115 22:48:22.086649 23767 test_expression.cpp:23] NUM: 2
I20230115 22:48:22.086653 23767 test_expression.cpp:20] MUL
I20230115 22:48:22.086658 23767 test_expression.cpp:23] NUM: 3
*博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點,如有侵權(quán)請聯(lián)系工作人員刪除。