W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎勵
現(xiàn)在讓我們小憩一下,聊一聊一種你可能聽過的編程風(fēng)格:函數(shù)式編程。
如果你用過Python,Ruby或Javascript,_甚或_Lisp,Scheme,Clojure或Haskell, 你應(yīng)該會覺得把函數(shù)作為變量類型,用不可變的狀態(tài)作為數(shù)據(jù)結(jié)構(gòu)是平常的事。 如果你沒用過,你可以放心地跳過這一章了,但我還是鼓勵你找機(jī)會去試試并拓寬自己的視野。
Vimscript具有使用函數(shù)式風(fēng)格進(jìn)行編程的潛力,不過會有點(diǎn)吃力。 我們可以創(chuàng)建一些輔助函數(shù)來讓這個(gè)過程少些痛苦。
繼續(xù)前進(jìn)并創(chuàng)建functional.vim
文件,這樣你就不用反復(fù)地重新?lián)舸蛎恳恍写a。 這個(gè)文件將會成為這一章的草稿本。
不幸的是,Vim沒有類似于Clojure內(nèi)置的vector和map那樣的不可變集合, 不過通過一些輔助函數(shù),我們可以在一定程度上模擬出來。
在你的文件加上下面的函數(shù):
function! Sorted(l)
let new_list = deepcopy(a:l)
call sort(new_list)
return new_list
endfunction
保存并source文件,然后執(zhí)行:echo Sorted([3,2,4,1])
來試試看。 Vim輸出[1,2,3,4]
。
這跟調(diào)用內(nèi)置的sort()
函數(shù)有什么區(qū)別呢?關(guān)鍵在于第一行:let new_list = deepcopy(a:l)
。 Vim的sort()
_就地_重排列表,所以我們先創(chuàng)建一個(gè)列表的副本,并排序副本, 這樣原本的列表不會被改變。
這樣就避免了副作用,并幫助我們寫出更容易推斷和測試的代碼。讓我們加入更多同樣風(fēng)格的輔助函數(shù):
function! Reversed(l)
let new_list = deepcopy(a:l)
call reverse(new_list)
return new_list
endfunction
function! Append(l, val)
let new_list = deepcopy(a:l)
call add(new_list, a:val)
return new_list
endfunction
function! Assoc(l, i, val)
let new_list = deepcopy(a:l)
let new_list[a:i] = a:val
return new_list
endfunction
function! Pop(l, i)
let new_list = deepcopy(a:l)
call remove(new_list, a:i)
return new_list
endfunction
除了中間的一行和它們接受的參數(shù),每一個(gè)函數(shù)都是一樣的。保存并source文件,在一些列表上試試它們。
Reversed()
接受一個(gè)列表并返回一個(gè)新的倒置了元素的列表。
Append()
返回一個(gè)在原列表的基礎(chǔ)上增加了給定值的新列表。
Assoc()
("associate"的縮寫)返回一個(gè)給定索引上的元素被替換成新值的新列表。
Pop()
返回一個(gè)給定索引上的元素被移除的新列表。
Vimscript支持使用變量儲存函數(shù),但是相關(guān)的語法有點(diǎn)愚鈍。執(zhí)行下面的命令:
:let Myfunc = function("Append")
:echo Myfunc([1, 2], 3)
Vim意料之中地顯示[1, 2, 3]
。注意我們使用的變量以大寫字母開頭。 如果一個(gè)Vimscript變量要引用一個(gè)函數(shù),它就要以大寫字母開頭。
就像其他種類的變量,函數(shù)也可以儲存在列表里。執(zhí)行下面命令:
:let funcs = [function("Append"), function("Pop")]
:echo funcs[1](['a', 'b', 'c'], 1)
Vim顯示['a', 'c']
。funcs
變量_不_需要以大寫字母開頭,因?yàn)樗鼉Υ娴氖橇斜?,而不是函?shù)。 列表的內(nèi)容不會造成任何影響。
讓我們創(chuàng)建一些用途廣泛的高階函數(shù)。如果你需要解釋,高階函數(shù)就是接受_別的_函數(shù)并使用它們的函數(shù)。
我們將從map
函數(shù)開始。在你的文件中添加這個(gè):
function! Mapped(fn, l)
let new_list = deepcopy(a:l)
call map(new_list, string(a:fn) . '(v:val)')
return new_list
endfunction
保存并source文件,執(zhí)行下面命令試試看:
:let mylist = [[1, 2], [3, 4]]
:echo Mapped(function("Reversed"), mylist)
Vim顯示[[2, 1], [4, 3]]
,正好是對列表中的每一個(gè)元素應(yīng)用了Reversed()
的結(jié)果。
Mapped()
是如何起作用的?我們又一次用deepcopy()
創(chuàng)建新的列表,修修改改,返回修改后的副本 —— 沒什么是新的。有門道的是中間的部分。
Mapped()
接受兩個(gè)參數(shù):一個(gè)funcref("儲存一個(gè)函數(shù)的變量"在Vim里的說法)和一個(gè)列表。 我們使用內(nèi)置的map()
函數(shù)實(shí)現(xiàn)真正的工作?,F(xiàn)在就閱讀:help map()
來看它怎么工作的。
現(xiàn)在我們將創(chuàng)建一些通用的高階函數(shù)。把下面的代碼加入到你的文件:
function! Filtered(fn, l)
let new_list = deepcopy(a:l)
call filter(new_list, string(a:fn) . '(v:val)')
return new_list
endfunction
用下面的命令嘗試Filtered()
:
:let mylist = [[1, 2], [], ['foo'], []]
:echo Filtered(function('len'), mylist)
Vim顯示[[1, 2], ['foo']]
。
Filtered()
接受一個(gè)謂詞函數(shù)和一個(gè)列表。它返回一個(gè)列表的副本, 而這個(gè)列表只包括將自身作為謂詞函數(shù)的輸入?yún)?shù)并返回真值的元素。 這里我們使用了內(nèi)置的len()
,讓它過濾掉所有長度為0的元素。
最后我們創(chuàng)建了Filtered()
的好基友(counterpart):
function! Removed(fn, l)
let new_list = deepcopy(a:l)
call filter(new_list, '!' . string(a:fn) . '(v:val)')
return new_list
endfunction
像使用Filtered()
一樣試一下:
:let mylist = [[1, 2], [], ['foo'], []]
:echo Removed(function('len'), mylist)
Vim顯示[[], []]
。Removed()
就像Filtered()
,不過它只保留謂詞函數(shù)返回_非_真值的元素。
代碼中的唯一不同在于調(diào)用命令前面的'!' .
,它把謂詞函數(shù)的結(jié)果取反。
考慮到Vim不得不持續(xù)地創(chuàng)建新的副本并垃圾回收舊的對象,你可能會認(rèn)為不停地制造副本是種浪費(fèi)。
是的,你是對的!Vim的列表不像Clojure的vector那樣支持結(jié)構(gòu)共享(structural sharing), 所以這里所有的復(fù)制操作是昂貴的。
有時(shí)這的確是個(gè)問題。如果你需要使用龐大的列表,程序就會因此變慢。 在現(xiàn)實(shí)世界,你可能會吃驚地發(fā)現(xiàn)你幾乎不會注意到其中的差別。
想想看吧:當(dāng)我正寫下本章時(shí),Vim占用了80M內(nèi)存(而且我可是裝了_一堆_插件)。 我的筆記本總共有_8G_內(nèi)存。有一些列表的副本被創(chuàng)建出來,這會造成可被察覺的不同嗎? 當(dāng)然這取決于列表的大小,但在大多數(shù)情況下答案將會是"No"。
作為比較,我的Firefox打開了五個(gè)tab,現(xiàn)在正饕餮著_1.22G_內(nèi)存。
你將需要自己判斷,什么時(shí)候這種編程風(fēng)格會導(dǎo)致不可接受的低效率。
閱讀:help sort()
。
閱讀:help reverse()
。
閱讀:help copy()
。
閱讀:help deepcopy()
。
閱讀:help map()
,如果你未曾讀過。
閱讀:help function()
。
修改Assoc()
,?Pop()
,?Mapped()
,?Filtered()
和Removed()
來支持字典類型。 你可能需要閱讀:help type()
來幫助自己。
實(shí)現(xiàn)Reduced()
。
倒給自己一杯最喜歡的飲料。這一章真激烈(intense)!
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: