提问人:GamersOfDead 提问时间:11/12/2023 最后编辑:ChrisGamersOfDead 更新时间:11/13/2023 访问量:63
为什么最后一个“else”没有使用新值重新开始递归?
Why does the last "else" not restart the recursion with the new values?
问:
所以我在大学学习 Ocaml,今年我们创建的代码有一些特殊的规则:没有循环或数组(我不知道为什么,但当我的一个朋友在考试中使用它们时,她几乎看起来很生气),所以我们必须只用递归来处理问题,在这里我必须创建一个代码来返回所有介于 (0,2) 和 (n) 之间的孪生素数,例如 3,5、5、7 或 11,13,n+2) 所以我试着这样做:
let rec calculpj p q n =
if q = p + 2 && n > 0 then
begin
if q mod p != 0 && p mod q != 0 then
begin
print_int(p) ;
print_int(q)
end
else
calculpj (p + 1) (q + 1) (n - 1)
end
else
calculpj (p + 1) (q + 1) (n - 1) ;;
calculpj 3 5 3
但是出于某种原因,我不明白为什么最后一个“else”不使用新值重新启动递归,因为第一个“循环”按预期返回值。 如果有人想知道这种练习是从哪里来的,那就是来自老师没有给我们答案的考试,所以我没有什么可以纠正自己的。
我尝试了很多东西,但由于我是 Ocaml 的新手,我不知道如何找到导致我问题的原因。
答:
您有几个案例。其中之一是:
begin
print_int(p) ;
print_int(q)
end
这涵盖了两个值不为 0 的情况。请注意,这种情况到此为止。打印两个整数后无需执行任何操作。这就是为什么您的代码在第一次后停止的原因。两个递归调用是针对其他情况的。mod
calculpj
其他观察结果:
OCaml 中通常的不相等运算符是 。你正在使用一个应该避免的,直到你以后的学习:-)<>
你测试 n 是否> 0,这是有道理的。但是当它是假的时,你无论如何都是递归调用的。这表明,如果你解决了第一个问题,你就会有一个无限循环。calculpj
目前尚不清楚为什么要测试是否 .如果提供两个相差 2 的值,则递归调用将导致它们始终相差 2。q = p + 2
评论
<>
是“不等于”,而检查物理内存地址是否不同,并且更复杂一点,如果 2 个值不引用相同的物理内存位置。所以使用不等于=!
!=
<>
如果一个数字正好有两个不同的正除数:1 和数字本身,则该数字是素数。因此,创建一个函数,该函数使用这些特征来测试它是否为素数:
## pseudo code
Function is_prime(n)
Function check(i)
If i * i > n Then
Return true
Else If n mod i = 0 Then
Return false
Else
Call check(i + 1)
End Function
If n > 1 and check(2) Then
Return true
Else
Return false
End Function
所以现在的主要游戏是通过递归重复数字,所以你需要一种确定的方法来突破。我建议你简单地要求一些对,一旦达到这个目标,回避就会结束。对于找到的每对,下面的参数减少 1,并在达到 0 时退出。第二个参数仅允许您指定起点,例如 1n
start
## pseudo code
Function twin_primes(n, start)
Function helper(p, q, n, result)
If n equals 0 Then
Return result
Else If p and q are twin primes Then
Decrease n by 1
Add (p, q) to result
Call helper(p + 1, q + 1, n, result)
Else
Call helper(p + 1, q + 1, n, result)
End Function
Call helper(start, start + 2, n, empty list)
End Function
这大约是我能做到的。
评论
为了解决您的特定代码,让我们通过消除一些干扰来简化它:
let rec calculpj p q n =
if q = p + 2 && n > 0 then
if q mod p != 0 && p mod q != 0 then
Format.printf "%d %d\n" p q
else
calculpj (p + 1) (q + 1) (n - 1)
else
calculpj (p + 1) (q + 1) (n - 1)
现在,我们知道了这一点,并且总是以相同的数量递增,所以让我们删除它们相差 2 的检查。p
q
let rec calculpj p q n =
if n > 0 then
if q mod p != 0 && p mod q != 0 then
Format.printf "%d %d\n" p q
else
calculpj (p + 1) (q + 1) (n - 1)
else
calculpj (p + 1) (q + 1) (n - 1)
然后我们可以消除某些级别的嵌套。
let rec calculpj p q n =
if n > 0 && q mod p != 0 && p mod q != 0 then
Format.printf "%d %d\n" p q
else if n > 0 then
calculpj (p + 1) (q + 1) (n - 1)
else
calculpj (p + 1) (q + 1) (n - 1)
请注意,最后两个结果是相同的,因此我们可以进一步减少这种情况。
let rec calculpj p q n =
if n > 0 && q mod p != 0 && p mod q != 0 then
Format.printf "%d %d\n" p q
else
calculpj (p + 1) (q + 1) (n - 1)
所以,当你调用 时,大于 和 不等于 和 不等于 ,所以并被打印出来。没有递归调用,因此不会发生任何进一步的事情。calculpj p q n
n
0
q mod p
0
p mod q
0
p
q
除此之外,您的逻辑将无法正确识别素数。
要使递归起作用,您需要一个将终止递归的基本情况。在您的例子中,如果等于或小于零,则函数的工作完成。n
let calculpj p q n =
if n <= 0 then ...
您需要确定一个情况,其中 和 都是素数。p
q
let calculpj p q n =
if n <= 0 then ...
else if is_prime p && is_prime q then ...
你需要处理他们不是的情况。
let calculpj p q n =
if n <= 0 then ...
else if is_prime p && is_prime q then ...
else ...
在后两种情况下,您都需要更新状态。两者都将递增,但只有在找到一对匹配的数字时才应递减。p
q
n
选择
在解决了这个问题之后,我鼓励你考虑最终目标。您需要从某个数字开始的数字,以某个步骤分隔,并且您需要一定数量的数字。这不一定与您的函数签名匹配,但可以调整该方法。
编程的关键是分解复杂的程序。根据我的经验,OCaml 使这变得非常简单。我们可以从起始数的整数序列开始。这是懒惰。我们可以根据需要走得更远。
我们的下一步是将序列中的数字映射到元组,例如 。然后,我们只过滤掉两个元素都是素数的元组。为此,您需要定义 ,可能使用一种非常基本的检查因子的方法。3
(3, 5)
is_prime
let is_prime n =
let rec check pf =
if pf = n then ...
else if n mod pf = 0 then ...
else ...
in
if n = 0 || n = 1 then false
else check 2
一旦你把你的序列过滤到只有一对整数,用一个步骤号分隔,你只需要将你的序列限制在一定数量的元素,然后通过转换为列表来使用序列。
# let calc start step take =
start
|> Seq.ints
|> Seq.map (fun i -> (i, i + step))
|> Seq.filter (fun (a, b) -> is_prime a && is_prime b)
|> Seq.take take
|> List.of_seq;;
val calc : int -> int -> int -> (int * int) list = <fun>
# calc 3 2 3;;
- : (int * int) list = [(3, 5); (5, 7); (11, 13)]
或者我们可以直接打印出来。
# let calc start step take =
start
|> Seq.ints
|> Seq.map (fun i -> (i, i + step))
|> Seq.filter (fun (a, b) -> is_prime a && is_prime b)
|> Seq.take take
|> Seq.iter (fun (a, b) -> Format.printf "%2d, %2d\n" a b);;
val calc : int -> int -> int -> unit = <fun>
# calc 3 2 3;;
3, 5
5, 7
11, 13
- : unit = ()
# calc 1007 2 3;;
1019, 1021
1031, 1033
1049, 1051
- : unit = ()
评论
begin ... end
begin ... end
Format.printf "%d %d\n" p q