我正在尝试编写一个创建斐波纳契序列列表的函数,但在列表中找到某个值时停止,然后返回该列表(我希望这是有意义的)。
所以例如,如果我给它fib-list(55),函数应该返回:(1 1 2 3 5 8 13 21 34 55)
所以它不是我想要的第55个斐波那契数字,它的列表值为55。
到目前为止我返回列表的代码如下所示:
; Create a list of the fibonacci sequence up to n.
(define (fib-list n)
; n = n, f2 = 1, f1 = 1, fs = a list.
(let loop ((n n) (f2 1) (f1 1) (fs (list)))
(cond
; If n = 0, return reversed list.
((zero? n) (reverse fs))
; Check if n is in list. If so, return list.
((equal? n (car fs)) fs)
;Else, find the next fibonacci number and add it to the list.
(else (loop (- n 1) f1 (+ f2 f1) (cons f2 fs))))))
(display (fib-list 55))
我的主要问题是找到一个元素是否在列表中,因为目前我只是在我尝试编写((equal?
语句的行上出错。
错误说:
mcar: contract violation
expected: mpair?
given: '()
我对Scheme还是非常新的,所以我对整个语言的理解并不是很好。因此,请告诉我为什么我的代码糟透/没有意义。
(list)
创建一个空列表,在第一次迭代中你会到达(car fs)
,它试图将car
应用于空列表,这是一个错误。
您的代码似乎对n
的性质有点困惑。
你的描述说它是你想要的最大数字,但你正在递归,就像你想要n:th
斐波那契数字一样 - 终止于(zero? n)
并递归(- n 1)
。
当您递归时,您仍然在寻找达到相同限制的数字。 因此,您不应该减少限制并终止于零,您应该单独保留限制并在达到更大数字时终止。
这是我写它的方式:
(1 1)
cons
它到累加器并递归“新”最后两个斐波纳契数。在代码中:
(define (fib-list n)
(let loop ((f2 1) (f1 1) (fs '(1 1)))
(let ((next (+ f1 f2)))
(if (> next n)
(reverse fs)
(loop f1 next (cons next fs))))))
这是另一种使用延续传递方式的方法。通过在循环中添加continuation参数,我们有效地创建了自己的返回机制。此实现的一个独特属性是输出列表以正向顺序构建,当n
达到零时不需要反转。
(define (fib-list n)
(let loop ((n n) (a 0) (b 1) (return identity))
(if (zero? n)
(return empty)
(loop (sub1 n)
b
(+ a b)
(lambda (rest) (return (cons a rest)))))))
(fib-list 10)
;; '(0 1 1 2 3 5 8 13 21 34)
仔细阅读你的问题,在fib-list(N)
你需要N
作为循环的停止条件,而不是列表中的第N个术语。这实际上更容易实现,因为不需要计算生成的术语数量。
(define (fib-list max)
(let loop ((a 0) (b 1) (return identity))
(if (> a max)
(return empty)
(loop b
(+ a b)
(lambda (rest) (return (cons a rest)))))))
(fib-list 55)
;; '(0 1 1 2 3 5 8 13 21 34 55)
(fib-list 1000)
;; '(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987)
car
function?car
函数接受列表的第一个元素,但如果列表为空,则它没有第一个元素。 fs
列表开头是空的。当您尝试获取空列表的第一个元素时,您会收到以下错误消息:
> (car (list))
mcar: contract violation
expected: mpair?
given: ()
如果列表不为空,那么它有第一个元素,并且没关系:
> (car (list 4 5 6))
4
但是,你的评论“检查n是否在列表中”让我相信(equal? n (car fs))
不是你想要的。用于确定元素是否在列表中的函数称为member
。
#!r6rs
(import (rnrs base)
(rnrs lists))
> (if (member 4 (list 1 2 4 8))
"it's in the list"
"go fish")
"it's in the list"
> (if (member 5 (list 1 2 4 8))
"it's in the list"
"go fish")
"go fish"
因此,用(equal? n (car fs))
替换(member n fs)
测试,您的代码如下所示:
; Create a list of the fibonacci sequence up to n.
(define (fib-list n)
; n = n, f2 = 1, f1 = 1, fs = a list.
(let loop ((n n) (f2 1) (f1 1) (fs (list)))
(cond
; If n = 0, return reversed list.
((zero? n) (reverse fs))
; Check if n is in list. If so, return list.
((member n fs) fs)
;Else, find the next fibonacci number and add it to the list.
(else (loop (- n 1) f1 (+ f2 f1) (cons f2 fs))))))
> (fib-list 55)
(10946 6765 4181 2584 1597 987 610 377 233 144 89 55 34 21 13 8 5 3 2 1 1)
这不是你想要的答案;你想要(1 1 2 3 5 8 13 21 34 55)
。
55
?其中一个问题是n
被遮蔽,就像在这个表达式中一样:
> (let ([n 5])
(let ([n 10])
n))
10
身体中的n
指的是10
而不是5
。
结果是超过55
因为在循环内部n
被遮蔽并且变成了不同的数字。我猜你的评论“检查n是否在列表中”,你的意思是“检查原始n是否在列表中”。要做到这一点,你必须重命名其中一个n
s:
> (let ([orig-n 5])
(let ([n 10])
orig-n))
5
在您的代码的上下文中:
; Create a list of the fibonacci sequence up to n.
(define (fib-list orig-n)
; n = n, f2 = 1, f1 = 1, fs = a list.
(let loop ((n orig-n) (f2 1) (f1 1) (fs (list)))
(cond
; If n = 0, return reversed list.
((zero? n) (reverse fs))
; Check if orig-n is in list. If so, return list.
((member orig-n fs) fs)
;Else, find the next fibonacci number and add it to the list.
(else (loop (- n 1) f1 (+ f2 f1) (cons f2 fs))))))
> (fib-list 55)
(55 34 21 13 8 5 3 2 1 1)
这是更接近的,但它是相反的。你有两个基本案例,(zero? n)
案例和(member orig-n fs)
案例。其中一个是逆转的,其中一个不是。更改它们以调用反向修复它:
; Create a list of the fibonacci sequence up to n.
(define (fib-list orig-n)
; n = n, f2 = 1, f1 = 1, fs = a list.
(let loop ((n orig-n) (f2 1) (f1 1) (fs (list)))
(cond
; If n = 0, return reversed list.
((zero? n) (reverse fs))
; Check if orig-n is in list. If so, return reversed list.
((member orig-n fs) (reverse fs))
;Else, find the next fibonacci number and add it to the list.
(else (loop (- n 1) f1 (+ f2 f1) (cons f2 fs))))))
> (fib-list 55)
(1 1 2 3 5 8 13 21 34 55)
这对于像55
这样的大型斐波那契数字是正确的,但它对小数字仍然有些奇怪:
> (fib-list 2)
(1 1)
> (fib-list 3)
(1 1 2)
如果你只想让它在到达orig-n
时停止,那么也许不需要减少n
参数,实际上是让它太早停止。删除它(并删除零检查)使member
检查唯一的停止案例。
这很危险,因为如果你给它一个非Fibonacci数作为输入它可能会进入一个无限循环。但是,它修复了一些小数例:
; Create a list of the fibonacci sequence up to n.
; The `orig-n` MUST be a fibonacci number to begin with,
; otherwise this loops forever.
(define (fib-list orig-n)
; f2 = 1, f1 = 1, fs = a list.
(let loop ((f2 1) (f1 1) (fs (list)))
(cond
; Check if orig-n is in list. If so, return reversed list.
((member orig-n fs) (reverse fs))
;Else, find the next fibonacci number and add it to the list.
(else (loop f1 (+ f2 f1) (cons f2 fs))))))
> (fib-list 55)
(1 1 2 3 5 8 13 21 34 55)
> (fib-list 2)
(1 1 2)
> (fib-list 3)
(1 1 2 3)
最后,考虑如果你给它一个像56
这样的数字会发生什么。
> (fib-list 56)
;infinite loop
这是一个你尚未在你的问题中指定的设计决定,但有办法解决它。
orig-n
或更高我应该指定我需要检查是否有一个大于OR的数字等于
orig-n
。我仍然可以使用member
函数来检查这个或者我需要使用不同的东西吗?
你将不得不使用不同的东西。在文档中的member
上方是memp
函数(在这种情况下你也可以使用exists
)。 mem
是成员的缩写,而p
是“谓词”的缩写。它确定列表的任何成员是否与某个谓词匹配。
> (if (memp positive? (list -4 -2 -3 5 -1))
"one of them is positive"
"go fish")
"one of them is positive"
> (if (memp positive? (list -4 -2 -3 -5 -1))
"one of them is positive"
"go fish")
"go fish"
> (define (five-or-greater? n)
(>= n 5))
> (if (memp five-or-greater? (list -4 -2 -3 6 -1))
"one of them is equal to 5 or greater"
"go fish")
"one of them is equal to 5 or greater"
> (if (memp five-or-greater? (list -4 -2 -3 4 -1))
"one of them is equal to 5 or greater"
"go fish")
"go fish"
要将它用于“orig-n或更高”,您必须定义一个函数,如:
(define (orig-n-or-greater? n)
(>= n orig-n))
作为主函数内部的局部函数,以便它可以引用orig-n
。然后你可以像(memp orig-n-or-greater? fs)
一样使用它。
; Create a list of the fibonacci sequence up to n.
(define (fib-list orig-n)
(define (orig-n-or-greater? n)
(>= n orig-n))
; f2 = 1, f1 = 1, fs = a list.
(let loop ((f2 1) (f1 1) (fs (list)))
(cond
; Check if orig-n or greater is in list. If so, return reversed list.
((memp orig-n-or-greater? fs) (reverse fs))
;Else, find the next fibonacci number and add it to the list.
(else (loop f1 (+ f2 f1) (cons f2 fs))))))
> (fib-list 3)
(1 1 2 3)
> (fib-list 55)
(1 1 2 3 5 8 13 21 34 55)
> (fib-list 56)
(1 1 2 3 5 8 13 21 34 55 89)