如果你的算式裡有一段東西一直重複出現:,B2*C2 就寫了三次,改一個地方還得記得改另外兩個。今天來聊聊 LET 跟 LAMBDA:前者幫算式裡的值取名字,後者讓你自己定義函式,最後再接上 MAP 跟 REDUCE 兩個吃 LAMBDA 的函式。
重複的算式有多麻煩
我手上有一張手搖飲的銷售表,5 個品項,有單價跟賣出的杯數:
| A | B | C | |
|---|---|---|---|
| 1 | 品項 | 單價 | 杯數 |
| 2 | 蘋果紅茶 | 45 | 37 |
| 3 | 烏龍奶茶 | 55 | 112 |
| 4 | 蘋果綠茶 | 45 | 8 |
| 5 | 青茶 | 35 | 64 |
| 6 | 桂花烏龍 | 50 | 21 |
我想算每個品項的營業額,也就是單價乘上杯數,而且營業額只要達到 2000 就打 9 折。先手算第一列:45 × 37 = 1665,沒到 2000,維持 1665。第二列 55 × 112 = 6160,打 9 折是 5544。
如果直接寫成算式,放在 E2,就是:
營業額達 2000 打 9 折,B2*C2 重複了三次
=IF(B2*C2>=2000, B2*C2*0.9, B2*C2)這條算式沒有錯,只是 B2*C2 出現了三次。折扣門檻或計算方式一改,我就得改三個地方,而且很容易只改到兩個。這時候就可以請 LET 出場。
LET
LET 讓你先替一段計算取個名字,後面的算式直接用名字。
語法
- 名稱1、值1:第一組,名稱自己取,值可以是數字、儲存格或一段計算。
- 名稱2、值2:選填,可以接著寫第二組、第三組。後面的值可以用前面取好的名稱。
- 算式:最後一個參數,用前面取好的名稱算出最後要回傳的結果。
名稱和值要成雙成對地出現,最後單獨留下的那一個就是算式。這邊的名稱我通常寫簡單的英文單字,像 sales、rate。
範例
把剛剛的題目改用 LET 寫,就是:
先把營業額取名 sales、折扣取名 rate,再相乘
=LET(sales, B2*C2, rate, IF(sales>=2000, 0.9, 1), sales*rate)sales, B2*C2:把單價乘杯數的結果取名叫sales。rate, IF(sales>=2000, 0.9, 1):折扣取名叫rate,營業額達 2000 是 0.9,否則是 1。這邊直接用了前一組的sales。sales*rate:最後的算式,營業額乘折扣。
B2*C2 現在只寫了一次。把這條算式往下填到 E6,來看看結果:
| E | |
|---|---|
| 2 | 1665 |
| 3 | 5544 |
| 4 | 360 |
| 5 | 2016 |
| 6 | 1050 |
跟手算一樣:烏龍奶茶 6160 × 0.9 = 5544,青茶 2240 × 0.9 = 2016,其他沒到 2000 的維持原價。
我自己的習慣是,看到算式裡有同一段東西出現兩次以上,就先想能不能用 LET 解決。算式讀起來也比較像在讀句子:「營業額是這個、折扣是那個、兩個相乘」。
LAMBDA
LET 的名稱只活在那一條算式裡。如果同樣的計算想在很多地方重複使用,而且每次帶入不同的值,就輪到 LAMBDA:把一段計算包成一個自己定義的函式。
語法
- 名稱1:第一個要傳進來的值,名稱自己取,但不能像儲存格參照(例如
c1),不能含空格或特殊字元(底線、點可以用),也不能以數字開頭。 - 名稱2:選填,還有別的值要傳進來就接著寫。
- 算式:最後一個參數,用前面取好的名稱寫出計算。
這邊跟 LET 不一樣,前面的名稱只是「預留位置」,還沒有放任何值。所以只寫 LAMBDA 本身是不會算出結果的(儲存格會顯示 #N/A,提示「LAMBDA 函式的後面必須接著含有實際值的呼叫」),要在後面再接一組括號,把真正的值傳進去。
Excel 365 也有 LAMBDA,以及 MAP、REDUCE 這一家族的函式,Excel 2021 則沒有 LAMBDA。
直接呼叫
先用最簡單的乘法試試看。我想把 B2 跟 C2 傳進去相乘:
直接在 LAMBDA 後面接括號傳值,單價 45 乘杯數 37
=LAMBDA(price, qty, price*qty)(B2, C2)price, qty:兩個預留位置的名稱。price*qty:用這兩個名稱寫的算式。(B2, C2):真正傳進去的值,順序跟前面的名稱一一對應,所以price是B2的 45,qty是C2的 37。
你應該會得到 1665,跟前面手算的一樣。
直接在 LAMBDA 後面接括號只適合拿來測試。實際在用的時候,它很少單獨出現,不是被存成具名函式,就是被當成參數交給 MAP、REDUCE 這類函式,下面兩節就是這兩種用法。
把 LET 放進 LAMBDA 裡也完全沒問題,把整段折扣邏輯包起來,傳 B3 跟 C3 進去:
LAMBDA 裡面用 LET,傳入烏龍奶茶的單價與杯數
=LAMBDA(
price, qty,
LET(sales, price*qty, rate, IF(sales>=2000, 0.9, 1), sales*rate)
)(
B3,
C3
)結果是 5544。
存成具名函式
每次都要把整條 LAMBDA 貼過去,其實也不方便。Google 試算表可以把它存成具名函式,之後像 SUM 一樣直接打名字。
- 點開畫面上方的「資料」>「已命名函式」,右側會出現面板。
- 按下「新增函式」,函式名稱填
NET_SALES。 - 「引數預留位置」填
price、qty,每個輸入後按 Enter,就會變成標籤。 - 公式定義填 ,這邊不用再寫
LAMBDA,因為引數預留位置已經在上一步定義了。 - 按「繼續」,確認函式預覽(可以順便填引數說明),再按「建立」。
圖:函式名稱、引數預留位置、公式定義,對應 LAMBDA 的三個部分。
存好之後,在 E2 就可以這樣用:
用具名函式算營業額,折扣規則只寫在函式定義裡
=NET_SALES(B2, C2)結果跟前面一樣,E2 是 1665。以後折扣規則改了,只要回「已命名函式」的面板改定義那一處,所有用到 NET_SALES 的格子都會跟著變。
具名函式是跟著這份試算表走的,換一份新的試算表要用的話,需要從別份試算表匯入。這邊先帶到能用為止,「已命名函式」面板的其他設定就不展開了。
MAP
寫好一個函式之後,常會想對一整欄的每一格都跑一遍。MAP 就是做這件事:它一格一格把資料交給 LAMBDA,再把每次的結果排成一欄。
map 在中文是「對應、映射」的意思,簡單來說就是「每一個進去,每一個出來」。
語法
- 陣列1:要處理的範圍。
- 陣列2:選填,有第二個範圍就接著寫,大小要跟陣列1 一樣。
- LAMBDA:最後一個參數,長得像
LAMBDA(名稱1, [名稱2, ...], 算式)。有幾個陣列,LAMBDA裡就要有幾個名稱,依序對應。
範例
我想一個公式算出 5 個品項的營業額,不打折。單價是 B2:B6、杯數是 C2:C6,兩個範圍都要交給 MAP:
把單價與杯數兩欄一格一格配對,相乘
=MAP(B2:B6, C2:C6, LAMBDA(price, qty, price*qty))B2:B6、C2:C6:兩個範圍。price, qty:price依序拿到 B 欄的每一格,qty依序拿到 C 欄的每一格。price*qty:每一組算一次。
公式只要放在 E2,結果會自己往下展開到 E6。來看看結果:
| E | |
|---|---|
| 2 | 1665 |
| 3 | 6160 |
| 4 | 360 |
| 5 | 2240 |
| 6 | 1050 |
要連折扣一起算也沒問題,把剛剛 LET 的邏輯整段放進 LAMBDA:
MAP 搭配 LAMBDA 與 LET,一個公式算出打折後的營業額
=MAP(
B2:B6,
C2:C6,
LAMBDA(
price, qty,
LET(sales, price*qty, rate, IF(sales>=2000, 0.9, 1), sales*rate)
)
)這樣 E2 到 E6 是 1665、5544、360、2016、1050,跟前面逐列填算式的結果一樣,但只有一個公式。這裡的 B2:B6、C2:C6 是固定五列;新增品項後,仍須同步延伸兩個來源範圍,不能只因使用 MAP 就自動包含第 7 列。
只傳一個範圍也可以。比方說杯數 50 杯以上的品項標「熱賣」:
只傳杯數一欄,達 50 杯標熱賣,其餘標一般
=MAP(C2:C6, LAMBDA(qty, IF(qty>=50, "熱賣", "一般")))| E | |
|---|---|
| 2 | 一般 |
| 3 | 熱賣 |
| 4 | 一般 |
| 5 | 熱賣 |
| 6 | 一般 |
簡單的運算,像這個標記熱賣的例子,用 ARRAYFORMULA 就夠了,我通常還是用 ARRAYFORMULA,比較短。當每一格要做的事需要好幾個步驟、想用 LET 取名字的時候,我才會換成 MAP。
REDUCE
MAP 是一個進去、一個出來。REDUCE 則是把一整個範圍「縮」成一個值,reduce 在中文是「縮減」的意思。
語法
- 初始值:累計的起點。
- 陣列:要一格一格處理的範圍。
- LAMBDA:最後一個參數,要有兩個名稱:
LAMBDA(累計, 目前, 算式)。第一個名稱拿到「到目前為止累計的值」,第二個名稱拿到「這一輪的那一格」,算式算出來的結果,會變成下一輪的累計。
範例
先從最單純的開始:把 C 欄的杯數加總。手算是 37 + 112 + 8 + 64 + 21 = 242,寫成 REDUCE:
累計從 0 開始,每一輪加上目前這一格的杯數
=REDUCE(0, C2:C6, LAMBDA(acc, qty, acc+qty))0:初始值,累計從 0 開始。C2:C6:要處理的範圍。acc:目前累計的值,qty:這一輪的杯數。acc+qty:累計加上這一格,就是下一輪的累計。
每一輪的狀況是這樣:
| 輪次 | acc | qty | acc+qty |
|---|---|---|---|
| 1 | 0 | 37 | 37 |
| 2 | 37 | 112 | 149 |
| 3 | 149 | 8 | 157 |
| 4 | 157 | 64 | 221 |
| 5 | 221 | 21 | 242 |
最後的結果是 242。要加總的話,其實直接用 SUM(C2:C6) 就好,這邊只是拿它來看清楚 REDUCE 怎麼一輪一輪走。
那什麼時候會想用 REDUCE?當每一輪的結果要依賴上一輪的時候。比方說蘋果紅茶的單價 45,連續調價三次:先漲 10%,再降 5%,最後漲 8%。調幅放在 G2:G4,分別是 0.1、-0.05、0.08。
手算一次:45 × 1.1 = 49.5,49.5 × 0.95 = 47.025,47.025 × 1.08 = 50.787。三次調整後是 50.787。
如果用 REDUCE 寫,就是:
從 45 開始,每一輪乘上 (1 + 調幅)
=REDUCE(45, G2:G4, LAMBDA(price, change, price*(1+change)))來看看結果:50.787,跟手算一致。這種「上一輪的輸出是下一輪的輸入」的算法,只用 SUM 或 SUMPRODUCT 這類函式比較難一次寫完,這是 REDUCE 派得上用場的地方。
其他幾個吃 LAMBDA 的函式
還有四個同一家族的函式,這邊只放最短的一個例子,語法差不多,只是 LAMBDA 拿到的東西不一樣。都用前面那張表,單價 B2:B6、杯數 C2:C6:
BYROW:一列一列處理。,每一列的單價乘杯數,得到 1665、6160、360、2240、1050。BYCOL:一欄一欄處理。,每一欄的最大值,橫向展開成兩格,55 跟 112。SCAN:跟REDUCE一樣一輪一輪累計,不過每一輪的結果都保留下來。,得到 37、149、157、221、242,就是上面那張輪次表裡最右邊的那一欄。MAKEARRAY:不吃現成的範圍,自己指定列數與欄數,LAMBDA拿到列號跟欄號。,產生 3 × 3 的乘法表。
MAKEARRAY 的結果長這樣:
| E | F | G | |
|---|---|---|---|
| 2 | 1 | 2 | 3 |
| 3 | 2 | 4 | 6 |
| 4 | 3 | 6 | 9 |
練習
加料之後的營業額
每個品項都加 5 元的珍珠,也就是單價 +5,不打折。請用一個 MAP 公式,算出 5 個品項各自「(單價 + 5)× 杯數」的結果。單價在 B2:B6,杯數在 C2:C6。
小測驗
LET、LAMBDA 與 REDUCE
的結果是多少?
這篇的公式都不算長,可以先挑你手上一條重複了好幾次的算式,用 LET 改寫看看,感覺一下名稱帶來的差別。
我是喜特先生,Mr. Sheet,我們下個教學見!
接下來可以
這篇在技能樹的 Lv.5「系統設計與最佳實踐」,模組「LAMBDA」(第 1/1 篇)。讀完這篇,路線上的下一個模組是「完整系統設計案例」。
回到技能樹相關文章
查看所有文章ARRAYFORMULA 入門:一個公式算完整欄,新增資料自動跟上
從公式拖拉到一半漏掉新資料的問題出發,說明 ARRAYFORMULA 的寫法、A2:A 這種開放式範圍、用 IF 擋掉空白列,以及 AND、OR 這類函式在裡面為什麼不會逐列計算。
表單回覆表自動填滿:編號、單價、金額、狀態一次搞定
用 Google 表單收訂單時,在回覆工作表旁邊用 ARRAYFORMULA 自動產生編號、查價目表帶出單價、算金額,並用巢狀 IF 標出大單與待確認的列,新回覆進來不用再拖公式。
設計可維護的試算表:讓三個月後的自己也看得懂
用一份批發折扣表示範怎麼改造:資料、計算、報表分成三類分頁並加前綴,折扣率搬到設定頁再取名字,巢狀 IF 拆成三欄或改用 LET,最後用統一的底色標出哪格能動。
留言討論
共 0 則留言

