十分鐘了解 18 個(gè)冷門(mén)編程概念
作為開(kāi)發(fā)者,我們需要不斷學(xué)習(xí)新的術(shù)語(yǔ)、技術(shù)和最佳實(shí)踐。但是,如果有人告訴你還有許多你甚至不知道的概念,而且這些概念可能會(huì)改變你編碼和思考的方式,你會(huì)怎么想?無(wú)論你是初學(xué)者還是經(jīng)驗(yàn)豐富的開(kāi)發(fā)者,這 18 個(gè)冷門(mén)概念都將幫助你提升水平。本文就會(huì)帶你深入了解這些概念,示例代碼將基于 JS 和 Python。
1. Thunk:善用拖延癥
你有過(guò)把事情拖到最后一刻才做的經(jīng)歷嗎?thunk 正是如此,它將函數(shù)的執(zhí)行推遲到絕對(duì)必要的時(shí)候。
想象一下,有一項(xiàng)計(jì)算成本很高的任務(wù),但暫時(shí)不確定是否需要計(jì)算結(jié)果。與其提前計(jì)算,不如用 thunk 將其封裝起來(lái)。
const thunk = () => 5 + 10;
console.log(thunk()); // 只在調(diào)用時(shí)進(jìn)行計(jì)算
2. Monad(單子):代碼的安全網(wǎng)
Monad 就像是函數(shù)的安全網(wǎng),幫你實(shí)現(xiàn)鏈?zhǔn)讲僮鞑⑻幚硪馔庑袨椋ㄈ珏e(cuò)誤或空值),而不會(huì)導(dǎo)致整個(gè)程序崩潰。如果你正在處理異步數(shù)據(jù),可能已經(jīng)在不知不覺(jué)中用到了 monad!
new Promise((resolve) => resolve(5))
.then(value => console.log(value)) // 輸出: 5
Monad 看起來(lái)很神秘,但可以把它們想象成沿著管道安全傳遞數(shù)據(jù)的容器。
3. Closure(閉包):代碼記憶
試想一下,如果過(guò)去的自己能給未來(lái)的自己留下一段回憶會(huì)怎么樣,這正是 closure(閉包)的作用,它允許函數(shù) "記住" 變量創(chuàng)建時(shí)候的上下文。
def counter():
count = 0
def _():
nonlocal count
count += 1
return count
return _
increment = counter()
print(increment())
print(increment())
print(increment())
#1
#2
#3
4. Memoization:讓我們提高效率,好嗎?
有沒(méi)有反復(fù)問(wèn)同一個(gè)問(wèn)題的經(jīng)歷?很煩人吧?計(jì)算機(jī)也是這么想的。Memoization 通過(guò)存儲(chǔ)結(jié)果解決了這個(gè)問(wèn)題,這樣就不必重復(fù)進(jìn)行昂貴的計(jì)算。
def fib(n, memo={}):
if n in memo:
return memo[n]
if n < 2:
return n
memo[n] = fib(n - 1, memo) + fib(n - 2, memo)
return memo[n]
fib(100)
#354224848179261915075
5. Continuation:未來(lái)掌握在自己手中
continuation 就像在代碼中按下 "暫停" 鍵,并將稍后執(zhí)行下一步,這是許多異步系統(tǒng)背后的秘訣。
function asyncTask(callback) {
setTimeout(() => {
console.log("Task complete");
callback();
}, 1000);
}
asyncTask(() => console.log("Next step"));
1 秒后,箭頭內(nèi)的代碼 () => { console.log("Task complete"); callback(); } 將被執(zhí)行。
處理過(guò)異步編程嗎?寫(xiě)過(guò)最后的回調(diào)函數(shù)吧?它就是一個(gè) continuation,是將在未來(lái)發(fā)生的事情!
6. Idempotence(冪等性):無(wú)論做多少次,結(jié)果都和第一次一樣
在編程過(guò)程中,有些操作無(wú)論執(zhí)行多少次,其行為方式都是一樣的。這就是冪等性 -- 無(wú)論調(diào)用 API 一次還是 100 次,結(jié)果都是一樣的。
GET /user/123 HTTP/1.1
如果一直調(diào)用 GET /user/123,得到的永遠(yuǎn)是同一個(gè)用戶(hù)。API 的世界依賴(lài)于這一原則,它讓一切都具有可預(yù)測(cè)性和安全性。
7. Quine(奎因):自我復(fù)制的程序
quine 是一種聰明的代碼,能輸出自己的源代碼,就像照鏡子時(shí)看到……嗯,代碼正盯著你看。
s = 's = {!r}; print(s.format(s))'; print(s.format(s))
#輸出 => s = 's = {!r}; print(s.format(s))'; print(s.format(s))
8. Zipper:超級(jí)結(jié)構(gòu)化導(dǎo)航
在不造成混亂的情況下瀏覽和修改數(shù)據(jù)結(jié)構(gòu)是一件棘手的事情,直到出現(xiàn)了 zipper,它可以幫助我們高效遍歷和修改結(jié)構(gòu)(如樹(shù)),而不會(huì)破壞原有結(jié)構(gòu)。
class Zipper:
def __init__(self, left, right):
self.left = left
self.right = right
def move_left(self):
return Zipper(self.left[:-1], [self.left[-1]] + self.right)
def move_right(self):
return Zipper(self.left + [self.right[0]], self.right[1:])
Zipper 是復(fù)雜數(shù)據(jù)結(jié)構(gòu)的導(dǎo)航指南針。
9. Functor(函子):友好的映射
functor 是一種可以映射函數(shù)的東西,可以在不接觸容器本身的情況下,對(duì)容器中的每個(gè)數(shù)據(jù)進(jìn)行轉(zhuǎn)換。它不僅僅是函數(shù)式編程,更具備強(qiáng)大的組合能力。
numbers = [1, 2, 3]
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # [1, 4, 9]
Python 中的 list 是函數(shù),允許我們?cè)诓桓淖冊(cè)冀Y(jié)構(gòu)的情況下進(jìn)行映射操作。
10. Tail Call Optimization(尾調(diào)用優(yōu)化):不會(huì)造成棧溢出的無(wú)限遞歸
尾調(diào)用優(yōu)化(TCO)讓我們?cè)诰帉?xiě)遞歸函數(shù)時(shí)不必?fù)?dān)心內(nèi)存不足。如果函數(shù)的最后一個(gè)操作是調(diào)用自身,TCO 可以重復(fù)使用當(dāng)前函數(shù)的棧幀。
def factorial(n, acc=1):
if n == 0:
return acc
return factorial(n - 1, acc * n)
注:以上示例只顯示了 TCO 的結(jié)構(gòu),但請(qǐng)記住 Python 本身并不支持 TCO。
11. Currying(柯里化):請(qǐng)一次只做一件事!
柯里化將一個(gè)接受多個(gè)參數(shù)的函數(shù)分解成一系列函數(shù),每個(gè)函數(shù)只有一個(gè)參數(shù)。
def add(x):
def inner(y):
return x + y
return inner
add_five = add(5)
print(add_five(3)) # 8
柯里化是獲得靈活性的關(guān)鍵 -- 想要應(yīng)用部分參數(shù)?柯里化可以做到。
還可以基于 partial 進(jìn)行函數(shù)柯里化。
from functools import partial
def adder(a, b):
return a + b
two_adder = partial(adder, 2)
two_adder(10)
#12
12. Spectre & Meltdown:困擾你的 CPU
你可能聽(tīng)說(shuō)過(guò) Spectre 和 Meltdown 這兩個(gè)震驚世界的臭名昭著的漏洞,它們利用了現(xiàn)代處理器優(yōu)化性能的方式,泄露了敏感數(shù)據(jù)。
if (untrusted_input < array_size) {
temp = array[untrusted_input * 512];
}
單純追求速度可能會(huì)讓事情更糟,投機(jī)取巧的執(zhí)行可能會(huì)加快代碼的執(zhí)行,但有時(shí)泄露的信息會(huì)超出我們的想象!
13. Homomorphism(同態(tài)):保留結(jié)構(gòu)
Homomorphism(同態(tài))是個(gè)花哨的詞,指在兩個(gè)代數(shù)系統(tǒng)之間保留結(jié)構(gòu)的函數(shù)。在數(shù)學(xué)中是很重要的概念,但在編程中轉(zhuǎn)換數(shù)據(jù)時(shí)也很重要。(最重要的函數(shù)式編程概念之一)
numbers = [1, 2, 3, 4]
def square(x):
return x * x
squared_numbers = list(map(square, numbers))
print(squared_numbers)
# Output: [1, 4, 9, 16]
14. Lazy Evaluation(惰性求值):只在需要時(shí)
惰性求值會(huì)將表達(dá)式的求值延遲到需要時(shí)才進(jìn)行,對(duì)于提高大型數(shù)據(jù)集或計(jì)算程序的性能非常有用。
def lazy_range(n):
i = 0
while i < n:
yield i
i += 1
for num in lazy_range(5):
print(num)
生成器只在需要時(shí)才計(jì)算數(shù)值,從而節(jié)省了內(nèi)存和處理時(shí)間。
thunk 和惰性求值生成器 ?? 有何區(qū)別?
- 惰性求值是一種延遲求值表達(dá)式的策略,直到實(shí)際需要時(shí)才進(jìn)行計(jì)算。它可以避免不必要的計(jì)算,從而幫助優(yōu)化性能,尤其是在使用無(wú)限數(shù)據(jù)結(jié)構(gòu)或處理昂貴的操作時(shí)。
- Thunk 是一種用于實(shí)現(xiàn)惰性求值的特定技術(shù),本質(zhì)上是一個(gè)沒(méi)有參數(shù)的函數(shù),封裝了計(jì)算或表達(dá)式,計(jì)算會(huì)被延遲到 Thunk 被明確調(diào)用時(shí)執(zhí)行。
15. Canonicalization(規(guī)格化):將數(shù)據(jù)標(biāo)準(zhǔn)化
Canonicalization(規(guī)格化)是將數(shù)據(jù)轉(zhuǎn)換為標(biāo)準(zhǔn)或規(guī)范化形式的過(guò)程,通常用于數(shù)據(jù)庫(kù)和 URL,以確保一致性。
http://example.com
https://example.com
http://www.example.com
規(guī)格化可確保所有版本都指向一個(gè)唯一首選版本。
16. Side Effect(副作用):超出預(yù)期
當(dāng)函數(shù)或表達(dá)式修改了其本地環(huán)境之外的某些狀態(tài)時(shí),就會(huì)產(chǎn)生副作用,例如改變?nèi)肿兞俊/O 操作或修改輸入?yún)?shù)。
def add_to_list(value, lst=[]):
lst.append(value)
return lst
print(add_to_list(1)) # [1]
print(add_to_list(2)) # [1, 2] (unexpected!)
本例中的函數(shù)修改了默認(rèn)參數(shù),導(dǎo)致了意想不到的副作用。
17. Hoisting:將聲明移至頂部
在 JavaScript 中,hoisting 會(huì)在編譯階段將變量和函數(shù)聲明移到其作用域的頂部。
console.log(x); // 未定義
var x = 5;
即使 x 是在 console.log 之后聲明,JavaScript 也會(huì)將聲明提升到頂部,使 x 在定義之前就可以訪(fǎng)問(wèn)。
18. Monoid(單態(tài)):以一致的方式組合數(shù)據(jù)
monoid (單態(tài))是一種代數(shù)結(jié)構(gòu),具有二元運(yùn)算和一個(gè)標(biāo)識(shí)元素,允許以關(guān)聯(lián)方式組合數(shù)據(jù)。
result = ''.join(['a', 'b', 'c'])
print(result) # 'abc'
這里的字符串連接構(gòu)成了一個(gè)以 '' 為標(biāo)識(shí)元素、以 + 為二進(jìn)制運(yùn)算的單元。
結(jié)論
這些高級(jí)編程術(shù)語(yǔ)初看起來(lái)可能很深?yuàn)W,但理解了它們可以極大改進(jìn)編寫(xiě)代碼的方法。無(wú)論是優(yōu)化性能、安全性還是可讀性,這些概念可以是強(qiáng)大的工具,幫助我們以?xún)?yōu)雅的方式解決復(fù)雜問(wèn)題。