haskell等效于if语句的循环

时间:2018-02-27 03:29:57

标签: function haskell boolean iteration

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。

2 个答案:

答案 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-1i+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函数定义的,因此在上下文中,常量015将自动转换为i的类型,无论是什么。 +运算符也来自Num。由于我们指定该类型实现了Integral,因此它还必须实现NumEqOrd,所以我们没问题。

所以让我们手动所有这些并假设每个递归调用只是在寄存器中传递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中倒数。