我想這個作業的意思可能是,要求
不用內建的 PUSH 跟 POP 巨集,並且
重新定義 PUSH 跟 POP 指令。
另外題目有說要把它定義改成用來
操作某個全域的堆疊,下面姑且把這個堆疊(其實是把串列當堆疊用)定義成 *my-global-stack* (名稱自取,
至於用兩個星號 * 包起來,是為了要提醒我自己那是
全域變數)。
在 ; 後面,直到行末都會被視為是註解喔,可以省略掉。
複製內容到剪貼板
代碼:
(DEFVAR *my-global-stack*) ; 宣告全域變數 *my-global-stack*
(DEFUN initialize ()
(SETQ *my-global-stack* NIL) ; 把 *my-global-stack* 設定成 NIL ,也就是空串列 ()
; 傳回值會是最後一行,也就是上面那行的 NIL 喔。
)
(DEFUN push (n)
(SETQ *my-global-stack* (CONS n *my-global-stack*)) ; 利用 CONS 把 *my-global-stack* 串列的最前方加入 n 並且把整個新的串列設成是 *my-global-stack*
n ; 因為題目要求最後要傳回新加入的值,所以就把 n 擺在最後一行當作是傳回值
)
(DEFUN pop ()
(LET ((first-element (CAR *my-global-stack*) )) ; 利用 CAR 取出 *my-global-stack* 串列的第一個元素 ,並且把它設成區域變數 first-element
(SETQ *my-global-stack* (CDR *my-global-stack*)) ; 把 *my-global-stack* 去掉第一個元素,然後把新的串列丟給 *my-global-stack*
first-element) ; 因為題目要求最後要傳回的是第一個元素,所以放在這裡當作是傳回值
)
(DEFUN list-stack()
*my-global-stack* ; 傳回整個串列,所以把 *my-global-stack* 當作是傳回值
)
讓它依序去執行如下
複製內容到剪貼板
代碼:
>(initialize)
NIL
到這裡時, *my-global-stack* 可視範圍是全域的這個串列被初始化(給值)成 NIL ,也就是空串列 ()
複製內容到剪貼板
代碼:
>(push 'foo)
FOO
到這裡時,剛剛的 *my-global-stack* 空串列被利用 CONS 加進去 foo ,變成是 (foo) 串列啦,本來是會傳回 (foo) 的,
可是我們在定義函數的最後一行多加了點東西,讓它傳回值變成是一開始丟進來的字符 foo 了,。
複製內容到剪貼板
代碼:
>(push 'bar)
BAR
到這裡的時候, CONS 這個函數又把 BAR 給加到 (FOO) 這個串列的最前面,變成是 (BAR FOO) ,然後指定給 *my-global-stack* ,
然後依照函數定義的最後一行,依然把當初傳入的參數當作是傳回值。
複製內容到剪貼板
代碼:
>(list-stack)
(BAR FOO)
這裡是要列出題目要求的可視範圍是全域 *my-global-stack* 堆疊(其實就是串列拿來當堆疊用而已),所以輸出結果是 (BAR FOO)。
複製內容到剪貼板
代碼:
>(pop)
BAR
取出串列的最前面的一個元素,也就是 BAR 。當然,要把 *my-global-stack* 重設成去掉第一個元素的新串列。
而取出來的 BAR 要擺在最後一行,才會變成是傳回值。
複製內容到剪貼板
代碼:
>(list-stack)
(FOO)
確認一下 *my-global-stack* 現在是蝦咪,所以現在是去掉 BAR 之後,就變成是 (FOO) 了。
而關於 POP 跟 PUSH 名稱重複的問題,我想可能跟實作環境有關,我試過 Allegro Common Lisp 它不讓我複寫已存在的巨集,
可是,改用 Corman Lisp 就讓我用下面的函數定義重新複寫了,所以如果你的實作環境不容許你複寫,也可以把下面定義 PUSH 跟 POP 的
名稱買成是 my-push 跟 my-pop (或是如你原本寫的 pus 跟 po 也都可以)。
其實我對巨集沒有很熟,呵呵,我也是好玩自學而已,也是個新手,
所以以後如果我看完書發現怎樣
強制要求用函數重寫巨集,那再多回一篇,呵呵。