方案 - 斐波那契数列达到一定值
Scheme - List of Fibonacci numbers up to certain value
我正在尝试编写一个函数来创建斐波那契数列列表,但当在列表中找到某个值时停止,然后 return 是该列表(我希望这是有道理的)。
例如,如果我给它 fib-list(55),函数应该 return:
(1 1 2 3 5 8 13 21 34 55)
所以这不是我想要的第 55 个斐波那契数,它是 UP TO 值 55 的列表。
到目前为止,我用于 returning 列表的代码如下所示:
; 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 还是非常陌生,所以我对这门语言的整体理解不是很好。所以请在告诉我为什么我的代码 sucks/doesn 没有意义时保持温和。
car
函数出了什么问题?
car
函数获取列表的第一个元素,但如果列表为空,则它没有第一个元素。 fs
列表一开始是空的。当您尝试获取空列表的第一个元素时,您会收到此错误消息:
> (car (list))
mcar: contract violation
expected: mpair?
given: ()
如果列表不为空,则它有第一个元素,没问题:
> (car (list 4 5 6))
4
按照您在评论中的意思
但是,您的评论 "Check if n is in list" 让我相信 (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
被遮蔽了,变成了一个不同的数字。我猜你在关于 "check if n is in list" 的评论中是指 "check if the original n is in list"。为此,您必须重命名 n
之一:
> (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
检查唯一的停止情况。
这是危险的,因为如果您给它一个非斐波那契数作为输入,它可能会进入无限循环。但是,它修复了少量示例:
; 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
或更高
I should have specified that I need to check if there is a number that is greater than OR equal to orig-n
. Can I still use the member
function to check for this or will I need to use something different?
您将不得不使用不同的东西。在本例中,文档中 member
的正上方是 memp
function (you could also use exists
)。 mem
是member的缩写,p
是"predicate"的缩写。它确定列表的任何成员是否匹配某个谓词。
> (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 or greater",您必须定义如下函数:
(define (orig-n-or-greater? n)
(>= n orig-n))
作为主函数中的 local 函数,以便它可以引用 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)
(list)
创建一个空列表,并且在第一次迭代时您会到达 (car fs)
,它会尝试将 car
应用于空列表,这是一个错误。
您的代码似乎对 n
的性质有点困惑。
你的描述说这是你想要的最大数字,但你正在递归,就像你想要 n:th
斐波那契数一样 - 终止于 (zero? n)
并递归于 (- n 1)
.
当您递归时,您仍在寻找不超过相同限制的数字。
因此,你不应该减少你的限制并终止于零,你应该不理会限制并在达到更大的数字时终止。
我会这样写:
- 初始列表是
(1 1)
- 每一步:
- 计算下一个斐波那契数
- 如果这大于限制,反转累加器列表并return它
- 否则,
cons
它到累加器并用"new"最后两个斐波那契数递归。
在代码中:
(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))))))
这是使用连续传递样式的另一种方法。通过在我们的循环中添加一个延续参数,我们有效地创建了我们自己的 return 机制。此实现的一个独特 属性 是输出列表按正向顺序构建,当 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
作为循环的停止条件,而不是 Nth 项列表。这实际上更容易实现,因为不需要计算生成的术语数。
(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)
我正在尝试编写一个函数来创建斐波那契数列列表,但当在列表中找到某个值时停止,然后 return 是该列表(我希望这是有道理的)。
例如,如果我给它 fib-list(55),函数应该 return:
(1 1 2 3 5 8 13 21 34 55)
所以这不是我想要的第 55 个斐波那契数,它是 UP TO 值 55 的列表。
到目前为止,我用于 returning 列表的代码如下所示:
; 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 还是非常陌生,所以我对这门语言的整体理解不是很好。所以请在告诉我为什么我的代码 sucks/doesn 没有意义时保持温和。
car
函数出了什么问题?
car
函数获取列表的第一个元素,但如果列表为空,则它没有第一个元素。 fs
列表一开始是空的。当您尝试获取空列表的第一个元素时,您会收到此错误消息:
> (car (list))
mcar: contract violation
expected: mpair?
given: ()
如果列表不为空,则它有第一个元素,没问题:
> (car (list 4 5 6))
4
按照您在评论中的意思
但是,您的评论 "Check if n is in list" 让我相信 (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
被遮蔽了,变成了一个不同的数字。我猜你在关于 "check if n is in list" 的评论中是指 "check if the original n is in list"。为此,您必须重命名 n
之一:
> (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
检查唯一的停止情况。
这是危险的,因为如果您给它一个非斐波那契数作为输入,它可能会进入无限循环。但是,它修复了少量示例:
; 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
或更高
I should have specified that I need to check if there is a number that is greater than OR equal to
orig-n
. Can I still use themember
function to check for this or will I need to use something different?
您将不得不使用不同的东西。在本例中,文档中 member
的正上方是 memp
function (you could also use exists
)。 mem
是member的缩写,p
是"predicate"的缩写。它确定列表的任何成员是否匹配某个谓词。
> (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 or greater",您必须定义如下函数:
(define (orig-n-or-greater? n)
(>= n orig-n))
作为主函数中的 local 函数,以便它可以引用 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)
(list)
创建一个空列表,并且在第一次迭代时您会到达 (car fs)
,它会尝试将 car
应用于空列表,这是一个错误。
您的代码似乎对 n
的性质有点困惑。
你的描述说这是你想要的最大数字,但你正在递归,就像你想要 n:th
斐波那契数一样 - 终止于 (zero? n)
并递归于 (- n 1)
.
当您递归时,您仍在寻找不超过相同限制的数字。
因此,你不应该减少你的限制并终止于零,你应该不理会限制并在达到更大的数字时终止。
我会这样写:
- 初始列表是
(1 1)
- 每一步:
- 计算下一个斐波那契数
- 如果这大于限制,反转累加器列表并return它
- 否则,
cons
它到累加器并用"new"最后两个斐波那契数递归。
在代码中:
(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))))))
这是使用连续传递样式的另一种方法。通过在我们的循环中添加一个延续参数,我们有效地创建了我们自己的 return 机制。此实现的一个独特 属性 是输出列表按正向顺序构建,当 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
作为循环的停止条件,而不是 Nth 项列表。这实际上更容易实现,因为不需要计算生成的术语数。
(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)