迴圈轉遞迴(稿)-文字檔

出自Tan Kian-ting的維基
於 2024年10月5日 (六) 16:00 由 Tankianting討論 | 貢獻 所做的修訂
跳至導覽 跳至搜尋
  1. import "@preview/algo:0.3.3": *


  1. set par(
 justify: true,
 leading: 1em,

)

  1. let CJKNonCJKSpacing = 0.2em
  1. let RegExCJK = "[\p{Han}\p{Hiragana}\p{Katakana}\p{Hangul}\p{Bopomofo}]"
  2. let RegExNonCJK = "[^\p{Han}\p{Hiragana}\p{Katakana}\p{Hangul}\p{Bopomofo}]"
  1. let RegExNonCJK = "[^\p{Han}\p{Hiragana}\p{Katakana}\p{Hangul}\p{Bopomofo}]"


  1. show regex(RegExCJK+RegExNonCJK) : it => {
   text[#it.text.first()#box(width: CJKNonCJKSpacing,"")#it.text.last()]

}

  1. show regex(RegExNonCJK+RegExCJK) : it => {
   text[#it.text.first()#box(width: CJKNonCJKSpacing,"")#it.text.last()]

}

  1. set page(
 numbering: "1 / 1",

)

  1. set text(
   size: 12pt,
   font: ("Linux Libertine", "Noto Serif CJK TC"),// "Linux Libertine Mono O"
   )
  1. let hd1(txt) = box[
   #set text(
   font: ("FreeSans", "Noto Sans CJK TC"),// "Linux Libertine Mono O"
   )
   = #txt ]
  1. let hd2(txt) = box[
   #set text(
   font: ("FreeSans", "Noto Sans CJK TC"),// "Linux Libertine Mono O"
   )
   == #txt ]
  1. let remark(txt, number) = box(width:100%,

fill: rgb("#EBFAEF"), [

  1. v(1em)
  2. align(center,[#txt~~#sub(#number)])
  3. v(1em)])
  1. 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$均為自然數:

  1. 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 *) ```