迴圈轉遞迴(稿)-文字檔
跳至導覽
跳至搜尋
使用Typst撰寫,生成PDF檔。
#import "@preview/algo:0.3.3": * #set par( justify: true, leading: 1em, ) #let CJKNonCJKSpacing = 0.2em #let RegExCJK = "[\p{Han}\p{Hiragana}\p{Katakana}\p{Hangul}\p{Bopomofo}]" #let RegExNonCJK = "[^\p{Han}\p{Hiragana}\p{Katakana}\p{Hangul}\p{Bopomofo}]" #let RegExNonCJK = "[^\p{Han}\p{Hiragana}\p{Katakana}\p{Hangul}\p{Bopomofo}]" #show regex(RegExCJK+RegExNonCJK) : it => { text[#it.text.first()#box(width: CJKNonCJKSpacing,"")#it.text.last()] } #show regex(RegExNonCJK+RegExCJK) : it => { text[#it.text.first()#box(width: CJKNonCJKSpacing,"")#it.text.last()] } #set page( numbering: "1 / 1", ) #set text( size: 12pt, font: ("Linux Libertine", "Noto Serif CJK TC"),// "Linux Libertine Mono O" ) #let hd1(txt) = box[ #set text( font: ("FreeSans", "Noto Sans CJK TC"),// "Linux Libertine Mono O" ) = #txt ] #let hd2(txt) = box[ #set text( font: ("FreeSans", "Noto Sans CJK TC"),// "Linux Libertine Mono O" ) == #txt ] #let remark(txt, number) = box(width:100%, fill: rgb("#EBFAEF"), [ #v(1em) #align(center,[#txt~~#sub([[#number]])]) #v(1em)]) #hd1("迴圈轉遞迴(稿)") _2023-07-20 by Yoxem_(2024-10-05:修正發佈日期) 迴圈和遞迴其實習習相關,最近略讀論文 _Transforming Programs into Recursive Functions_ (Myreen & Gordon, 2008),提到迴圈如何轉換成遞迴的方法。我在這邊想到一些思路。 一個迴圈可以理解為條件(cond)和滿足條件即執行的程式本體(body),即: ```python while (cond): body ``` 其中一些while外的變數,被body處理之後,在body的最後就會變。假設輸入到body改變數值的變數為$x_i space (i=0, 1,dots)$,輸出的結果是$x^prime_i$,那我們可以假設$bold(accent(x,->))$是$x_i$組成的向量,$bold(accent(x,->)^prime)$是是$x^prime_i$組成的向量,則body可以視為函數,且$bold(accent(x,->)^prime) = "body"(bold(accent(x,->)))$。 舉下列求除法商數的$"Quotient"(a, b) = a$除以$b$的商數"為例,為求簡化,$a, b$均為自然數: #algo( title: "Quotient", parameters: ("a","b"), strong-keywords: true // display keywords in bold )[ let q = 0 #comment[商數] \ while $a gt.eq b$:#i#comment[cond]\ // $a <- a - b$ #comment[body的第1行]\ $ q <- q + 1$ #comment[body的第2行]:#d\ return q ] 其中 $a, b$ 和 $q$ 是body外的變數,$"body"(a, b, q)$ $= (a-b, b, q+1)$,比較下,$bold(accent(x,->)) = (a, b, q)$,$bold(accent(x,->)^prime) = (a-b, b, q+1)$然後可以把`while(cond){body}`組合$bold(accent(x,->))$想成這樣的OCaml函數: ```ML (* 以下括號非必要則略 *) WHILE cond body XVec= if cond then let NewXVec = body XVec in (* NewXVec = body XVec *) WHILE cond body NewXVec (* 帶 NewXVec 重新進去WHILE函數,做if判斷的手續 *) else XVec (* XVec 不變 *) ``` 另可以這樣改寫,其中cond視為輸入$bold(accent(x,->))$的函數: ```ML let WHILE cond body XVec = if (cond XVec) then WHILE cond (body XVec) else XVec ``` 那麼這個除法可以這樣寫成類OCaml風格: ```ML …… let q = 0 in (*原樣*) while a >= b do let a = a - b let q = q + 1 ``` 但是要找出while外變數,可以找出a, b, q,且把狀態更新改為 let…in 模式,則 ```ML …… let q = 0 in let cond = fun a b q -> a >= b in WHILE cond (fun a b q ->( let a = b - a in let q = q + 1 in (WHILE cond [a; b; q] ))) a b q (* XVec*) ``` 用let的規約規則轉換一下變成尾遞迴 ```ML …… let q = 0 in let cond = fun a b q -> a >= b in WHILE cond (fun a b q ->( (WHILE cond [b - a; b; q + 1] ))) a, b, q ``` 最後修改: ```ML …… let cond = fun a b q -> a >= b in letrec WHILE cond = (if (cond X) then (WHILE cond (b - a) b (q + 1)) else [a; b; q]) in WHILE cond a b q (* 執行 LOOP *) ```