【问题标题】:haskell: factors of a natural numberhaskell:自然数的因数
【发布时间】:2014-07-27 16:07:04
【问题描述】:

我正在尝试在 Haskell 中编写一个函数,该函数计算给定数字的所有因子,除了它本身。

结果应该是这样的:

factorlist 15 => [1,3,5]

我是 Haskell 和整个递归主题的新手,我很确定我应该在这个例子中应用它,但我不知道在哪里或如何。

我的想法是将给定的数字与从 1 到 n 的列表的第一个元素进行比较 div2 使用mod 函数,但以某种方式递归,如果结果是0,那么我将数字添加到新列表中。 (我希望这是有道理的)

我将不胜感激有关此事的任何帮助

到目前为止,这是我的代码:(它不起作用..但以某种方式说明我的想法)

 factorList :: Int -> [Int]
 factorList n  |n `mod` head [1..n`div`2] == 0 = x:[]

【问题讨论】:

标签: haskell


【解决方案1】:

有几种方法可以处理这个问题。但首先,让我们写一个小助手:

isFactorOf :: Integral a => a -> a -> Bool
isFactorOf x n = n `mod` x == 0

这样我们可以写12 `isFactorOf` 24 并得到TrueFalse。对于递归部分,假设我们使用一个带有两个参数的函数:一个是我们想要分解的数字,第二个是我们当前正在测试的因子。我们只测试小于或等于n `div` 2 的因子,这导致:

createList n f | f <= n `div` 2 = if f `isFactorOf` n
                                     then f : next
                                     else next
               | otherwise      = []
    where next = createList n (f + 1)

所以如果第二个参数是n的因子,我们将它添加到列表中并继续,否则我们就继续。只要f &lt;= n `div` 2,我们就会这样做。现在为了创建factorList,我们可以简单地使用createList 和足够的第二个参数:

factorList n = createList n 1

递归隐藏在createList 中。因此,createListworker,您可以将其隐藏在 where 内部的 factorList 中。

请注意,可以使用过滤器或列表推导轻松定义 factorList

factorList'  n = filter (`isFactorOf` n) [1 .. n `div` 2]
factorList'' n = [ x | x <- [1 .. n`div` 2], x `isFactorOf` n]

但在这种情况下,您不会自己编写递归。

进一步练习:

  • 尝试自己实现filter函数。
  • 创建另一个函数,它只返回 prime 因子。您可以使用之前的结果并编写一个素数过滤器,也可以编写一个直接生成它们的递归函数(后者更快)。

【讨论】:

    【解决方案2】:

    @Zeta 的回答很有趣。但是如果你像我一样是 Haskell 的新手,你可能想要一个“简单”的答案开始。 (只是为了获得基本的递归模式......并理解缩进,以及类似的事情。)

    我不会将任何内容除以 2,我将包括数字本身。所以factorlist 15 =&gt; [1,3,5,15]在我的例子中:

    factorList :: Int -> [Int]
    
    factorList value = factorsGreaterOrEqual 1
      where
        factorsGreaterOrEqual test
          | (test == value) = [value]
          | (value `mod` test == 0) = test : restOfFactors
          | otherwise = restOfFactors
          where restOfFactors = factorsGreaterOrEqual (test + 1)
    

    第一行是类型签名,你已经知道了。类型签名doesn't have to live right next to the list of pattern definitions for a function,(尽管模式本身需要在连续的行上全部在一起)。

    然后factorList 是根据辅助函数定义的。这个辅助函数在where 子句中定义...这意味着它是本地的并且可以访问值参数。如果我们在全局范围内定义 factorGreaterOrEqual,那么它需要两个参数,因为 value 不在范围内,例如

    factorsGreaterOrEqual 4 15 => [5,15]
    

    您可能会争辩说,factorsGreaterOrEqual 本身就是一个有用的函数。也许是,也许不是。但在这种情况下,我们要说它除了帮助我们定义 factorList...之外没有什么用处,所以使用 where 子句并隐式获取值更简洁。

    Haskell 的缩进规则(在我看来)很奇怪,但是 here they are summarized。我在这里缩进了两个空格,因为如果你使用 4,它会增长得太远。

    在 Haskell 中,前面带有管道字符的布尔测试列表称为“守卫”。我只是将终止条件确定为测试达到值时;所以factorsGreaterOrEqual N = [N],如果我们打电话给factorList N。然后我们通过该值除以它是否没有余数来决定是否将测试编号连接到列表中。 otherwise 是一个 Haskell 关键字,有点像 C 类 switch 语句中的 default 用于失败的情况)

    为了展示另一个层次的嵌套和另一个隐式参数演示,我添加了一个 where 子句来在本地定义一个名为 restOfFactors 的函数。无需将 test 作为参数传递给 restOfFactors,因为它存在于 factorsGreaterOrEqual 的“范围内”......并且因为它存在于 factorList 的范围内,所以 value 也是可用的。

    【讨论】:

    • 如果当你缩进 4 个空格时,某些东西向右太远,你可能应该重写它以减少嵌套。
    猜你喜欢
    • 2011-01-30
    • 2014-10-16
    • 2021-02-25
    • 2011-10-16
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    • 1970-01-01
    • 2022-11-24
    相关资源
    最近更新 更多