HI noob问题,但我正在学习Haskell
我试图理解如何迭代0直到n并返回false如果迭代包含5其他明智的返回true
这是我的方法
Fivecount :: Integral a => a -> Bool
isfivecount z = False
在JAVA中需要这样的东西
method boolean (n)
{
for(int i = 2; i < n; i++){
if(i == 5){
return true;
}
else{
return false;
}
}
先谢谢FRIENDS :)解释也很酷,只是真的想了解Haskell。
答案 0 :(得分:2)
如果您打开使用库函数,可能会使用any
来编写如下内容:
anyFivesInRangeZeroTo :: (Eq a, Num a, Enum a) => a -> Bool
anyFivesInRangeZeroTo n = not $ any (5 ==) [0..n]
另一种选择,如果你更喜欢类似循环的方法,那就是使用递归:
anyFivesInRange' :: (Enum t, Ord t, Num t) => t -> Bool
anyFivesInRangeZeroTo' nn =
let
iter 5 _ = False
iter n nn = if n < nn then iter (succ n) nn else True
in
iter 0 nn
这相当于一个for循环,其索引从零开始并递增1,直到找到5
(通过辅助函数的第一个参数的模式匹配)并返回{{1} }或到达循环边界并返回False
。
答案 1 :(得分:2)
已经有一些好的答案了,你已经接受了一个,但请允许我详细介绍为什么某些不同的版本等同于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
通过并且它提前终止。这与我们原来的循环完全相同! (好吧,除非它被积极优化。)
您更有可能通过计算 down 来简化此操作,例如:
containsFive :: Integral a => a -> Bool
containsFive 5 = true
containsFive n | n > 0 = containsFive (n-1)
| otherwise = false
你也可以通过编写for ( int i = n; i > 0; i-- )
以及我曾经谈过的一些C程序员发誓,在Java中倒数。