【问题标题】:Converting isPrime() Python to Haskell将 isPrime() Python 转换为 Haskell
【发布时间】:2021-01-19 23:57:26
【问题描述】:

我有一个工作(尽管效率低下)函数来检查 Python 中的数字是否为素数,我想将其转换为 Haskell。效率现在对我来说并不重要,因为我主要专注于在 Haskell 中制作一个使用初学者可以理解的语言功能的可读算法。

我是 Haskell 的新手,所以我可能对如何处理这个问题有错误的心态。我的基本算法是检查一个数字是否为奇数且大于 2,如果是,则检查它是否可以被任何数字整除,直到 2。

问题:我不知道如何设置i 等于[2..n] 范围内的递增项。

问题:有没有办法用一个变量遍历一个范围并引用它?


Python:

def is_prime(n):
    if n < 2: 
        return False # by definition, numbers smaller than 2 are not prime
    if n == 2: # edge case, 2 is the only even number that is prime
        return True
    if (n % 2) == 0:
        return False # if even, n is not prime (except 2)
    else:
        for i in range(2,n): #iterate from 2 to the input(n)-1 (since range() is non-inclusive of stop param)
            if (n % i == 0): #if n is divisible by a number that isn't 1 or itself...
                return False # ...it isn't prime
    return True 

Haskell(我目前拥有的):

isPrimeInner :: Int -> Bool
isPrimeInner n = map (n `rem` i == 0) [2..n] where i 
   -- I don't know how to set i equal to an increasing term 
   -- in the range [2..n]

isPrime :: Int -> Bool 
isPrime 2 = True -- edge case
isPrime n = if n < 2 
              then False 
            else if n `rem` 2 == 0 
              then False
            else isPrimeInner n

【问题讨论】:

  • “我不知道如何设置 i 等于 [2..n] 范围内的递增项”您可以使用 lambda expression。跨度>
  • 那么我会在每次 map 执行一次迭代时使用它来修改 i 吗?例如:[2..n]!!i where (\i -> i + 1)?
  • map (\i -&gt; n `rem` i == 0) [2..n-1] 将生成一个布尔值列表。 andor 等一些函数可以使用该列表并生成单个布尔值。您可能还需要not。 (或者,使用库中的 anyall。)
  • @chi 或者可以使用 LC 和null 轻松清晰地重新实现。 :)
  • 在 Haskell 中,and (some list) 将只评估列表直到第一个 false。不要害怕产生明显尝试所有除数的布尔值的完整列表:Haskell 实际上不会尝试所有除数,而只会尝试and 要求的数量。最终结果是,一旦找到第一个除数,您就退出“循环”。

标签: python haskell primes primality-test


【解决方案1】:

列表理解最简单:

isPrime :: Int -> Bool
isPrime n = n > 1 && ( n == 2 ||
   null [ () | i <- [2..n-1], rem n i == 0] )
   --          ~~~~~~~~~~~~~

这里的i 是从2 .. n-1 范围内提取的,即它的值从2n-1,一次一个。

当它把n 整除而没有余数时,会产生一个() 值作为输出列表的成员,然后用null 测试它是否为空。因此,代码表达了这样一个概念:“确实存在这样的i2n-1,将n均匀地分开”。

i &lt;- [2..n-1] 部分被称为列表理解的 生成器 部分,rem n i == 0 被称为 测试表达式

测试:

> filter isPrime [0..545]
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,
137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,
271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,
431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541]
it :: [Int]

> length it
100

感谢 Haskell 的惰性求值,当给定一个复合参数 n 时,isPrime 会尽快失败,n最小除数 i

【讨论】:

  • 为什么需要将类型更改为 Integral?
  • 不,不是,它只是 GHCi 为我派生的东西。 :)
  • 啊。我尝试使用 Int 但这会导致错误。函数需要一个 Int 是一个要求,那么是否可以将函数编写成它需要一个 Int 而不是一个 Integral?
  • 正确,这是一个无效的签名。 Int 是一个简单类型,但 Int a 将是一个“约束”,如 Integral a。只有约束位于=&gt; 的左侧。你可以使用isPrime :: a ~ Int =&gt; a -&gt; Bool,但这只是多余的。
  • () 是一个不重要的值。我们也可以使用1True 或其他任何东西,因为我们只检查列表是否为空;所以价值本身并不重要。而在 Haskell 中,不重要的值是 ()
【解决方案2】:

Haskell 中没有赋值。不幸的是,您遇到了一个需要您学习新概念的主题:与其关注单个值 (x),不如关注值的完整范围/列表。

假设n = 8。 Python 程序员可能会迭代 (x in i in range(2, n)),Haskell 程序员可能会将输入列表 [2..n] 转换为结果列表并从那里继续解决问题:

xs = [   2,     3,    4,     5, ...]  -- input [2..n]
ys = [True, False, True, False, ...]  -- the resulting list of the divisibility test

因此我建议将问题分解并解决两个子问题:

  1. 如何从xs计算ys
  2. 假设您有可分性测试列表 (ys)。你怎么能解决你原来的问题?在您的情况下,将 any True 值作为元素很重要。幸运的是,有一个功能 any 可以帮助您。

【讨论】:

  • 我想我明白你在说什么。对于子问题 1,我想我可以使用 map 对列表 xs 的每个元素进行测试(测试为 n rem i == 0)。如上所述,唯一的问题是我不知道如何在测试 xs 中的每个元素后增加 i 。对于子问题 2,我想在 ys 中出现一个 True 时立即返回 False,因为这意味着除了 1 和 n 之外还有一个除数。
  • 这在两个帐户上都是正确的。 1. 当您尝试查看map 的一些示例时,您的i 问题就会消失。 2.您可能正在寻找的功能是any
  • 啊,我看到“任何”是有道理的。所以对于地图,我认为我应该这样做:map(n rem i == 0) xs 但我仍然对如何让 i 等于 xs 中的当前元素感到有些困惑。
  • 没关系,我想通了。使用“
【解决方案3】:

不要使用循环,而是尝试将其转换为尾递归。

isPrimeHelper :: Integer -> [Integer] -> Bool
isPrimeHelper p (i:is) 
    | i*i > p = True
    | p `rem` i == 0 = False
    | otherwise = isPrimeHelper p is

isPrime :: Integer -> Bool
isPrime p = isPrimeHelper p [2..p]

isPrimeHelper 是循环的递归版本,其中出现的i 就像循环中的i,您可以跟踪它。

对于等效的 Python 代码:

def isPrimeHelper(p : int, xs : [int]):
  i = xs[0]
  if i*i >p:
    return True
  elif p % i == 0:
    return False
  else:
    return isPrimeHelper(p, xs[1:])

【讨论】:

  • 这很优雅。但是“i*i > p”检查的是什么?
  • @Prithvi Boinpally 在计算质数时是一种技巧。我们只需要检查数字平方根小的因子(因为因子是对称的),例如。对于 18 -> 1、2、3、6、9、18; 2 和 9 配对,3 和 6 配对所以我们只需要检查小于 sqrt(18) 的 2 和 3
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-07
  • 2011-02-16
  • 2012-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多