2014-10-09 29 views
3

我想在用Lisp編寫的遞歸函數中實現一個基本條件,但是我無法做到這一點,因爲在Lisp中沒有返回語句。Lisp中有什麼類似於C的return語句嗎?

我的Lisp代碼就是基於這種C代碼

if (n==0) return; 

如何在Lisp中實現這一點?

+0

Lisp經常用作函數式編程語言。這意味着對term/stdout的輸出被認爲是「副作用」,並且在某種程度上(沒有那麼重的負面含義)_unclean_。當然,Common Lisp中的任何函數都會評估其最終形式的值。 – miercoledi 2014-10-10 02:48:54

+0

因此,對於類似_abort的東西,如果函數中的n等於0_,我們將調用**#'foo **您可能會使用條件 - 在大多數情況下,這可能是**#'cond * *函數,但這裏是一個簡單的實現,使用**#',這是一個經常內置的宏。 '(when(= n 0)(return-from foo nil))'在上面的_when_表單中,_nil_可以被更復雜的形式所取代,但是如果這是必要的,你可能想要構造一個'(cond)'或'(case)'等等。 – miercoledi 2014-10-10 02:50:46

回答

5

Common Lisp有特殊的形式RETURN-FROM(及其相對的RETURN)做你想做的。

(defun accumulate-list (list) 
    (when (null list) 
    (return-from accumulate-list 0)) 
    (+ (first list) 
    (accumulate-list (rest list)))) 

話雖如此,在編寫遞歸函數時我更喜歡使用COND

(defun accumulate-list (list) 
    (cond 
    ((null list) 
     0) 
    (t 
     (+ (first list) 
     (accumulate-list (rest list)))))) 
+0

請注意,如果'cond'中的子句沒有主體形式,那麼如果測試表單返回非零,則返回測試表單的結果。這意味着你可以編寫'(cond((null list)0)((+(first list)...)))';在這種情況下不需要't'。 – 2014-10-10 01:15:35

+1

@JoshuaTaylor(我的評論不適用於CL)。在Scheme中,使用'else'代替使用表達式作爲測試表單更好,因爲'else'表達式處於尾部位置,而測試表單仍然必須進行真實性測試。 – 2014-10-10 02:41:33

+0

@JoshuaTaylor謝謝。我不知道。 – 2014-10-10 05:04:54

4

對於Algol程序員(或者C,Java,perl等許多方言中的一個)每個表達式在LISP中的工作方式類似於「返回表達式」。例如:

{ 
    int x = 10; 
    if(x == 10) 
    return 10 * 5; 
    else 
    return 5 * 19; 
} 

在LISP這可以寫成這樣的:

;; direct version 
(let ((x 10)) 
    (if (= x 10) 
     (* 10 x) 
     (* 5 x))) 


;; if can be anywhere 
(let ((x 10)) 
    (* x 
    (if (= x 10) 
     10 
     5)))) 

正如你可能已經注意到LISP if更像是三元運算符(expression ? consequent : alternative)比一個C if

編輯

現在您已經添加了一個例子USNG用C return要翻譯我看到你沒有使用return返回一個值,但作爲一個goto提前退出功能。由於goto is still considered harmful只是使用CL return-from並不總是正確的答案,即使這肯定是最好的直譯。

在任何LISP中,即使您不打算使用它(對於稱爲副作用的函數),您仍需要提供返回值。如果你不打算使用的值,你可以只使用nil

(if (zerop x) 
    nil 
    (something-else x)) 

如果你需要一個以上的語句(副作用)您使用letprogn或切換整個事情的cond

;; using let to bind as well as implicit progn 
(if (zerop x) 
    nil 
    (let ((tmp (gethash x *h*))) 
     (setf (gethash x *h*) (+ x 1)) 
     (something-else tmp))) 

;; using progn 
(if (zerop x) 
    nil 
    (progn 
     (setf (gethash x *h*) (+ (gethash x *h*) 1)) 
     (something-else (gethash x *h*)))) 

;; using cond 
(cond ((zerop x) nil) 
     (t 
     (setf (gethash x *h*) (+ (gethash x *h*) 1)) 
     (something-else (gethash x *h*)))) 
+0

我最近在某處讀過那些被認爲有害的GOTO,當Dijkstra發表那篇文章是那種自由跳進和跳出代碼塊的類型(使得這些塊在概念上有多個進入點和退出點),事實上在那即使是子程序,在程序集編程時也被認爲是新穎*範例*(函數調用協議只是程序員要保留的約定)。所以有人認爲(我回過頭來看我們的問題),算法上使用'return-from'時沒有什麼問題。 :) – 2014-10-10 11:25:22

+0

@WillNess我喜歡它的概念仍然被認爲是有害的,並且有例外。這樣你每次寫它時都會考慮它。這就像Scheme中的'call/cc',我幾乎從不使用它。 – Sylwester 2014-10-10 11:38:41

+0

我個人喜歡結構化的本地化gotos。 YMMV,他們*說。 :)最近我用C編寫了一些循環,有兩個入口點,它們有時不得不執行一些內部初始化(在'concatMap ... span ...'範例之後,基本上是通過塊來處理,然後生成一個平坦的輸出) 。與*標誌* *似乎非常* messier *,對我來說。 – 2014-10-10 11:49:34

1

你只需要把if是整個身體,並把nil而非return,如果你真的想返回返回「無」。

因此將所有數字加起來從0到n:

(defun somefunc (n) 
    (if (zerop n) 
     0 
     (+ n (somefunc (- n 1))))) 

所以,如果n==0,遞歸函數返回0。否則執行另外補充nf(n-1)並返回結果。 (請注意,這是而不是的理想算法,只是一個遞歸函數的例子)。請記住,Lisp返回函數中最後一次執行的任何expr的值作爲其值。如果n==0,則返回0.如果n > 0,則返回+ expr的結果。

如果您需要多步測試(例如,以確保您沒有通過負數),cond是(如前所述)。無論哪種方式,執行的最後一件事的價值是該函數的價值。