for each循环_Power Query — 循环初步
題記:
《Excel圣經》1:3 微軟說,“要有循環”,便有了循環。
引子:
keyword: one of and as each else error false if in is let meta not otherwise or section shared then true try type #binary #date #datetime #datetimezone #duration #infinity #nan #sections #shared #table #time
可以發現M語言的關鍵字里面并沒有for/while這樣的關鍵字,那么M如何實現循環呢?
遍歷:
M實現循環是通過特定的函數或者運算符來實現的,按照實現原理的不同,可以初步地分為——遍歷、迭代與遞歸三大類,我們先看一些簡單的遍歷案例:
~~~~~~~~~~
案例1:不使用Repeat函數,生成一個list,里面有20個Number "1"。
~~~~~~~~~~
在M語言中,就有一個函數可以遍歷一個list,它就是List.Transform ——
List.Transform(list as list, transform as function) as list
這個函數實現的功能其實是“list內元素轉化”的功能,但由于這個過程會遍歷list中的每一個元素,因此我們可以把這個函數當做for循環來使用:
= List.Transform({1..20},(x)=>1)
我們再看看這個實現過程:將構造的{1..20}的每一個元素都轉化為1,構造了1~20共20個元素的序列,因此最終轉化的結果也是有20個數字1的list。
~~~~~~~~~~
案例2:使用List.Transform函數,生成20以內的全部奇數。
(List.Numbers可以快捷實現,本節重點介紹的是循環,因此不使用)
~~~~~~~~~~
我們知道全體奇數可以通過自然數來構造,因此我們容易想到:
= List.Transform({1..10},(x)=>2*x-1)
我們回顧下實現過程,20以內最大的奇數是19,而19=2*10-1,因此我們需要構造{1..10}這樣的list來進行遍歷,通項公式就是(x)=2*x-1。
~~~~~~~~~~
案例3:使用質數的定義證明19是質數。
~~~~~~~~~~
質數定義:在大于1的自然數中,除了1和它本身以外不再有其它的因數。
所謂因數就是能夠整除給定的數,我們可以通過Mod函數來判斷,只要余數為0,那就是因數,因此我按照定義把2~18全部逐一驗證遍即可:
= List.Min(List.Transform({2..18},(x)=>Number.Mod(19,x)))
上述表達式結果為1,說明2~18分別除19得到的余數list中最小值為1,沒有0,也就是19除了1和自身外不再有其它的因數了,因此19為質數。
迭代:
我們先考察一個簡單的計數問題:10以內偶數的個數是多少?
顯然使用List.Count是無法得到結果的,最簡單的思路就是我們假設有一個容器專門用來實現計數的功能,逐一判斷10以內的每一個自然數是不是偶數,如果是偶數,計數器就加一。那么對于計數器的上一個結果來說,每次新出現一個偶數,計數器的結果就“迭代”更新了一次。
上述解決問題的過程就是接下來要介紹的迭代循環了。
~~~~~~~~~~
案例4:10以內的奇數有多少個?
~~~~~~~~~~
在M中,Accumulate函數具有積累的功能,可以實現“容器”迭代的功能,我們通過Number.IsOdd函數來對每一個數做是否為奇數的判斷:
List.Accumulate(list as list, seed as any, accumulator as function) as any
這個函數需要給定一個種子參數作為起始值,然后根據指定的function進行迭代運算。
= List.Accumulate({1..10},0,(x,y)=>if Number.IsOdd(y) then x+1 else x)
我們回顧下迭代的過程:
首先計數器(seed)的初始值為0,然后開始對{1..10}依次做判斷,(x,y)的x就是上一次的seed,而y就是當前{1..10}的一個值;最初x為0,y為1,然后y依次為2~10,x對應的為上一次function運算結束后的那個值;因此1判斷為奇數時x就執行了then x+1(這里面的x還是seed的值:0),因此當對y=2做判斷時,就執行了else x,x值不變,當全部判斷完時,輸出x最后的值,也就是5,這樣就完成了整個迭代循環。
~~~~~~~~~~
案例5:使用迭代循環的方法倒序輸出“Hello World!”。
~~~~~~~~~~
既然要求使用迭代,那么就要想清楚“容器”里面到底裝什么內容,這個例子需要倒序輸出,我們不妨將最終輸出的結果理解為一個一個字符拼接成的字符串,這樣我們就知道怎么構造這個字符串容器了:
= List.Accumulate({0..(Text.Length("Hello World!")-1)},"",(x,y)=>
x&Text.At("Hello World!",Text.Length("Hello World!")-y-1))
這里使用了一個小技巧,使用了N-x的結構構造一個降序的list,實現順序迭代時是從后向前進行的,然后使用字符串容器保存這些迭代的字符串,最終輸出。
遞歸:
前面介紹過@這個符號,它的作用是調用后面的內容,后面接函數時,就是遞歸了。
我們通過一個簡單的例子來看看遞歸的作用和用法:
~~~~~~~~~~
案例6:計算9!(不使用Number.Factorial)。
~~~~~~~~~~
let
factorial=(x)=>if x=1 then 1 else x*@factorial(x-1)
in
factorial(9)
我們把這個語句和高中學的數列對應起來就好理解了,首先告知首項f(1)=1,然后使用遞歸的方法寫出遞推公式f(n)=n*f(n-1),最后求第9項。
不難發現,遞歸具備如下幾個特點:
1.函數調用了自身,并用“@”進行函數名的標識;
2.調用自身時,參數會發生變化,避免無限遞歸;
3.通過 if 語句對特定參數的函數值進行了定義。
~~~~~~~~~~
案例7:求36和63的最小公倍數。
~~~~~~~~~~
我們都知道兩個整數的最小公倍數等于兩數之積除以兩數的最大公約數,因此這個案例如果能夠解決如何求最大公約數,問題也就迎刃而解了,下面先介紹一種比較高效的算法。
輾轉相除法:
用較小數除較大數,再用出現的余數(第一余數)去除除數,再用出現的余數(第二余數)去除第一余數,如此反復,直到最后余數是0為止,最后的除數就是這兩個數的最大公約數。
這里面核心遞歸的是什么步驟?這個一定要理清楚,否則就寫不出正確的遞歸語句。看完介紹后可以發現,最核心的一步就是用余數去除除數,如果不好理清余數除數的關系,你就這樣理解:用最小的那個數去除中間的數。(被除數最大,除數第二排中間,余數最小)
let
GCD=(x,y)=>if y=0 then x else @GCD(y,Number.Mod(x,y))
in
36*63/GCD(36,63)
我們用數列的遞推公式思路把這個案例的核心步驟理一理:
GCD(x,y)=GCD(y,Number.Mod(x,y))
用語言描述就是:大數和小數的最大公約數等于小數和兩數取余的最大公約數。
for循環和while循環:
前面的遍歷基本上可以實現for循環的效果了,那么while循環與哪一個函數最接近呢?
這里介紹一個用得稍微少一點的循環函數:
List.Generate(initial as function,condition as function,next as function, optional selector as nullable function) as list
~~~~~~~~~~
例如下面這段Python代碼:
#!/usr/bin/python3
i=0
while i<10:
i=i+1
print(i)
~~~~~~~~~~
使用M語句就是:
let
list=List.Generate(()=>1, each _ <11, each _ + 1)
in
list
這個函數的特點也是有初始值,然后有一個條件判斷,條件內會一直循環下去。
下篇筆記:《循環嵌套與綜合應用》
總結
以上是生活随笔為你收集整理的for each循环_Power Query — 循环初步的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html input不能输入小数_【Py
- 下一篇: 笔记本电脑处理器_高通提示低成本5G芯片