2013-10-04 25 views
0

我必須在Scheme(R5RS)中生成一個函數,其功能如下: (power-close-to bn) 而且它必須返回一個整數,我稱之爲「e」即: b^e> n 使用b,e和n整數。錯誤:for:undefined(Scheme)

所以,如果我們這樣做: (電源關閉至2 10) 它必須返回4,因爲4是其中B^E的第一個整數>ň 我在迭代的方式使這個功能但我必須以遞歸形式進行。 所以這是我的代碼:

(define e 0) 
(define (power-close-to b n) 
    (for ((e (< (expt b e) n)) 
    (+ e 1)) 
    e)) 

但是當我嘗試它,方案給出了以下錯誤:「爲:未定義;」 因此,我的方案似乎並不知道程序「for」,但我在互聯網上看到了多個Scheme代碼,所以我不明白爲什麼在我的情況下他說他不知道「for」。

感謝您的幫助!編輯:我試着讓它遞歸,這是我是如何做到的,但我認爲它仍然是迭代的,我真的不知道如何讓它遞歸。

(define e 0) 
(define (power-close-to b n) 
    (if (< (expt b e) n) 
    (and (set! e (+ e 1)) (power-close-to b n)) 
    e)) 

我也試過了,但是當我嘗試它,它從來沒有打印任何東西,永遠不會結束(但這是遞歸的(我認爲))

(define e 0) 
(define (power-close-to b n) 
    (if (< (expt b e) n) 
    (* b (power-close-to b n)) 
    e)) 
+0

你混淆了「迭代」和「遞歸」的含義。在Scheme中,編寫遞歸解決方案很常見,但其中一些遞歸解決方案在寫爲尾遞歸時表現爲_iteratively_。問題中的第一個實現將不起作用,因爲您的解釋器中不存在'for' –

+0

您使用的是什麼Scheme實現? – uselpa

回答

2

當別人問你變身遞歸過程在迭代之中,它一般意味着你必須使用尾遞歸,而不是你應該使用語言的循環結構。

請注意,並非所有Scheme解釋器都提供for循環(大多數將提供do循環,但我認爲這不是練習的要點)。您報告的錯誤意味着您的解釋器沒有for構造,因此很可能您需要以尾遞歸方式重寫該過程。我給你我的意思的例子,這是一個遞歸的階乘:

(define (fac n) 
    (if (zero? n) 
     1 
     (* n (fac (sub1 n))))) 

(fac 10) 
=> 3628800 

現在同樣的過程可以寫在它生成一個迭代過程(即使語法,它這樣的方式仍然使用遞歸):

(define (fac n acc) ; now the result is stored in the accumulator parameter 
    (if (zero? n)  ; when recursion ends 
     acc   ; return accumulator 
     (fac (sub1 n) (* n acc)))) ; else update accumulator in each iteration 

(fac 10 1) ; initialize the accumulator in the right value 
=> 3628800 

重點是什麼,你問?該過程的第二個版本是以尾遞歸形式編寫的(注意遞歸調用結束後沒有什麼可做的事情),所以編譯器技巧叫做tail call optimization,並且該過程在恆定的堆棧空間中運行,效率與以其他非功能語言的循環 - 使遞歸調用非常便宜。現在嘗試編寫您的power-close-to實現,以便使用尾部調用。

+0

感謝您的回答,在這種情況下,我已經完成的功能是迭代形式。所以現在我試着讓它遞歸,但我有一個問題。在遞歸時,就像我在互聯網上看到很多東西,並且在你的例子中,最後我必須做一些事情,比如+, - ,*,/但在我的情況下,我不能那樣做,因爲我有用1加強「e」,但e不是我函數的參數。那麼我該如何做這個遞歸呢? – HyperZ

+1

@HyperZ開始傳遞'e'作爲參數,將這個值作爲全局變量保留是一個非常糟糕的主意,這不是一個好的方法。不要使用'set!',只要你不斷地將它作爲參數傳遞並更新爲參數就沒有必要改變它的值,就像我在例子中用'acc'所做的那樣。 –

+0

是的,但後來我有(*, - ,*,/)用我的函數做一個,就像你在例子中做的那樣:(* n(fac(sub1 n)))))但是我的問題是我不必做這4件事中的任何一件與我的功能,我只需要增加「e」1,它必須是一個遞歸的形式。所以這就是爲什麼我不明白怎麼做:(我也可能不會引入第三個參數 – HyperZ

0

與傳統環路最接近(也是最方便)的是命名的let(搜索「named let」here)。這將是這樣的:

(define (power-close-to b n) 
    (let loop ((e 0)) 
    (if (<= (expt b e) n) 
     (loop (+ e 1)) 
     e))) 

(display (power-close-to 2 10)) 

循環變量是在設級別定義,所以它是本地環路(在全球沒有在你的例子)。除此之外,代碼看起來與你的非常相似。

命名咱們創建一個內部功能,如下,你也可以表達出來:

(define (power-close-to b n) 

    (define (loop e) 
    (if (<= (expt b e) n) 
     (loop (+ e 1)) 
     e)) 

    (loop 0)) 

不幸的是,R5RS不爲論據支持的默認值,但如果你想避免內部功能你可以去:

(define (power-close-to b n e) 
    (if (<= (expt b e) n) 
     (power-close-to b n (+ e 1)) 
     e)) 

但你不得不與調用它的額外0喜歡

(power-close-to 2 10 0) 
+0

ü給了我兩個迭代的形式,我也有迭代的形式。但我必須做一個遞歸的形式。 @OscarLopez我嘗試使用let,但它也是迭代的,因爲我(+ e 1)然後再次調用我的函數。 – HyperZ

+0

@HyperZ循環正在調用自己,這是遞歸的。 – uselpa