最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

記一次程序優(yōu)化

2023-08-23 16:31 作者:SAS駱豪  | 我要投稿

背景

大約在一年前,我接觸到了 IN 操作符的特殊用法:

var in array_name

該用法將變量的值直接與數(shù)組中每個元素的值進行比較,而無需借助循環(huán)語句,返回值則是布爾類型,形式簡潔且優(yōu)美。為了記住此法,我盡可能地在自己的工作中使用它,其中一個用例是:如何找到字符串中首個漢字的位置?測試數(shù)據(jù)集如下所示:

輸入數(shù)據(jù)集截圖

1行純粹是單字節(jié)字符,第2行至第5行在不同位置開始出現(xiàn)中文,第68行則是外文文字(阿、日、俄)。我當時給出的解決方案如下:

輸出數(shù)據(jù)集截圖

我利用 unicode() 函數(shù),將基本漢字集(U+4E00-9FA5)存入臨時數(shù)組,然后遍歷源字符串中的所有字符,判斷其是否為數(shù)組中的一員,在這里我用到了 IN 操作符的特殊用法,當判斷返回真值時,查找當前字符在源字符串中的位置,并將其作為結(jié)果輸出。


自定義函數(shù)

最近,我又翻到了這個用例,我認為它與 SAS 的內(nèi)建函數(shù) anynum()、anyalpha()、anyspace()等非常類似,都是用來查找源字符串中特定類型字符的首次出現(xiàn)位置。于是,我誕生了制作自定義函數(shù) anyhan() 的想法,將之前的代碼片段進行封裝,并做了必要的調(diào)整,第一版設(shè)計就誕生了:

我使用 PROC FCMP 進行自定義函數(shù)的設(shè)計,主要有三處調(diào)整:

  1. FCMP 過程步對數(shù)組的定義與數(shù)據(jù)步不同,數(shù)組下標起始值和_temporary_ 關(guān)鍵字發(fā)生了改變;

  2. FCMP 過程步不支持 var in array_name 用法,調(diào)整成了do 循環(huán)進行遍歷查找;

  3. 由于增加了一層循環(huán),循環(huán)的跳出和結(jié)果變量的賦值稍有改變;

這個函數(shù)定義完備,并可以使用,但是速度卻不盡如人意。我嘗試將它用在測試數(shù)據(jù)集上,每條觀測調(diào)用該函數(shù)100次,居然花費了7秒多的時間。測試程序如下:

我稍加思索,認為不是每個字符都需要判斷是否為漢字,比如單字節(jié)字符明顯就可以跳過,于是我將函數(shù)定義第14行的:

do j=1 to dim(_han_);

改為:

if length(kchar)>1 then do j=1 to dim(_han_);

速度有了一點點改善,同樣的測試從7秒變成了約6秒,但仍然不能使我滿意。我盯著程序,要找出是誰拉了后腿,注意到在原始的解決方案中,數(shù)組賦值的操作有限定條件:if _n_=1,而 anyhan() 中是沒有的,會不會是它耗時太多呢?我立刻為自定義函數(shù)的數(shù)組賦值也加上這個限定條件,但接下來又陷入泥潭:耗時減少到1.5秒,但結(jié)果變量的值全為0!看來,_n_ 并沒有被 FCMP 當成數(shù)據(jù)步中的自動變量。

我無法再閱讀出蛛絲馬跡,但我很快想到還有另外一種手段可用,那就是刪代碼調(diào)試法。這種方法脫胎于控制變量法思想,在保證代碼可以運行的前提下,每次刪除一部分代碼,從而觀察到被刪除的代碼對結(jié)果的影響。我早就懷疑自定義函數(shù)中數(shù)組的賦值行為與數(shù)據(jù)步不同,因此直接就修改了函數(shù)定義:

運行同樣的測試程序,耗時約4.8秒,拖慢速度的源頭找到了!找到它不容易,要優(yōu)化它就更麻煩些。我深入地思考:制作漢字集數(shù)組是為了后面的比較提供方便,但每條觀測都執(zhí)行20901次循環(huán),調(diào)用20901 unicode() 函數(shù),有這個必要嗎?顯然,要執(zhí)行查找的源字符串一般不會用到這么多個不同的漢字,《現(xiàn)代漢語常用字表》不過3500字,如果限定在臨床研究或金融研究領(lǐng)域就更少了。同樣是判定指定字符是否處在漢字基本字集內(nèi),我不一定要將該字符與基本字集元素逐個比較,基于基本字集編碼連續(xù)的特點,我也可以將該字符的編碼與基本字集的編碼范圍進行比較,這樣就無需定義漢字集數(shù)組,一定能快上許多。我改了改,又調(diào)試了一會,第二版設(shè)計也出來了:

在逐個截取源字符串中的字符時,使用 unicodec() 獲取當前字符的 unicode 編碼值,編碼值以 \u 開頭,后面跟著2位、4位或8位十六進制數(shù)字,接著配合使用 substr() input() 將字符型編碼值轉(zhuǎn)換為數(shù)值,最后再與基本字集的編碼上下限進行比較,當比較結(jié)果為真時,查找當前字符在源字符串中的位置,并將其作為結(jié)果輸出。

我再次將它放在同樣的測試程序上,大概一眨眼的功夫,程序就成功地給出了結(jié)果,這十分令我振奮!我再次回顧函數(shù)定義,發(fā)現(xiàn)還有兩點可以改進的地方:

  1. 當前只剩下一層循環(huán),goto 語句的使用或許存在爭議,可改用其它跳出循環(huán)做法;

  2. 既然是對字符編碼進行比較,就不一定要把編碼值轉(zhuǎn)換為數(shù)值,字符型的編碼值也可以比較;

基于這兩點改進思路,我很快給出了第三版的設(shè)計:

在新的設(shè)計當中,do until() 代替了原先的 goto 語句+標簽語句的組合,程序的可讀性更好。另外,編碼值的比較被設(shè)計成基于字符串的比較方式,即從左至右逐個比較字符的的大小,由于編碼值的構(gòu)成特點,這實際上就是在挨個比較編碼值中組成十六進制值的字符的大小,更具體一些,就是在比較這些字符的 ASCII 碼值的大小,例如4(碼值52)小于9(碼值57),而F(碼值70)大于E(碼值69)。

我十分滿意這個設(shè)計,它足夠簡潔,又易于擴展,假如將不等式兩端的上下限換為 \u3040 \u30FF,就可以用來匹配日文字符,換為 \u0400 \u052F 就可以匹配俄文字符。

我使用 SAS 9.4 M8 進行開發(fā)和測試, CPU Intel i7-1265U,不同設(shè)計的 anyhan() 函數(shù)運行時間如下(運行10次取平均):

不同設(shè)計的 anyhan() 函數(shù)運行時間




記一次程序優(yōu)化的評論 (共 條)

分享到微博請遵守國家法律
沽源县| 同心县| 军事| 靖边县| 常熟市| 张北县| 南召县| 静海县| 文成县| 云梦县| 木里| 兴和县| 勃利县| 凤山市| 青铜峡市| 于田县| 浮山县| 厦门市| 探索| 视频| 化德县| 中卫市| 黑龙江省| 霍林郭勒市| 义乌市| 深州市| 侯马市| 商南县| 怀宁县| 海林市| 岳普湖县| 惠州市| 旅游| 周宁县| 宝兴县| 历史| 龙口市| 界首市| 彩票| 灯塔市| 即墨市|