已经有一些很好的答案,并且您已经接受了一个,但请允许我更详细地说明为什么某些不同版本等同于 for 循环。 (警告:一堆未经测试的代码。)请记住,通常不会从命令式方法开始并转换它来编写函数式程序。您将使用不同的策略,例如尾递归。事实上,严格的尾递归可以做任何for 循环可以做的事情。
您提供的当前代码示例似乎与您的规范不匹配,因为它总是返回false。听起来好像一个简单的return (i >= 5) 就可以做到,但由于这是一个学习示例,让我们编写一个for 循环:
public static boolean containsFive( int n )
{
for ( int i = 0; i <= n; i++ )
if ( i == 5 )
return true;
return false;
}
for 循环与while 循环相同,如下所示:
public static boolean containsFive( int n )
{
int i = 0;
while ( i <= n ) {
if ( i == 5 )
return true;
i++;
}
return false;
}
保持简单,我们可以做的一件事是将do 循环转换为函数调用,以i 作为参数:
public static boolean containsFive(int n)
{
return containsFive( n, 0 );
}
private static boolean containsFive( int n, int i )
{
if (i > n)
return false;
else if (i == 5)
return true;
else
return containsFive( n, i+1 );
}
循环的每次迭代现在都替换为尾递归调用。该解决方案与 Haskell 的翻译很接近:
containsFive :: Integral a => a -> Bool
containsFive n = containsFive' 0 where
containsFive' i =
if i > n then false
else if i == 5 then true
else containsFive' (i+1)
但是,您更有可能看到使用模式保护编写此代码:
containsFive :: Integral a => a -> Bool
containsFive n = containsFive' 0 where
containsFive' i | i > n = false
| i == 5 = true
| otherwise = containsFive' (i+1)
一个细微的区别是 Java 将严格评估表达式,例如 n-1 或 i+1,而 Haskell 默认情况下会延迟评估它们。但是,编译器可能会判断出使参数严格更优化,如果不是,有几种不同的方式来判断它。我们也可以传入n 作为第二个参数,就像我们在Java 中所做的那样,但是由于我们这里的辅助函数是嵌套的,n 仍然在作用域内,不需要再次传递它。
另一个是Integral 类型类更像interface,它涵盖了一些比Int 更大、更复杂的类。 Integer 更像是一个bignum。而且,如果您想知道,您可以指定containsFive' :: Integral a => a -> a -> Bool,但编译器可以从表达式i > n 推断出i 必须与n 具有相同的类型,这是某种Integral。 == 运算符来自 Eq 类型类,> 来自 Ord 类型类。 Haskell 中的数字常量是根据fromInteger 函数定义的,每种Num 都实现了该函数,因此在上下文中,常量0、1 和5 将自动转换为i 的类型,不管那是什么。 + 运算符也来自 Num。由于我们指定该类型实现Integral,它还必须实现Num、Eq 和Ord,所以我们没问题。
所以让我们手动处理所有这些,并假设每个递归调用只是将 i 的值传递到寄存器中,就像在 Java 中一样。
首先,代码运行递归函数,i 最初设置为 0。代码生成器已经可以优化为每个函数调用创建堆栈帧的开销,并且每次都重复使用同一个——我们调用的是完全相同的函数,所以它具有相同数量的相同参数类型和需要相同大小的堆栈。不需要改变n的值,但需要将包含i的寄存器设置为i+1。然后,它跳回到函数的开头并再次运行它,直到循环条件失败或测试i == 5 通过并提前终止。这与我们原始循环的操作完全相同! (好吧,除非它被积极优化。)
通过计数向下,您更有可能看到这一简化,如下所示:
containsFive :: Integral a => a -> Bool
containsFive 5 = true
containsFive n | n > 0 = containsFive (n-1)
| otherwise = false
你也可以在 Java 中倒计时,写 for ( int i = n; i > 0; i-- ) 和我交谈过的一些 C 程序员发誓。