loop 流程控制


如果需要重複執行某個流程,可以使用 loop,它也是一種區塊,因此看不到區塊前對堆疊的置入的數值,可以定義結果型態,若沒有定義,那離開區塊前必須清空堆疊。

loopblockif 不同的是,使用 br 指定分支流程至 loop 時,是指分支至 loop 開頭,而不是 end

例如,從 1 顯示到 10:

(module
    (import "env" "log" (func $log (param i32)))
    (func $main
        (local $i i32) 
        (set_local $i (i32.const 1))                        ;; i = 1
        loop $LOOP
            (i32.le_s (get_local $i) (i32.const 10))        ;; i <= 10
            if
                get_local $i
                call $log
                (set_local $i                               ;; i = i + 1
                    (i32.add (get_local $i) (i32.const 1)))
                br $LOOP
            end
        end
    )
    (start $main)
)

loop 不會自動形成迴圈,如果不使用 br 指定流程分支至 $LOOP,就不會重複進行運算,因此,想要製作無窮迴圈,基本上就是:

loop
    br 0 
end

來點真正的應用好了,例如,來計算 30 與 12 的最大公因數:

(module
    (import "env" "log" (func $log (param i32)))
    (func $main
        (local $m i32) (local $n i32) (local $r i32)
        (set_local $m (i32.const 30))                            ;; m = 30
        (set_local $n (i32.const 12))                            ;; n = 12
        loop $LOOP
            (i32.ne (get_local $n) (i32.const 0))                ;; n != 0
            if
                (set_local $r                                    ;; r = m % n
                    (i32.rem_s (get_local $m) (get_local $n)))

                (set_local $m (get_local $n))                    ;; m = n
                (set_local $n (get_local $r))                    ;; n = r
                br $LOOP  
            end
        end
        get_local $m
        call $log
    )
    (start $main)
)

來看個費式數的例子,求第 10 個費式數:

(module
    (import "env" "log" (func $log (param i32)))
    (func $main
        (local $n i32)                                           ;; 求第 n 個費式數
        (local $a i32) (local $b i32)
        (local $i i32) (local $tmp i32)

        (set_local $n (i32.const 10))                            ;; n = 10

        (i32.or                                                  ;; n == 0 || n == 1
            (i32.eqz (get_local $n))
            (i32.eq (get_local $n) (i32.const 1))
        )

        if
            get_local $n
            call $log
        else
            (set_local $b (i32.const 1))                         ;; b = 1
            (set_local $i (i32.const 2))                         ;; i = 2
            loop 
                (i32.le_s (get_local $i) (get_local $n))         ;; i <= n
                if
                    (set_local $tmp (get_local $b))              ;; tmp = b
                    (set_local $b                                ;; b = a + b
                        (i32.add (get_local $a) (get_local $b)))
                    (set_local $a (get_local $tmp))              ;; a = tmp
                    (set_local $i                                ;; i = i + 1
                        (i32.add (get_local $i) (i32.const 1)))
                    br 1
                end
                get_local $b
                call $log
            end
        end
    )
    (start $main)
)