揭秘你處理數據的「底層邏輯」,詳解公式引擎計算(一)

背景

身處資訊時代之中,我們最能明顯感受到的一點就是密集數據大量爆發,人們積累的數據也越來越多。這些龐雜的數據出現在一起,傳統使用的很多數據記錄、查詢、匯總工具並不能滿足人們的需求。更有效的將這些大量數據處理,讓電腦聽懂人類需要的數據效果,從而形成更加自動化、智慧的數據處理方式。

為了處理這些海量數據,出現了各種大數據引擎、搜索引擎、計算引擎、3D引擎等,用以更好解決數據龐雜帶來人工無法處理的問題。而作為其中比較基礎的計算公式引擎,是在計算程式中負責對數據進行處理的核心部分。接下來我們將展開介紹計算引擎的基本原理、計算鏈和非同步函數構成,並從計算公式引擎的基本概念出發,用我們的表格電子組件作為例子,為大家演示這些內容如何在JavaScript中實現。

公式引擎的計算原理

計算引擎負責解決數據來源的統計,數據的操作,數據的管理,並將合適的計算結果按照要求給予返回。針對數據處理的目的不同,需要返回的內容不同,也有很對多應不同的類別。

為了實現讓電腦更好的識別我們需要的處理操作,需要進過編譯的過程,將我們書寫的語言翻譯成機器可以識別的語言。

而整個編譯階段的流程,按照過程劃分是按照下圖進行的:

其中比較關鍵的兩個環節是詞法分析、語法分析的過程,在這兩部分會將我們的輸入逐漸拆分,轉化為程式能夠識別的內容。

輸入內容後,編譯器先對內容進行詞法分析,在這一步編譯器的任務是識別源程式中的單詞是否有誤,編譯程式中實現這種功能的部分一般稱為詞法分析器。通常詞法分析的輸出是一個個單獨的單詞符號。

以JS為例,在這個過程中有三個主要部分:分析函數參數、分析變數聲明、分析函數聲明。語法分析階段的目的是識別出源程式的語法結構(即語句或句子)是否錯誤,這一階段通常可以發現語法的錯誤。在這個階段中,編譯器實際處理的是來自詞法分析得出的單詞符號。

而在計算公式引擎中我們處理數據的方式和編譯原理中處理語言這一過程極度相似,從實際應用出發實現一個類似Excel的計算公式的計算公式引擎,我們可以採用的思路是從詞法分析出發,將完整的長串公式語句拆分成小塊內容,然後再進行語法分析,最後對生成語法結構樹進行運算。接下來讓我們一起看看細節如何實現。

公式引擎的實現細節

我們從公式計算開始為大家說明,公式計算即由一個公式字元串進行計算後,得出表達式結果。比如:公式「=1+10*11」
計算後得到結果111。電子電腦並不是人類,這樣一個簡單的表達式想要完全正確計算,最終變成我們需要的數據內容,並不是簡單的我們看一眼後,口算就得到答案。實現這樣類Excel表格計算的功能,需要通過詞法分析,語法分析,語法結構樹計算這幾個過程。

1. 詞法分析

中常用的公式進行說明。

首先我們進行詞法分析,在這個過程中我們將公式字元拆成字元串數組,在Excel表格公式計算中,表達式的公式字元串中只包括:運算符、符號、字元串、數字、數組、引用、名稱這幾類。

名稱:sum

運算符:( ) :/ % +

引用:A1 A11 B1

數字:100

2. 語法分析

詞法分析完成之後,我們對詞法分析的結果進行進一步語法分析。通常計算中語法分析可以採用表達式樹或者堆棧(即逆波蘭式)來處理。

這裡我們先介紹表達式樹的方法。

語法分析——表達式樹

使用表達式樹進行分析的過程,從一棵二叉樹開始。首先我們將詞法分析的結果按照優先順序組成表達式樹,表達式樹的葉子結點就是操作數,內部節點是操作符。

在這個事例中,冒號的優先順序最高,其次是括弧,最後是除號。當這棵樹形成之後,就離我們拿到最終的運算結果很接近了。

我們會採用遞歸調用的方式對這顆樹進行運算,從根結點出發,到sum,一直向下遞歸,到A1:A11時,有了第一個結果,然後逐層返回計算結果。

這就完證的展示了如何實現一個公式計算。

語法分析——逆波蘭演算法

逆波蘭演算法是在語法分析階段形成了一個堆棧(即逆波蘭表達式),這個表達式的核心在於將普通我們是用的中綴表達式轉換為後綴表達式。括弧在運算的過程中只進行運算順序的提示,但並不是實際參與計算的元素內容,所以在中綴轉後綴的過程中就可以省略掉括弧內容,

然後由電腦編寫程式碼完成運算。

這裡展示了一棵樹轉化成對應的逆波蘭式的樣子。

二叉樹遞歸VS逆波蘭演算法

與一棵樹遞歸計算相比,逆波蘭式更符合數學計算的習慣。但實際在項目中處理這種公式計算的時候,到底哪一種更加能處理更複雜的情況呢?

讓我們來看一個多層嵌套的公示內容:

這個公示的使用場景是SUMIFS函數多列求和,等價於下面這個內容:

=SUMIFS($C:$C,$B:$B,$A1)+SUMIFS($D:$D,$B:$B,$A1)+….

很明顯上面的公式更加簡單,採用二叉樹遞歸的方式,只需要判斷SUMIFS節點的父節點以及孩子結點內容,只需要短短一行程式碼就可以搞定這個多列求和。

但是如果是用逆波蘭演算法,程式碼一開始遇到SUM就開始計算,很難判定SUM此時要運行的內容其實在最內層括弧之中。可以解決,但卻並不是最簡單的。

對比結果

與堆棧的方式相比,樹的解法更容易擴展、增強,可以更加輕鬆應對複雜的公式。這在處理大量公式、複雜計算就是得天獨厚的優勢。

總結

在介紹完如何解析並進行公式計算的全過程之後,接下來我們會繼續介紹在公式計算引擎中計算鏈和非同步函數的相關內容。在處理複雜公式時,有向圖如何求解,calcOnDemand解法又是什麼,還有在前後端計算中非同步函數的花式用法。

覺得不錯點個贊再走吧~後續還會為大家帶來更多有趣的內容~